Review AB11194 — Improve media selector UX (#10157)
* set input file accept * Use PreValue file extensions for limiting the files to be chosen in file input * Current state for Warren to review * This should fix up what you need Niels * update csproj * use empty string if fileExtensions is undefined * public interface * initial work * local crops * translations * translation correction * fix misspeling * some progress * filter media picker * align media card grid items correctly * responsive media cropper * always be able to scale 3 times smallest scale * making image cropper property editor responsive * scroll to scale * adjust slider look * rearrange parts of mediaentryeditor * test helper * styling * move controls inside umb-image-crop * seperate umg-cropper-gravity styling * corrected layout * more ui refinement * keep the idea of mandatory out for now. * remove double ; * removed testing code * JSON Property Value Convertor now has an array of property editors to exclude * Property Value Convertor for Media Picker 3 aka Media Picker with Local Crops * Experimenting on best approach to retrieve local crop in razor view when iterating over picked media items * Update ValueConvertor to use ImageCropperValue as part of the model for views as alot of existing CropUrls can then use it * Update extension methods to take an ImageCropperValue model (localCropData) * Forgot to update CSProj for new ValueConvertor * New GetCropUrl @Url.GetCropUrl(crop.Alias, media.LocalCrops) as oppposed to @Url.GetCropUrl(media.LocalCrops, cropAlias:crop.Alias, useCropDimensions: true) * Remove dupe item in CSProj * Use a contains as an opposed to Array.IndexOf * various corrections, SingleMode based on max 1, remove double checkerBackground, enforce validation for Crops, changed error indication * mediapicker v3 * correct version * fixing file ext label text color * clipboard features for MediaPicker v3 * highlight not allowed types * highlight trashed as an error * Media Types Video, Sound, Document and Vector Image * Rename to Audio and VectorGraphics * Add (SVG) in the name for Vector Graphics * adding CSV to Documents * remove this commented code. * remove this commented code * number range should not go below 0, at-least as default until we make that configurable. * use min not ng-min * description for local crops * Error/Limits highlighting reactive * visual adjustments * Enabling opening filtered folders + corrected select hover states * Varous fixes to resolve issues with unit tests. * Refactor MediaType Documents to only contain Article file type * mark as build-in * predefined MediaPicker3 DataTypes, renaming v2 to "old" * set scale bar current value after min and max has been set * added missing } * update when focal point is dragged * adjusted styling for Image Cropper property editor * correcting comment * remove todo - message for trashed media items works * Changed parameter ordering * Introduced new extension method on MediaWithCrops to get croppings urls in with full path * Reintroducing Single Item Mode * use Multiple instead of SingleMode * renaming and adding multiple to preconfigured datatypes * Change existing media picker to use the Clipboard type MEDIA, enabling shared functionality. * clean up unused clipboard parts * adjusted to new amount * correcting test * Fix unit test * Move MediaWithCrops to separate file and move to Core.Models * parseContentForPaste * clean up * ensure crops is an array. * actively enable focal points, so we dont set focal points that aren't used. * only accept files that matches file extensions from Umbraco Settings * Cleanup * Add references from MediaPicker3 to media * corrections from various feedback * remove comment * correct wording * use windowResizeListener * Show Media Type Selector if there is multiple types that matches the current upload batch * clean-up and refactoring * auto pick option Co-authored-by: Warren Buckley <warren@umbraco.com> Co-authored-by: Niels Lyngsø <nsl@umbraco.com> Co-authored-by: Mads Rasmussen <madsr@hey.com> Co-authored-by: Andy Butland <abutland73@gmail.com> Co-authored-by: Bjarke Berg <mail@bergmania.dk> Co-authored-by: Sebastiaan Janssen <sebastiaan@umbraco.com> Co-authored-by: Elitsa Marinovska <elm@umbraco.dk>
This commit is contained in:
@@ -20,7 +20,7 @@ TODO
|
||||
|
||||
angular.module("umbraco.directives")
|
||||
.directive('umbFileDropzone',
|
||||
function ($timeout, Upload, localizationService, umbRequestHelper, overlayService) {
|
||||
function ($timeout, Upload, localizationService, umbRequestHelper, overlayService, mediaHelper, mediaTypeHelper) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
@@ -88,21 +88,12 @@ angular.module("umbraco.directives")
|
||||
});
|
||||
scope.queue = [];
|
||||
}
|
||||
// One allowed type
|
||||
if (scope.acceptedMediatypes && scope.acceptedMediatypes.length === 1) {
|
||||
// Standard setup - set alias to auto select to let the server best decide which media type to use
|
||||
if (scope.acceptedMediatypes[0].alias === 'Image') {
|
||||
scope.contentTypeAlias = "umbracoAutoSelect";
|
||||
} else {
|
||||
scope.contentTypeAlias = scope.acceptedMediatypes[0].alias;
|
||||
}
|
||||
// If we have Accepted Media Types, we will ask to choose Media Type, if Choose Media Type returns false, it only had one choice and therefor no reason to
|
||||
if (scope.acceptedMediatypes && _requestChooseMediaTypeDialog() === false) {
|
||||
scope.contentTypeAlias = "umbracoAutoSelect";
|
||||
|
||||
_processQueueItem();
|
||||
}
|
||||
// More than one, open dialog
|
||||
if (scope.acceptedMediatypes && scope.acceptedMediatypes.length > 1) {
|
||||
_chooseMediaType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,8 +137,8 @@ angular.module("umbraco.directives")
|
||||
// set percentage property on file
|
||||
file.uploadProgress = progressPercentage;
|
||||
// set uploading status on file
|
||||
file.uploadStatus = "uploading";
|
||||
}
|
||||
file.uploadStatus = "uploading";
|
||||
}
|
||||
})
|
||||
.success(function(data, status, headers, config) {
|
||||
if (data.notifications && data.notifications.length > 0) {
|
||||
@@ -195,35 +186,61 @@ angular.module("umbraco.directives")
|
||||
});
|
||||
}
|
||||
|
||||
function _chooseMediaType() {
|
||||
function _requestChooseMediaTypeDialog() {
|
||||
|
||||
const dialog = {
|
||||
view: "itempicker",
|
||||
filter: scope.acceptedMediatypes.length > 15,
|
||||
availableItems: scope.acceptedMediatypes,
|
||||
submit: function (model) {
|
||||
scope.contentTypeAlias = model.selectedItem.alias;
|
||||
_processQueueItem();
|
||||
if (scope.acceptedMediatypes.length === 1) {
|
||||
// if only one accepted type, then we wont ask to choose.
|
||||
return false;
|
||||
}
|
||||
|
||||
overlayService.close();
|
||||
},
|
||||
close: function () {
|
||||
var uploadFileExtensions = scope.queue.map(file => mediaHelper.getFileExtension(file.name));
|
||||
|
||||
scope.queue.map(function (file) {
|
||||
file.uploadStatus = "error";
|
||||
file.serverErrorMessage = "Cannot upload this file, no mediatype selected";
|
||||
scope.rejected.push(file);
|
||||
});
|
||||
scope.queue = [];
|
||||
var filteredMediaTypes = mediaTypeHelper.getTypeAcceptingFileExtensions(scope.acceptedMediatypes, uploadFileExtensions);
|
||||
|
||||
overlayService.close();
|
||||
}
|
||||
};
|
||||
var mediaTypesNotFile = filteredMediaTypes.filter(mediaType => mediaType.alias !== "File");
|
||||
|
||||
localizationService.localize("defaultdialogs_selectMediaType").then(value => {
|
||||
dialog.title = value;
|
||||
if (mediaTypesNotFile.length <= 1) {
|
||||
// if only one or less accepted types when we have filtered type 'file' out, then we wont ask to choose.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
localizationService.localizeMany(["defaultdialogs_selectMediaType", "mediaType_autoPickMediaType"]).then(function (translations) {
|
||||
|
||||
filteredMediaTypes.push({
|
||||
alias: "umbracoAutoSelect",
|
||||
name: translations[1],
|
||||
icon: "icon-wand"
|
||||
});
|
||||
|
||||
const dialog = {
|
||||
view: "itempicker",
|
||||
filter: filteredMediaTypes.length > 8,
|
||||
availableItems: filteredMediaTypes,
|
||||
submit: function (model) {
|
||||
scope.contentTypeAlias = model.selectedItem.alias;
|
||||
_processQueueItem();
|
||||
|
||||
overlayService.close();
|
||||
},
|
||||
close: function () {
|
||||
|
||||
scope.queue.map(function (file) {
|
||||
file.uploadStatus = "error";
|
||||
file.serverErrorMessage = "No files uploaded, no mediatype selected";
|
||||
scope.rejected.push(file);
|
||||
});
|
||||
scope.queue = [];
|
||||
|
||||
overlayService.close();
|
||||
}
|
||||
};
|
||||
|
||||
dialog.title = translations[0];
|
||||
overlayService.open(dialog);
|
||||
});
|
||||
|
||||
return true;// yes, we did open the choose-media dialog, therefor we return true.
|
||||
}
|
||||
|
||||
scope.handleFiles = function(files, event) {
|
||||
|
||||
@@ -23,15 +23,15 @@ function mediaTypeHelper(mediaTypeResource, $q) {
|
||||
getAllowedImagetypes: function (mediaId){
|
||||
|
||||
// TODO: This is horribly inneficient - why make one request per type!?
|
||||
//This should make a call to c# to get exactly what it's looking for instead of returning every single media type and doing
|
||||
//This should make a call to c# to get exactly what it's looking for instead of returning every single media type and doing
|
||||
//some filtering on the client side.
|
||||
//This is also called multiple times when it's not needed! Example, when launching the media picker, this will be called twice
|
||||
//This is also called multiple times when it's not needed! Example, when launching the media picker, this will be called twice
|
||||
//which means we'll be making at least 6 REST calls to fetch each media type
|
||||
|
||||
// Get All allowedTypes
|
||||
return mediaTypeResource.getAllowedTypes(mediaId)
|
||||
.then(function(types){
|
||||
|
||||
|
||||
var allowedQ = types.map(function(type){
|
||||
return mediaTypeResource.getById(type.id);
|
||||
});
|
||||
@@ -39,16 +39,8 @@ function mediaTypeHelper(mediaTypeResource, $q) {
|
||||
// Get full list
|
||||
return $q.all(allowedQ).then(function(fullTypes){
|
||||
|
||||
// Find all the media types with an Image Cropper property editor
|
||||
var filteredTypes = mediaTypeHelperService.getTypeWithEditor(fullTypes, ['Umbraco.ImageCropper']);
|
||||
|
||||
// If there is only one media type with an Image Cropper we will return this one
|
||||
if(filteredTypes.length === 1) {
|
||||
return filteredTypes;
|
||||
// If there is more than one Image cropper, custom media types have been added, and we return all media types with and Image cropper or UploadField
|
||||
} else {
|
||||
return mediaTypeHelperService.getTypeWithEditor(fullTypes, ['Umbraco.ImageCropper', 'Umbraco.UploadField']);
|
||||
}
|
||||
// Find all the media types with an Image Cropper or Upload Field property editor
|
||||
return mediaTypeHelperService.getTypeWithEditor(fullTypes, ['Umbraco.ImageCropper', 'Umbraco.UploadField']);
|
||||
|
||||
});
|
||||
});
|
||||
@@ -68,6 +60,31 @@ function mediaTypeHelper(mediaTypeResource, $q) {
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
getTypeAcceptingFileExtensions: function (mediaTypes, fileExtensions) {
|
||||
return mediaTypes.filter(mediaType => {
|
||||
var uploadProperty;
|
||||
mediaType.groups.forEach(group => {
|
||||
var foundProperty = group.properties.find(property => property.alias === "umbracoFile");
|
||||
if(foundProperty) {
|
||||
uploadProperty = foundProperty;
|
||||
}
|
||||
});
|
||||
if(uploadProperty) {
|
||||
var acceptedFileExtensions;
|
||||
if(uploadProperty.editor === "Umbraco.ImageCropper") {
|
||||
acceptedFileExtensions = Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes;
|
||||
} else if(uploadProperty.editor === "Umbraco.UploadField") {
|
||||
acceptedFileExtensions = (uploadProperty.config.fileExtensions && uploadProperty.config.fileExtensions.length > 0) ? uploadProperty.config.fileExtensions.map(x => x.value) : null;
|
||||
}
|
||||
if(acceptedFileExtensions && acceptedFileExtensions.length > 0) {
|
||||
return fileExtensions.length === fileExtensions.filter(fileExt => acceptedFileExtensions.includes(fileExt)).length;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -343,6 +343,7 @@
|
||||
<area alias="mediaType">
|
||||
<key alias="copyFailed">Kopiering af medietypen fejlede</key>
|
||||
<key alias="moveFailed">Flytning af medietypen fejlede</key>
|
||||
<key alias="autoPickMediaType">Auto vælg</key>
|
||||
</area>
|
||||
<area alias="memberType">
|
||||
<key alias="copyFailed">Kopiering af medlemstypen fejlede</key>
|
||||
|
||||
@@ -362,6 +362,7 @@
|
||||
<area alias="mediaType">
|
||||
<key alias="copyFailed">Failed to copy media type</key>
|
||||
<key alias="moveFailed">Failed to move media type</key>
|
||||
<key alias="autoPickMediaType">Auto pick</key>
|
||||
</area>
|
||||
<area alias="memberType">
|
||||
<key alias="copyFailed">Failed to copy member type</key>
|
||||
|
||||
@@ -369,6 +369,7 @@
|
||||
<area alias="mediaType">
|
||||
<key alias="copyFailed">Failed to copy media type</key>
|
||||
<key alias="moveFailed">Failed to move media type</key>
|
||||
<key alias="autoPickMediaType">Auto pick</key>
|
||||
</area>
|
||||
<area alias="memberType">
|
||||
<key alias="copyFailed">Failed to copy member type</key>
|
||||
|
||||
Reference in New Issue
Block a user