Merge pull request #6325 from umbraco/v8/feature/2438AB-RTE-Image-Config-MediaFolder
V8: AB 2438 - RTE Pasted/Dragged Images Media Folder Config
This commit is contained in:
@@ -118,9 +118,11 @@ Use this directive to generate a thumbnail grid of media items.
|
||||
var item = scope.items[i];
|
||||
setItemData(item);
|
||||
setOriginalSize(item, itemMaxHeight);
|
||||
|
||||
|
||||
item.selectable = getSelectableState(item);
|
||||
|
||||
// remove non images when onlyImages is set to true
|
||||
if(scope.onlyImages === "true" && !item.isFolder && !item.thumbnail){
|
||||
if( scope.onlyImages === "true" && !item.isFolder && !item.thumbnail){
|
||||
scope.items.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
@@ -188,6 +190,22 @@ Use this directive to generate a thumbnail grid of media items.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns wether a item should be selectable or not.
|
||||
*/
|
||||
function getSelectableState(item) {
|
||||
|
||||
// check if item is a folder or image
|
||||
if (item.isFolder === true) {
|
||||
return scope.disableFolderSelect !== "true" && scope.onlyImages !== "true";
|
||||
} else {
|
||||
return scope.onlyFolders !== "true";
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
function setOriginalSize(item, maxHeight) {
|
||||
|
||||
@@ -331,7 +349,9 @@ Use this directive to generate a thumbnail grid of media items.
|
||||
itemMaxHeight: "@",
|
||||
itemMinWidth: "@",
|
||||
itemMinHeight: "@",
|
||||
disableFolderSelect: "@",
|
||||
onlyImages: "@",
|
||||
onlyFolders: "@",
|
||||
includeSubFolders: "@",
|
||||
currentFolderId: "@"
|
||||
},
|
||||
|
||||
@@ -21,12 +21,29 @@
|
||||
|
||||
margin: 10px;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
box-shadow: 0 1px 1px 0 rgba(0,0,0,.2);
|
||||
transition: box-shadow 150ms ease-in-out;
|
||||
}
|
||||
|
||||
.umb-media-grid__item.-unselectable {
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(230, 230, 230, .5);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-media-grid__item.-selectable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.umb-media-grid__item.-file {
|
||||
background-color: @white;
|
||||
}
|
||||
@@ -37,7 +54,8 @@
|
||||
color: @ui-selected-type;
|
||||
}
|
||||
}
|
||||
.umb-media-grid__item.-selected, .umb-media-grid__item:hover {
|
||||
.umb-media-grid__item.-selected,
|
||||
.umb-media-grid__item.-selectable:hover {
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
@@ -52,7 +70,7 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
.umb-media-grid__item:hover {
|
||||
.umb-media-grid__item.-selectable:hover {
|
||||
&::before {
|
||||
opacity: .33;
|
||||
}
|
||||
@@ -159,6 +177,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.umb-media-grid__item-name {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.umb-media-grid__item-name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
angular.module("umbraco")
|
||||
.controller("Umbraco.Editors.MediaPickerController",
|
||||
function ($scope, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService) {
|
||||
|
||||
|
||||
|
||||
if (!$scope.model.title) {
|
||||
localizationService.localizeMany(["defaultdialogs_selectMedia", "general_includeFromsubFolders"])
|
||||
.then(function (data) {
|
||||
@@ -14,10 +15,11 @@ angular.module("umbraco")
|
||||
}
|
||||
|
||||
var dialogOptions = $scope.model;
|
||||
|
||||
$scope.disableFolderSelect = dialogOptions.disableFolderSelect;
|
||||
$scope.onlyImages = dialogOptions.onlyImages;
|
||||
$scope.showDetails = dialogOptions.showDetails;
|
||||
|
||||
$scope.disableFolderSelect = (dialogOptions.disableFolderSelect && dialogOptions.disableFolderSelect !== "0") ? true : false;
|
||||
$scope.onlyImages = (dialogOptions.onlyImages && dialogOptions.onlyImages !== "0") ? true : false;
|
||||
$scope.onlyFolders = (dialogOptions.onlyFolders && dialogOptions.onlyFolders !== "0") ? true : false;
|
||||
$scope.showDetails = (dialogOptions.showDetails && dialogOptions.showDetails !== "0") ? true : false;
|
||||
$scope.multiPicker = (dialogOptions.multiPicker && dialogOptions.multiPicker !== "0") ? true : false;
|
||||
$scope.startNodeId = dialogOptions.startNodeId ? dialogOptions.startNodeId : -1;
|
||||
$scope.cropSize = dialogOptions.cropSize;
|
||||
@@ -188,26 +190,25 @@ angular.module("umbraco")
|
||||
};
|
||||
|
||||
$scope.clickHandler = function (image, event, index) {
|
||||
|
||||
if (image.isFolder) {
|
||||
if ($scope.disableFolderSelect) {
|
||||
$scope.gotoFolder(image);
|
||||
} else {
|
||||
eventsService.emit("dialogs.mediaPicker.select", image);
|
||||
selectImage(image);
|
||||
}
|
||||
} else {
|
||||
eventsService.emit("dialogs.mediaPicker.select", image);
|
||||
if ($scope.showDetails) {
|
||||
|
||||
|
||||
$scope.target = image;
|
||||
|
||||
|
||||
// handle both entity and full media object
|
||||
if (image.image) {
|
||||
$scope.target.url = image.image;
|
||||
} else {
|
||||
$scope.target.url = mediaHelper.resolveFile(image);
|
||||
}
|
||||
|
||||
|
||||
$scope.openDetailsDialog();
|
||||
} else {
|
||||
selectImage(image);
|
||||
@@ -222,6 +223,9 @@ angular.module("umbraco")
|
||||
};
|
||||
|
||||
function selectImage(image) {
|
||||
if(!image.selectable) {
|
||||
return;
|
||||
}
|
||||
if (image.selected) {
|
||||
for (var i = 0; $scope.model.selection.length > i; i++) {
|
||||
var imageInSelection = $scope.model.selection[i];
|
||||
@@ -234,6 +238,7 @@ angular.module("umbraco")
|
||||
if (!$scope.multiPicker) {
|
||||
deselectAllImages($scope.model.selection);
|
||||
}
|
||||
eventsService.emit("dialogs.mediaPicker.select", image);
|
||||
image.selected = true;
|
||||
$scope.model.selection.push(image);
|
||||
}
|
||||
|
||||
@@ -98,7 +98,9 @@
|
||||
item-max-height="150"
|
||||
item-min-width="100"
|
||||
item-min-height="100"
|
||||
disable-folder-select={{disableFolderSelect}}
|
||||
only-images={{onlyImages}}
|
||||
only-folders={{onlyFolders}}
|
||||
include-sub-folders={{showChilds}}
|
||||
current-Folder-id="{{currentFolder.id}}">
|
||||
</umb-media-grid>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div data-element="media-grid" class="umb-media-grid">
|
||||
<div data-element="media-grid-item-{{$index}}" class="umb-media-grid__item" title="{{item.name}}" ng-click="clickItem(item, $event, $index)" ng-repeat="item in items | filter:filterBy" ng-style="item.flexStyle" ng-class="{'-selected': item.selected, '-file': !item.thumbnail, '-svg': item.extension == 'svg'}">
|
||||
<div data-element="media-grid-item-{{$index}}" class="umb-media-grid__item" title="{{item.name}}" ng-click="clickItem(item, $event, $index)" ng-repeat="item in items | filter:filterBy" ng-style="item.flexStyle" ng-class="{'-selected': item.selected, '-file': !item.thumbnail, '-svg': item.extension == 'svg', '-selectable': item.selectable, '-unselectable': !item.selectable}">
|
||||
<div>
|
||||
<!--<i ng-show="item.selected" class="icon-check umb-media-grid__checkmark"></i>-->
|
||||
<a ng-if="allowOnClickEdit === 'true'" ng-click="clickEdit(item, $event)" ng-href="" class="icon-edit umb-media-grid__edit"></a>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
function mediaFolderPickerController($scope, editorService, entityResource) {
|
||||
|
||||
|
||||
$scope.folderName = "";
|
||||
|
||||
|
||||
function retriveFolderData() {
|
||||
|
||||
var id = $scope.model.value;
|
||||
|
||||
if (id == null) {
|
||||
$scope.folderName = "";
|
||||
return;
|
||||
}
|
||||
|
||||
entityResource.getById(id, "Media").then(
|
||||
function (media) {
|
||||
$scope.media = media;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
retriveFolderData();
|
||||
|
||||
|
||||
$scope.add = function() {
|
||||
var mediaPickerOptions = {
|
||||
view: "mediapicker",
|
||||
multiPicker: true,
|
||||
disableFolderSelect: false,
|
||||
onlyImages: false,
|
||||
onlyFolders: true,
|
||||
submit: function (model) {
|
||||
|
||||
$scope.model.value = model.selection[0].udi;
|
||||
|
||||
retriveFolderData();
|
||||
|
||||
editorService.close();
|
||||
},
|
||||
close: function () {
|
||||
editorService.close();
|
||||
}
|
||||
};
|
||||
editorService.mediaPicker(mediaPickerOptions);
|
||||
};
|
||||
|
||||
$scope.remove = function () {
|
||||
$scope.model.value = null;
|
||||
retriveFolderData();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
angular.module('umbraco').controller("Umbraco.PrevalueEditors.MediaFolderPickerController", mediaFolderPickerController);
|
||||
@@ -0,0 +1,37 @@
|
||||
<div ng-controller="Umbraco.PrevalueEditors.MediaFolderPickerController" class="umb-property-editor umb-mediapicker umb-mediapicker-single">
|
||||
|
||||
<div class="flex flex-wrap error">
|
||||
<ul class="umb-sortable-thumbnails">
|
||||
<li ng-if="model.value" class="umb-sortable-thumbnails__wrapper">
|
||||
|
||||
<p class="label label__trashed" ng-if="media.trashed">
|
||||
<localize key="mediaPicker_trashed"></localize>
|
||||
<i class="icon-trash" aria-hidden="true"></i>
|
||||
</p>
|
||||
|
||||
<!-- FILE -->
|
||||
<div ng-class="{'trashed': media.trashed}" class="umb-icon-holder" ng-hide="media.thumbnail">
|
||||
<span class="file-icon">
|
||||
<i class="icon {{media.icon}}"></i>
|
||||
<span ng-if="media.extension">.{{media.extension}}</span>
|
||||
</span>
|
||||
<small>{{media.name}}</small>
|
||||
</div>
|
||||
|
||||
<div class="umb-sortable-thumbnails__actions" data-element="sortable-thumbnail-actions">
|
||||
|
||||
<a class="umb-sortable-thumbnails__action -red" data-element="action-remove" href="" ng-click="remove()">
|
||||
<i class="icon icon-delete"></i>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li style="border: none;" class="add-wrapper unsortable" ng-hide="model.value">
|
||||
<a data-element="sortable-thumbnails-add" href="#" class="add-link add-link-square" ng-click="add()" prevent-default>
|
||||
<i class="icon icon-add large"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -6,7 +6,7 @@
|
||||
<umb-checkbox model="cmd.selected"
|
||||
on-change="selectCommand(cmd)"
|
||||
class="mce-cmd" />
|
||||
|
||||
|
||||
<!--<img ng-src="{{cmd.icon}}" />-->
|
||||
<i class="mce-ico" ng-class="(cmd.isCustom ? ' mce-i-custom ' : ' mce-i-') + cmd.fontIcon"></i>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
text="{{css.name}}"/>
|
||||
</div>
|
||||
</umb-control-group>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group label="Dimensions" description="Set the editor dimensions">
|
||||
<div class="vertical-align-items">
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
// Now remove all old files so that the temp folder(s) never grow
|
||||
// Anything older than one day gets deleted
|
||||
var tempFiles = Directory.GetFiles(SystemDirectories.TempFileUploads, "*", SearchOption.AllDirectories);
|
||||
var tempFiles = Directory.GetFiles(IOHelper.MapPath(SystemDirectories.TempFileUploads), "*", SearchOption.AllDirectories);
|
||||
foreach (var tempFile in tempFiles)
|
||||
{
|
||||
if (DateTime.UtcNow - File.GetLastWriteTimeUtc(tempFile) > TimeSpan.FromDays(1))
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
@@ -20,5 +21,9 @@ namespace Umbraco.Web.PropertyEditors
|
||||
"Ignore User Start Nodes", "boolean",
|
||||
Description = "Selecting this option allows a user to choose nodes that they normally don't have access to.")]
|
||||
public bool IgnoreUserStartNodes { get; set; }
|
||||
|
||||
[ConfigurationField("mediaParentId", "Image Upload Folder", "MediaFolderPicker",
|
||||
Description = "Choose the upload location of pasted images")]
|
||||
public GuidUdi MediaParentId { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -76,6 +77,10 @@ namespace Umbraco.Web.PropertyEditors
|
||||
if (editorValue.Value == null)
|
||||
return null;
|
||||
|
||||
var config = editorValue.DataTypeConfiguration as GridConfiguration;
|
||||
var mediaParent = config?.MediaParentId;
|
||||
var mediaParentId = mediaParent == null ? Guid.Empty : mediaParent.Guid;
|
||||
|
||||
// editorValue.Value is a JSON string of the grid
|
||||
var rawJson = editorValue.Value.ToString();
|
||||
var grid = JsonConvert.DeserializeObject<GridValue>(rawJson);
|
||||
@@ -89,10 +94,9 @@ namespace Umbraco.Web.PropertyEditors
|
||||
// Parse the HTML
|
||||
var html = rte.Value?.ToString();
|
||||
|
||||
var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? -1;
|
||||
var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? Constants.Security.SuperUserId;
|
||||
|
||||
// TODO: In future task(get the parent folder from this config) to save the media into
|
||||
var parsedHtml = TemplateUtilities.FindAndPersistPastedTempImages(html, Constants.System.Root, userId, _mediaService, _contentTypeBaseServiceProvider, _logger);
|
||||
var parsedHtml = TemplateUtilities.FindAndPersistPastedTempImages(html, mediaParentId, userId, _mediaService, _contentTypeBaseServiceProvider, _logger);
|
||||
rte.Value = parsedHtml;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
@@ -19,5 +20,9 @@ namespace Umbraco.Web.PropertyEditors
|
||||
"Ignore User Start Nodes", "boolean",
|
||||
Description = "Selecting this option allows a user to choose nodes that they normally don't have access to.")]
|
||||
public bool IgnoreUserStartNodes { get; set; }
|
||||
|
||||
[ConfigurationField("mediaParentId", "Image Upload Folder", "MediaFolderPicker",
|
||||
Description = "Choose the upload location of pasted images")]
|
||||
public GuidUdi MediaParentId { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,10 +117,13 @@ namespace Umbraco.Web.PropertyEditors
|
||||
var editorValueWithMediaUrlsRemoved = TemplateUtilities.RemoveMediaUrlsFromTextString(editorValue.Value.ToString());
|
||||
var parsed = MacroTagParser.FormatRichTextContentForPersistence(editorValueWithMediaUrlsRemoved);
|
||||
|
||||
var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? -1;
|
||||
var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? Constants.Security.SuperUserId;
|
||||
|
||||
// TODO: In future task(get the parent folder from this config) to save the media into
|
||||
parsed = TemplateUtilities.FindAndPersistPastedTempImages(parsed, Constants.System.Root, userId, _mediaService, _contentTypeBaseServiceProvider, _logger);
|
||||
var config = editorValue.DataTypeConfiguration as RichTextConfiguration;
|
||||
var mediaParent = config?.MediaParentId;
|
||||
var mediaParentId = mediaParent == null ? Guid.Empty : mediaParent.Guid;
|
||||
|
||||
parsed = TemplateUtilities.FindAndPersistPastedTempImages(parsed, mediaParentId, userId, _mediaService, _contentTypeBaseServiceProvider, _logger);
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@ using System.Text.RegularExpressions;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using Umbraco.Web.Routing;
|
||||
using File = System.IO.File;
|
||||
|
||||
namespace Umbraco.Web.Templates
|
||||
{
|
||||
@@ -189,7 +191,7 @@ namespace Umbraco.Web.Templates
|
||||
// see comment in ResolveMediaFromTextString for group reference
|
||||
=> ResolveImgPattern.Replace(text, "$1$3$4$5");
|
||||
|
||||
internal static string FindAndPersistPastedTempImages(string html, int mediaParentFolder, int userId, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, ILogger logger)
|
||||
internal static string FindAndPersistPastedTempImages(string html, Guid mediaParentFolder, int userId, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, ILogger logger)
|
||||
{
|
||||
// Find all img's that has data-tmpimg attribute
|
||||
// Use HTML Agility Pack - https://html-agility-pack.net
|
||||
@@ -213,7 +215,13 @@ namespace Umbraco.Web.Templates
|
||||
var safeFileName = fileName.ToSafeFileName();
|
||||
|
||||
var mediaItemName = safeFileName.ToFriendlyName();
|
||||
var mediaFile = mediaService.CreateMedia(mediaItemName, mediaParentFolder, Constants.Conventions.MediaTypes.Image, userId);
|
||||
IMedia mediaFile;
|
||||
|
||||
if(mediaParentFolder == Guid.Empty)
|
||||
mediaFile = mediaService.CreateMedia(mediaItemName, Constants.System.Root, Constants.Conventions.MediaTypes.Image, userId);
|
||||
else
|
||||
mediaFile = mediaService.CreateMedia(mediaItemName, mediaParentFolder, Constants.Conventions.MediaTypes.Image, userId);
|
||||
|
||||
var fileInfo = new FileInfo(absoluteTempImagePath);
|
||||
|
||||
var fileStream = fileInfo.OpenReadWithRetry();
|
||||
@@ -242,9 +250,8 @@ namespace Umbraco.Web.Templates
|
||||
// for each image uploaded from TinyMceController
|
||||
var folderName = Path.GetDirectoryName(absoluteTempImagePath);
|
||||
try
|
||||
{
|
||||
File.Delete(absoluteTempImagePath);
|
||||
Directory.Delete(folderName);
|
||||
{
|
||||
Directory.Delete(folderName, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user