Media Previews (#11888)

Co-authored-by: Niels Lyngsø <nsl@umbraco.com>
Co-authored-by: Mads Rasmussen <madsr@hey.com>
Co-authored-by: Paul Johnson <pmj@umbraco.com>
This commit is contained in:
Niels Lyngsø
2022-01-20 16:47:00 +01:00
committed by GitHub
parent 029a261476
commit e204c5bab9
29 changed files with 530 additions and 201 deletions

View File

@@ -5,3 +5,4 @@ paths:
paths-ignore:
- '**/node_modules'
- 'src/Umbraco.Web.UI/wwwroot'

View File

@@ -22,8 +22,8 @@ jobs:
with:
config-file: ./.github/config/codeql-config.yml
- name: Build
run: dotnet build umbraco-netcore-only.sln # also runs npm build
- name: dotnet build
run: dotnet build umbraco-netcore-only.sln
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -134,7 +134,7 @@ function dependencies() {
"./node_modules/angular-messages/angular-messages.min.js.map"
],
"base": "./node_modules/angular-messages"
},
},
{
"name": "angular-mocks",
"src": ["./node_modules/angular-mocks/angular-mocks.js"],
@@ -285,11 +285,11 @@ function dependencies() {
// add streams for node modules
nodeModules.forEach(module => {
var task = gulp.src(module.src, { base: module.base, allowEmpty: true });
_.forEach(config.roots, function(root){
task = task.pipe(gulp.dest(root + config.targets.lib + "/" + module.name))
});
stream.add(task);
});
@@ -299,12 +299,12 @@ function dependencies() {
_.forEach(config.roots, function(root){
libTask = libTask.pipe(gulp.dest(root + config.targets.lib))
});
stream.add(libTask);
//Copies all static assets into /root / assets folder
//css, fonts and image files
var assetsTask = gulp.src(config.sources.globs.assets, { allowEmpty: true });
assetsTask = assetsTask.pipe(imagemin([
imagemin.gifsicle({interlaced: true}),
@@ -321,8 +321,8 @@ function dependencies() {
_.forEach(config.roots, function(root){
assetsTask = assetsTask.pipe(gulp.dest(root + config.targets.assets));
});
stream.add(assetsTask);
// Copies all the less files related to the preview into their folder
@@ -342,13 +342,13 @@ function dependencies() {
configTask = configTask.pipe(gulp.dest(root + config.targets.views + "/propertyeditors/grid/config"));
});
stream.add(configTask);
var dashboardTask = gulp.src("src/views/dashboard/default/*.jpg", { allowEmpty: true });
_.forEach(config.roots, function(root){
dashboardTask = dashboardTask .pipe(gulp.dest(root + config.targets.views + "/dashboard/default"));
});
stream.add(dashboardTask);
return stream;
};

View File

@@ -31,6 +31,8 @@ const coreBuild = parallel(dependencies, js, less, views);
// ***********************************************************
exports.build = series(coreBuild, testUnit);
exports.buildDev = series(setDevelopmentMode, coreBuild);
exports.coreBuild = coreBuild;
exports.dev = series(setDevelopmentMode, coreBuild, runUnitTestServer, watchTask);
exports.watch = series(watchTask);

View File

@@ -7,6 +7,7 @@
"e2e": "gulp testE2e",
"build": "gulp build",
"build:skip-tests": "gulp coreBuild",
"build:dev": "gulp buildDev",
"dev": "gulp dev",
"fastdev": "gulp fastdev",
"watch": "gulp watch"

View File

@@ -117,12 +117,11 @@
vm.files = _.map(files, function (file) {
var f = {
fileName: file,
fileSrc: file,
isImage: mediaHelper.detectIfImageByExtension(file),
extension: getExtension(file)
};
f.fileSrc = getThumbnail(f);
return f;
});
@@ -190,21 +189,6 @@
}
}
function getThumbnail(file) {
if (file.extension === 'svg') {
return file.fileName;
}
if (!file.isImage) {
return null;
}
var thumbnailUrl = mediaHelper.getThumbnailFromPath(file.fileName);
return thumbnailUrl;
}
function getExtension(fileName) {
var extension = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length);
return extension.toLowerCase();
@@ -238,7 +222,8 @@
isImage: isImage,
extension: extension,
fileName: files[i].name,
isClientSide: true
isClientSide: true,
fileData: files[i]
};
// Save the file object to the files collection
@@ -247,6 +232,7 @@
//special check for a comma in the name
newVal += files[i].name.split(',').join('-') + ",";
// TODO: I would love to remove this part. But I'm affright it would be breaking if removed. Its not used by File upload anymore as each preview handles the client-side data on their own.
if (isImage || extension === "svg") {
var deferred = $q.defer();

View File

@@ -0,0 +1,94 @@
/**
* @ngdoc service
* @name umbraco.services.mediaPreview
* @description A service providing views used for dealing with previewing files.
*
* ##usage
* The service allows for registering and retrieving the view for one or more file extensions.
*
* You can register your own custom view in this way:
*
* <pre>
* angular.module('umbraco').run(['mediaPreview', function (mediaPreview) {
* mediaPreview.registerPreview(['docx'], "app_plugins/My_PACKAGE/preview.html");
* }]);
* </pre>
*
* Here is a example of a preview template. (base on the audio-preview).
*
* <pre>
* <audio ng-if="vm.clientSide" name="{{vm.name}}" controls>
* <source ng-init="previewUrl = URL.createObjectURL(vm.clientSideData)" ng-src="{{previewUrl}}"/>
* </audio>
* <audio ng-if="!vm.clientSide" name="{{vm.name}}" controls>
* <source ng-src="{{vm.source}}" />
* </audio>
* </pre>
*
* Notice that there often is a need to differentiate based on the file-data origin. In the state of the file still begin located locally its often needed to create an Object-URL for the data to be useable in HTML. As well you might want to provide links for the uploaded file when it is uploaded to the server. See 'vm.clientSide' and 'vm.clientSideData'.
*
**/
function mediaPreview() {
const DEFAULT_FILE_PREVIEW = "views/components/media/umbfilepreview/umb-file-preview.html";
var _mediaPreviews = [];
function init(service) {
service.registerPreview(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes.split(","), "views/components/media/umbimagepreview/umb-image-preview.html");
service.registerPreview(["svg"], "views/components/media/umbimagepreview/umb-image-preview.html");
service.registerPreview(["mp4", "mov", "webm", "ogv"], "views/components/media/umbvideopreview/umb-video-preview.html");
service.registerPreview(["mp3", "weba", "oga", "opus"], "views/components/media/umbaudiopreview/umb-audio-preview.html");
}
var service = {
/**
* @ngdoc method
* @name umbraco.services.mediaPreview#getMediaPreview
* @methodOf umbraco.services.mediaPreview
*
* @param {string} fileExtension A string with the file extension, example: "pdf"
*
* @description
* The registered view matching this file extensions will be returned.
*
*/
getMediaPreview: function (fileExtension) {
fileExtension = fileExtension.toLowerCase();
var previewObject = _mediaPreviews.find((preview) => preview.fileExtensions.indexOf(fileExtension) !== -1);
if(previewObject !== undefined) {
return previewObject.view;
}
return DEFAULT_FILE_PREVIEW;
},
/**
* @ngdoc method
* @name umbraco.services.mediaPreview#registerPreview
* @methodOf umbraco.services.mediaPreview
*
* @param {array} fileExtensions An array of file extensions, example: ["pdf", "jpg"]
* @param {array} view A URL to the view to be used for these file extensions.
*
* @description
* The registered view will be used when file extensions match the given file.
*
*/
registerPreview: function (fileExtensions, view) {
_mediaPreviews.push({
fileExtensions: fileExtensions.map(e => e.toLowerCase()),
view: view
})
}
};
init(service);
return service;
} angular.module('umbraco.services').factory('mediaPreview', mediaPreview);

View File

@@ -202,6 +202,11 @@
@import "components/contextdialogs/umb-dialog-datatype-delete.less";
@import "components/umbemailmarketing.less";
@import "../views/components/media/umbmediapreview/umb-media-preview.less";
@import "../views/components/media/umbaudiopreview/umb-audio-preview.less";
@import "../views/components/media/umbfilepreview/umb-file-preview.less";
@import "../views/components/media/umbimagepreview/umb-image-preview.less";
@import "../views/components/media/umbvideopreview/umb-video-preview.less";
// Editors
@import "../views/common/infiniteeditors/rollback/rollback.less";

View File

@@ -38,4 +38,8 @@
border-color: @gray-1;
}
}
.umb-property-file-upload--actions {
margin-top: 10px;
}
}

View File

@@ -828,11 +828,15 @@
.umb-fileupload {
display: flex;
flex-direction: column;
padding: 20px;
border: 1px solid @inputBorder;
box-sizing: border-box;
width: 100%;
.umb-property-editor--limit-width();
}
.umb-fileupload .preview {
border-radius: 5px;
border: 1px solid @gray-6;
padding: 3px;
background: @gray-9;
float: left;

View File

@@ -38,6 +38,8 @@ angular.module("umbraco")
entityResource.getById(vm.mediaEntry.mediaKey, "Media").then(function (mediaEntity) {
vm.media = mediaEntity;
vm.imageSrc = mediaHelper.resolveFileFromEntity(mediaEntity, true);
vm.fileSrc = mediaHelper.resolveFileFromEntity(mediaEntity, false);
vm.fileExtension = mediaHelper.getFileExtension(vm.fileSrc);
vm.loading = false;
vm.hasDimensions = false;
vm.isCroppable = false;

View File

@@ -1,127 +1,148 @@
<div class="umb-media-entry-editor" ng-controller="Umbraco.Editors.MediaEntryEditorController as vm">
<div
class="umb-media-entry-editor"
ng-controller="Umbraco.Editors.MediaEntryEditorController as vm"
>
<ng-form name="vm.imageCropperForm" val-form-manager>
<umb-editor-view umb-tabs ng-if="!page.loading">
<umb-editor-header
name="vm.title"
name-required="false"
name-locked="true"
hide-alias="true"
hide-icon="true"
hide-description="true"
>
</umb-editor-header>
<ng-form name="vm.imageCropperForm" val-form-manager>
<div class="umb-editor-container umb-panel-body umb-scrollable">
<div ng-if="vm.media.trashed" class="umb-editor--trashed-message">
<umb-icon icon="icon-trash" class="icon"></umb-icon>
<localize key="content_nodeIsInTrash"
>This item is in the Recycle Bin</localize
>
</div>
<umb-editor-view umb-tabs ng-if="!page.loading">
<div class="umb-media-entry-editor__pane">
<div class="umb-media-entry-editor__crops" ng-if="vm.isCroppable">
<button
class="btn-reset umb-outline"
ng-class="{'--is-active':vm.currentCrop === null}"
ng-click="vm.deselectCrop()"
>
<span class="__text">Media</span>
</button>
<umb-editor-header
name="vm.title"
name-required="false"
name-locked="true"
hide-alias="true"
hide-icon="true"
hide-description="true">
</umb-editor-header>
<div class="umb-editor-container umb-panel-body umb-scrollable">
<div ng-if="vm.media.trashed" class="umb-editor--trashed-message">
<umb-icon icon="icon-trash" class="icon"></umb-icon> <localize key="content_nodeIsInTrash">This item is in the Recycle Bin</localize>
</div>
<div class="umb-media-entry-editor__pane">
<div class="umb-media-entry-editor__crops" ng-if="vm.isCroppable">
<button class="btn-reset umb-outline" ng-class="{'--is-active':vm.currentCrop === null}" ng-click="vm.deselectCrop()">
<span class="__text">Media</span>
</button>
<button ng-repeat="crop in vm.mediaEntry.crops track by crop.alias" class="btn-reset umb-outline" ng-class="{'--is-active':vm.currentCrop.alias === crop.alias, '--is-defined':!!vm.currentCrop.coordinates}" ng-click="vm.selectCrop(crop)">
<umb-image-thumbnail center="vm.mediaEntry.focalPoint"
crop="crop.coordinates"
src="vm.imageSrc"
height="{{crop.height}}"
width="{{crop.width}}"
max-size="75">
</umb-image-thumbnail>
<span class="__text">{{crop.label}}</span>
</button>
</div>
<div class="imagecropper umb-media-entry-editor__imagecropper">
<div ng-if="vm.currentCrop" class="umb-cropper__container">
<umb-image-crop height="{{vm.currentCrop.height}}"
width="{{vm.currentCrop.width}}"
crop="vm.currentCrop.coordinates"
alias="{{vm.currentCrop.alias}}"
force-update="{{vm.forceUpdateCrop}}"
center="vm.mediaEntry.focalPoint"
src="vm.imageSrc">
<button class="btn btn-link" ng-click="vm.resetCrop()">
<umb-icon icon="icon-wrong"></umb-icon> <localize key="imagecropper_reset">Reset this crop</localize>
</button>
</umb-image-crop>
</div>
<div ng-if="!vm.currentCrop" class="umb-cropper-imageholder">
<umb-image-gravity
class="umb-media-entry-editor__imageholder"
ng-if="vm.imageSrc"
src="vm.imageSrc"
center="vm.mediaEntry.focalPoint"
disable-focal-point="!vm.model.enableFocalPointSetter"
on-value-changed="vm.focalPointChanged(left, top)"
on-image-loaded="vm.onImageLoaded(isCroppable, hasDimensions)">
</umb-image-gravity>
<umb-file-icon
class="umb-media-entry-editor__imageholder"
ng-if="vm.loading === false && !vm.imageSrc"
ng-class="{'trashed': vm.media.trashed}"
extension="{{vm.media.extension}}"
icon="{{vm.media.icon}}"
size="m"
text="{{vm.media.name}}">
</umb-file-icon>
<div class="umb-media-entry-editor__imageholder-actions">
<button class="btn btn-link" ng-click="vm.repickMedia()">
<umb-icon icon="icon-wrong"></umb-icon> <localize key="mediaPicker_changeMedia">Replace media</localize>
</button>
<button class="btn btn-link" ng-click="vm.openMedia()">
<umb-icon icon="icon-out"></umb-icon> <localize key="mediaPicker_openMedia">Open media</localize>
</button>
<button type="button" ng-show="vm.model.enableFocalPointSetter && (vm.mediaEntry.focalPoint.left !== 0.5 && vm.mediaEntry.focalPoint.top !== 0.5)" class="btn btn-link" ng-click="vm.focalPointChanged(0.5, 0.5)">
<umb-icon icon="icon-axis-rotation"></umb-icon> <localize key="content_resetFocalPoint">Reset focal point</localize>
</button>
</div>
</div>
</div>
</div>
<button
ng-repeat="crop in vm.mediaEntry.crops track by crop.alias"
class="btn-reset umb-outline"
ng-class="{'--is-active':vm.currentCrop.alias === crop.alias, '--is-defined':!!vm.currentCrop.coordinates}"
ng-click="vm.selectCrop(crop)"
>
<umb-image-thumbnail
center="vm.mediaEntry.focalPoint"
crop="crop.coordinates"
src="vm.imageSrc"
height="{{crop.height}}"
width="{{crop.width}}"
max-size="75"
>
</umb-image-thumbnail>
<span class="__text">{{crop.label}}</span>
</button>
</div>
<div class="imagecropper umb-media-entry-editor__imagecropper">
<div ng-if="vm.currentCrop" class="umb-cropper__container">
<umb-image-crop
height="{{vm.currentCrop.height}}"
width="{{vm.currentCrop.width}}"
crop="vm.currentCrop.coordinates"
alias="{{vm.currentCrop.alias}}"
force-update="{{vm.forceUpdateCrop}}"
center="vm.mediaEntry.focalPoint"
src="vm.imageSrc"
>
<button class="btn btn-link" ng-click="vm.resetCrop()">
<umb-icon icon="icon-wrong"></umb-icon>
<localize key="imagecropper_reset">Reset this crop</localize>
</button>
</umb-image-crop>
</div>
<umb-editor-footer>
<div ng-if="!vm.currentCrop" class="umb-cropper-imageholder">
<umb-image-gravity
class="umb-media-entry-editor__imageholder"
ng-if="vm.imageSrc"
src="vm.imageSrc"
center="vm.mediaEntry.focalPoint"
disable-focal-point="!vm.model.enableFocalPointSetter"
on-value-changed="vm.focalPointChanged(left, top)"
on-image-loaded="vm.onImageLoaded(isCroppable, hasDimensions)"
>
</umb-image-gravity>
<!-- Missing breadcrumbs -->
<umb-media-preview
ng-if="vm.loading === false && !vm.imageSrc"
class="umb-media-entry-editor__previewholder"
ng-class="{'trashed': vm.media.trashed}"
extension="vm.fileExtension"
source="vm.fileSrc"
icon="vm.media.icon"
name="vm.media.name"
>
</umb-media-preview>
<umb-editor-footer-content-right>
<div class="umb-media-entry-editor__imageholder-actions">
<button class="btn btn-link" ng-click="vm.repickMedia()">
<umb-icon icon="icon-wrong"></umb-icon>
<localize key="mediaPicker_changeMedia"
>Replace media</localize
>
</button>
<button class="btn btn-link" ng-click="vm.openMedia()">
<umb-icon icon="icon-out"></umb-icon>
<localize key="mediaPicker_openMedia">Open media</localize>
</button>
<button
type="button"
ng-show="vm.model.enableFocalPointSetter && (vm.mediaEntry.focalPoint.left !== 0.5 && vm.mediaEntry.focalPoint.top !== 0.5)"
class="btn btn-link"
ng-click="vm.focalPointChanged(0.5, 0.5)"
>
<umb-icon icon="icon-axis-rotation"></umb-icon>
<localize key="content_resetFocalPoint"
>Reset focal point</localize
>
</button>
</div>
</div>
</div>
</div>
</div>
<umb-button
action="vm.close()"
shortcut="esc"
button-style="link"
label="{{vm.closeLabel}}"
type="button">
</umb-button>
<umb-editor-footer>
<!-- Missing breadcrumbs -->
<umb-button
action="vm.submitAndClose()"
button-style="primary"
state="vm.saveButtonState"
label="{{vm.submitLabel}}"
type="button">
</umb-button>
<umb-editor-footer-content-right>
<umb-button
action="vm.close()"
shortcut="esc"
button-style="link"
label="{{vm.closeLabel}}"
type="button"
>
</umb-button>
</umb-editor-footer-content-right>
</umb-editor-footer>
</umb-editor-view>
</ng-form>
<umb-button
action="vm.submitAndClose()"
button-style="primary"
state="vm.saveButtonState"
label="{{vm.submitLabel}}"
type="button"
>
</umb-button>
</umb-editor-footer-content-right>
</umb-editor-footer>
</umb-editor-view>
</ng-form>
</div>

View File

@@ -110,11 +110,23 @@
}
.umb-media-entry-editor__imageholder {
display: flex;
align-items: center;
justify-content: center;
position: relative;
height: calc(100% - 50px);
display: block;
}
.umb-media-entry-editor__previewholder {
position: relative;
height: calc(100% - 50px);
display: flex;
justify-content: center;
align-items: center;
overflow-y: auto;
}
.umb-media-entry-editor__imageholder-actions {

View File

@@ -0,0 +1,8 @@
<div class="umb-audio-preview" ng-controller="umbAudioPreviewController as controller">
<audio ng-if="vm.clientSide" name="{{vm.name}}" controls>
<source ng-init="previewUrl = controller.getClientSideUrl(vm.clientSideData)" ng-src="{{previewUrl}}">
</audio>
<audio ng-if="!vm.clientSide" name="{{vm.name}}" controls>
<source ng-src="{{vm.source}}">
</audio>
</div>

View File

@@ -0,0 +1,18 @@
.umb-audio-preview {
display: flex;
justify-content: center;
align-items: center;
audio {
max-width: 100%;
}
audio::-webkit-media-controls-panel {
background-color: white;
}
audio::-webkit-media-controls {
padding: 6px;
}
audio::-webkit-media-controls-enclosure {
&:extend(.shadow-depth-1);
border-radius: 6px;
}
}

View File

@@ -0,0 +1,11 @@
angular.module("umbraco")
.controller("umbAudioPreviewController",
function () {
var vm = this;
vm.getClientSideUrl = function(source) {
return URL.createObjectURL(source);
}
});

View File

@@ -0,0 +1,18 @@
<div class="umb-file-preview">
<a
class="umb-file-preview--file"
ng-show="!vm.clientSide"
href="#"
ng-href="{{vm.source}}"
alt="{{vm.name}}"
target="_blank"
rel="noopener"
>
<umb-file-icon extension="{{vm.extension}}" size="m"> </umb-file-icon>
<div class="mt2">{{vm.name}}</div>
</a>
<div class="umb-file-preview--file" ng-show="vm.clientSide">
<umb-file-icon extension="{{vm.extension}}" size="m"> </umb-file-icon>
<div class="mt2">{{vm.name}}</div>
</div>
</div>

View File

@@ -0,0 +1,13 @@
.umb-file-preview {
display: flex;
justify-content: center;
align-items: center;
.umb-file-preview--file {
display: block;
box-sizing: border-box;
text-align: center;
max-width: 320px;
padding: 10px;
}
}

View File

@@ -0,0 +1,6 @@
<div class="umb-image-preview" ng-controller="umbImagePreviewController as controller">
<img class="umb-image-preview--image" ng-if="vm.clientSide" ng-init="previewUrl = controller.getClientSideUrl(vm.clientSideData)" ng-src="{{previewUrl}}" alt="{{vm.name}}" />
<a ng-if="!vm.clientSide" href="#" ng-href="{{vm.source}}" target="_blank" rel="noopener">
<img class="umb-image-preview--image" ng-init="previewUrl = controller.getThumbnail(vm.source)" ng-src="{{previewUrl}}" alt="{{vm.name}}" />
</a>
</div>

View File

@@ -0,0 +1,9 @@
.umb-image-preview {
display: flex;
justify-content: center;
align-items: center;
img {
width: 100%;
}
}

View File

@@ -0,0 +1,18 @@
angular.module("umbraco")
.controller("umbImagePreviewController",
function (mediaHelper) {
var vm = this;
vm.getThumbnail = function(source) {
return mediaHelper.getThumbnailFromPath(source) || source;
}
vm.getClientSideUrl = function(sourceData) {
return URL.createObjectURL(sourceData);
}
});

View File

@@ -0,0 +1,14 @@
umb-media-preview {
position: relative;
}
.umb-media-preview {
position: relative;
width: 100%;
height: 100%;
min-height: 240px;
display: flex;
justify-content: center;
align-items: center;
}

View File

@@ -0,0 +1,38 @@
(function () {
"use strict";
angular
.module("umbraco")
.component("umbMediaPreview", {
template: "<div ng-include='vm.previewView' class='umb-media-preview'></div><umb-load-indicator ng-if='vm.loading'></umb-load-indicator>",
controller: UmbMediaPreviewController,
controllerAs: "vm",
bindings: {
extension: "<",
source: "<",
name: "<",
clientSide: "<?",
clientSideData: "<?"
}
});
function UmbMediaPreviewController($scope, mediaPreview) {
var vm = this;
vm.loading = false;
vm.$onInit = function() {
vm.previewView = mediaPreview.getMediaPreview(vm.extension);
}
$scope.$on("mediaPreviewLoadingStart", () => {
vm.loading = true;
})
$scope.$on("mediaPreviewLoadingComplete", () => {
vm.loading = false;
})
}
})();

View File

@@ -0,0 +1,8 @@
<div class="umb-video-preview" ng-controller="umbVideoPreviewController as controller">
<video ng-if="vm.clientSide" name="{{vm.name}}" controls>
<source ng-init="previewUrl = controller.getClientSideUrl(vm.clientSideData)" ng-src="{{previewUrl}}">
</video>
<video ng-if="!vm.clientSide" name="{{vm.name}}" controls>
<source ng-src="{{vm.source}}">
</video>
</div>

View File

@@ -0,0 +1,10 @@
.umb-video-preview {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
video {
max-width: 100%;
max-height: 100%;
}
}

View File

@@ -0,0 +1,15 @@
angular.module("umbraco")
.controller("umbVideoPreviewController",
function () {
var vm = this;
vm.getClientSideUrl = function(source) {
return URL.createObjectURL(source);
}
});

View File

@@ -30,7 +30,7 @@
<umb-file-icon ng-if="vm.loading === false && !vm.thumbnail"
ng-class="{'trashed': vm.media.trashed}"
title="{{vm.media.name}}"
extension="{{vm.media.extension}}"
extension="{{vm.fileExtension}}"
icon="{{vm.media.icon}}"
size="s"
text="{{vm.media.name}}">

View File

@@ -74,6 +74,7 @@
vm.media = mediaEntity;
checkErrorState();
vm.thumbnail = mediaHelper.resolveFileFromEntity(mediaEntity, true);
vm.fileExtension = mediaHelper.getFileExtension(vm.media.metaData.MediaPath);
vm.loading = false;
}, function () {

View File

@@ -1,58 +1,68 @@
<div class="umb-property-file-upload">
<ng-form name="vm.fileUploadForm" ng-class="{ 'drag-over': vm.dragover }">
<input
type="hidden"
ng-model="mandatoryValidator"
ng-required="vm.required && !vm.files.length"
/>
<ng-form name="vm.fileUploadForm" ng-class="{ 'drag-over': vm.dragover }">
<input type="hidden" ng-model="mandatoryValidator" ng-required="vm.required && !vm.files.length" />
<div
class="fileinput-button umb-upload-button-big"
ng-hide="vm.files.length > 0"
>
<umb-icon icon="icon-page-up" class="icon"></umb-icon>
<p><localize key="media_clickToUpload">Click to upload</localize></p>
<umb-single-file-upload
accept-file-ext="vm.acceptFileExt"
></umb-single-file-upload>
</div>
<div class="fileinput-button umb-upload-button-big" ng-hide="vm.files.length > 0">
<umb-icon icon="icon-page-up" class="icon"></umb-icon>
<p><localize key="media_clickToUpload">Click to upload</localize></p>
<umb-single-file-upload accept-file-ext="vm.acceptFileExt"></umb-single-file-upload>
<div ng-if="vm.files.length > 0">
<div ng-if="!vm.hideSelection">
<div class="umb-fileupload clearfix" ng-repeat="file in vm.files">
<umb-media-preview
extension="file.extension"
source="file.fileSrc"
name="file.fileName"
client-side="file.isClientSide"
client-side-data="file.fileData"
></umb-media-preview>
</div>
<div ng-if="vm.files.length > 0">
<div ng-if="!vm.hideSelection">
<div class="umb-fileupload clearfix" ng-repeat="file in vm.files">
<div ng-if="file.isImage || file.extension === 'svg'">
<div class="gravity-container">
<div class="viewport">
<img ng-if="file.isClientSide" ng-src="{{file.fileSrc}}" style="max-width: 100%; max-height: 100%" alt="{{file.fileName}}" />
<a ng-if="!file.isClientSide" href="#" ng-href="{{file.fileSrc}}" target="_blank" rel="noopener">
<img ng-src="{{file.fileSrc}}" style="max-width: 100%; max-height: 100%" alt="{{file.fileName}}" />
</a>
</div>
</div>
</div>
<div ng-if="!file.isImage && file.extension !== 'svg'">
<a class="span6 thumbnail tc" ng-show="!file.isClientSide" href="#" ng-href="{{file.fileName}}" target="_blank" rel="noopener">
<umb-file-icon
extension="{{file.extension}}"
size="m">
</umb-file-icon>
<div class="mt2">{{file.fileName}}</div>
</a>
<div class="span6 thumbnail tc" ng-show="file.isClientSide">
<umb-file-icon
extension="{{file.extension}}"
size="m">
</umb-file-icon>
<div class="mt2">{{file.fileName}}</div>
</div>
</div>
</div>
<div>
<button type="button" class="btn btn-link btn-crop-delete" ng-click="vm.clear()"><umb-icon icon="icon-delete" class="red"></umb-icon> <localize key="content_uploadClear">Remove file</localize></button>
<button type="button" class="sr-only" ng-if="file.isImage" ng-click="vm.clear()"><localize key="content_uploadClearImageContext">Click here to remove the image from the media item</localize></button>
<button type="button" class="sr-only" ng-if="!file.isImage" ng-click="vm.clear()"><localize key="content_uploadClearFileContext">Click here to remove the file from the media item</localize></button>
</div>
</div>
<div ng-if="vm.hideSelection">
<div ng-transclude></div>
</div>
<div class="umb-property-file-upload--actions">
<button
type="button"
class="btn btn-link"
aria-hidden="true"
ng-click="vm.clear()"
>
<i class="icon-trash"></i>
<localize key="content_uploadClear">Remove file</localize>
</button>
<button
type="button"
class="sr-only"
ng-if="file.isImage"
ng-click="vm.clear()"
>
<localize key="content_uploadClearImageContext"
>Click here to remove the image from the media item</localize
>
</button>
<button
type="button"
class="sr-only"
ng-if="!file.isImage"
ng-click="vm.clear()"
>
<localize key="content_uploadClearFileContext"
>Click here to remove the file from the media item</localize
>
</button>
</div>
</ng-form>
</div>
<div ng-if="vm.hideSelection">
<div ng-transclude></div>
</div>
</div>
</ng-form>
</div>