b7985cb2c8
# Conflicts: # src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs # src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs # src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs
313 lines
14 KiB
C#
313 lines
14 KiB
C#
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Umbraco.Core;
|
|
using Umbraco.Core.Configuration;
|
|
using Umbraco.Core.Configuration.UmbracoSettings;
|
|
using Umbraco.Core.IO;
|
|
using Umbraco.Core.Logging;
|
|
using Umbraco.Core.Models;
|
|
using Umbraco.Core.PropertyEditors;
|
|
using Umbraco.Core.Services;
|
|
|
|
namespace Umbraco.Web.PropertyEditors
|
|
{
|
|
[PropertyEditor(Constants.PropertyEditors.ImageCropperAlias, "Image Cropper", "imagecropper", ValueType = PropertyEditorValueTypes.Json, HideLabel = false, Group="media", Icon="icon-crop")]
|
|
public class ImageCropperPropertyEditor : PropertyEditor, IApplicationEventHandler
|
|
{
|
|
private readonly MediaFileSystem _mediaFileSystem;
|
|
private readonly IContentSection _contentSettings;
|
|
|
|
public ImageCropperPropertyEditor(ILogger logger, MediaFileSystem mediaFileSystem, IContentSection contentSettings)
|
|
: base(logger)
|
|
{
|
|
if (mediaFileSystem == null) throw new ArgumentNullException("mediaFileSystem");
|
|
if (contentSettings == null) throw new ArgumentNullException("contentSettings");
|
|
|
|
_applicationStartup = new FileUploadPropertyEditorApplicationStartup(this);
|
|
|
|
_mediaFileSystem = mediaFileSystem;
|
|
_contentSettings = contentSettings;
|
|
|
|
_internalPreValues = new Dictionary<string, object>
|
|
{
|
|
{"focalPoint", "{left: 0.5, top: 0.5}"},
|
|
{"src", ""}
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates our custom value editor
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected override PropertyValueEditor CreateValueEditor()
|
|
{
|
|
var baseEditor = base.CreateValueEditor();
|
|
return new ImageCropperPropertyValueEditor(baseEditor, Logger, _mediaFileSystem, _contentSettings);
|
|
}
|
|
|
|
protected override PreValueEditor CreatePreValueEditor()
|
|
{
|
|
return new ImageCropperPreValueEditor();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Ensures any files associated are removed
|
|
/// </summary>
|
|
/// <param name="allPropertyData"></param>
|
|
IEnumerable<string> ServiceEmptiedRecycleBin(Dictionary<int, IEnumerable<Property>> allPropertyData)
|
|
{
|
|
var list = new List<string>();
|
|
//Get all values for any image croppers found
|
|
foreach (var cropperVal in allPropertyData
|
|
.SelectMany(x => x.Value)
|
|
.Where(x => x.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.ImageCropperAlias)
|
|
.Select(x => x.Value)
|
|
.WhereNotNull())
|
|
{
|
|
JObject json;
|
|
try
|
|
{
|
|
json = JsonConvert.DeserializeObject<JObject>(cropperVal.ToString());
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Error<ImageCropperPropertyEditor>("An error occurred parsing the value stored in the image cropper value: " + cropperVal, ex);
|
|
continue;
|
|
}
|
|
|
|
if (json["src"] != null && json["src"].ToString().IsNullOrWhiteSpace() == false)
|
|
{
|
|
list.Add(json["src"].ToString());
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensures any files associated are removed
|
|
/// </summary>
|
|
/// <param name="deletedEntities"></param>
|
|
IEnumerable<string> ServiceDeleted(IEnumerable<ContentBase> deletedEntities)
|
|
{
|
|
var list = new List<string>();
|
|
foreach (var property in deletedEntities.SelectMany(deletedEntity => deletedEntity
|
|
.Properties
|
|
.Where(x => x.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.ImageCropperAlias
|
|
&& x.Value != null
|
|
&& string.IsNullOrEmpty(x.Value.ToString()) == false)))
|
|
{
|
|
JObject json;
|
|
try
|
|
{
|
|
json = JsonConvert.DeserializeObject<JObject>(property.Value.ToString());
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Error<ImageCropperPropertyEditor>("An error occurred parsing the value stored in the image cropper value: " + property.Value, ex);
|
|
continue;
|
|
}
|
|
|
|
if (json["src"] != null && json["src"].ToString().IsNullOrWhiteSpace() == false)
|
|
{
|
|
list.Add(json["src"].ToString());
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// After the content is copied we need to check if there are files that also need to be copied
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
void ContentServiceCopied(IContentService sender, Core.Events.CopyEventArgs<IContent> e)
|
|
{
|
|
if (e.Original.Properties.Any(x => x.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.ImageCropperAlias))
|
|
{
|
|
bool isUpdated = false;
|
|
var fs = _mediaFileSystem;
|
|
|
|
//Loop through properties to check if the content contains media that should be deleted
|
|
foreach (var property in e.Original.Properties.Where(x => x.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.ImageCropperAlias
|
|
&& x.Value != null
|
|
&& string.IsNullOrEmpty(x.Value.ToString()) == false))
|
|
{
|
|
JObject json;
|
|
try
|
|
{
|
|
json = JsonConvert.DeserializeObject<JObject>(property.Value.ToString());
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Error<ImageCropperPropertyEditor>("An error occurred parsing the value stored in the image cropper value: " + property.Value.ToString(), ex);
|
|
continue;
|
|
}
|
|
|
|
if (json["src"] != null && json["src"].ToString().IsNullOrWhiteSpace() == false)
|
|
{
|
|
if (fs.FileExists(fs.GetRelativePath(json["src"].ToString())))
|
|
{
|
|
var currentPath = fs.GetRelativePath(json["src"].ToString());
|
|
var propertyId = e.Copy.Properties.First(x => x.Alias == property.Alias).Id;
|
|
var newPath = fs.GetRelativePath(propertyId, System.IO.Path.GetFileName(currentPath));
|
|
|
|
fs.CopyFile(currentPath, newPath);
|
|
json["src"] = fs.GetUrl(newPath);
|
|
e.Copy.SetValue(property.Alias, json.ToString());
|
|
|
|
//Copy thumbnails
|
|
foreach (var thumbPath in fs.GetThumbnails(currentPath))
|
|
{
|
|
var newThumbPath = fs.GetRelativePath(propertyId, System.IO.Path.GetFileName(thumbPath));
|
|
fs.CopyFile(thumbPath, newThumbPath);
|
|
}
|
|
isUpdated = true;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if (isUpdated)
|
|
{
|
|
//need to re-save the copy with the updated path value
|
|
sender.Save(e.Copy);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MediaServiceCreated(IMediaService sender, Core.Events.NewEventArgs<IMedia> e)
|
|
{
|
|
AutoFillProperties(e.Entity);
|
|
}
|
|
|
|
void MediaServiceSaving(IMediaService sender, Core.Events.SaveEventArgs<IMedia> e)
|
|
{
|
|
foreach (var m in e.SavedEntities)
|
|
{
|
|
AutoFillProperties(m);
|
|
}
|
|
}
|
|
|
|
void AutoFillProperties(IContentBase model)
|
|
{
|
|
foreach (var p in model.Properties.Where(x => x.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.ImageCropperAlias))
|
|
{
|
|
var uploadFieldConfigNode =
|
|
_contentSettings.ImageAutoFillProperties
|
|
.FirstOrDefault(x => x.Alias == p.Alias);
|
|
|
|
if (uploadFieldConfigNode != null)
|
|
{
|
|
if (p.Value != null)
|
|
{
|
|
JObject json = null;
|
|
try
|
|
{
|
|
json = JObject.Parse((string)p.Value);
|
|
}
|
|
catch (JsonException)
|
|
{
|
|
//note: we are swallowing this exception because in some cases a normal string/non json value will be passed in which will just be the
|
|
// file path like /media/23454/hello.jpg
|
|
// This will happen everytime an image is uploaded via the folder browser and we don't really want to pollute the log since it's not actually
|
|
// a problem and we take care of this below.
|
|
// see: http://issues.umbraco.org/issue/U4-4756
|
|
}
|
|
if (json != null && json["src"] != null)
|
|
{
|
|
model.PopulateFileMetaDataProperties(uploadFieldConfigNode, json["src"].Value<string>());
|
|
}
|
|
else if (p.Value is string)
|
|
{
|
|
var src = p.Value == null ? string.Empty : p.Value.ToString();
|
|
var config = ApplicationContext.Current.Services.DataTypeService.GetPreValuesByDataTypeId(p.PropertyType.DataTypeDefinitionId).FirstOrDefault();
|
|
var crops = string.IsNullOrEmpty(config) == false ? config : "[]";
|
|
p.Value = "{src: '" + p.Value + "', crops: " + crops + "}";
|
|
//Only provide the source path, not the whole JSON value
|
|
model.PopulateFileMetaDataProperties(uploadFieldConfigNode, src);
|
|
}
|
|
}
|
|
else
|
|
model.ResetFileMetaDataProperties(uploadFieldConfigNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
private IDictionary<string, object> _internalPreValues;
|
|
public override IDictionary<string, object> DefaultPreValues
|
|
{
|
|
get { return _internalPreValues; }
|
|
set { _internalPreValues = value; }
|
|
}
|
|
|
|
internal class ImageCropperPreValueEditor : PreValueEditor
|
|
{
|
|
[PreValueField("crops", "Crop sizes", "views/propertyeditors/imagecropper/imagecropper.prevalues.html")]
|
|
public string Crops { get; set; }
|
|
}
|
|
|
|
#region Application event handler, used to bind to events on startup
|
|
|
|
private readonly FileUploadPropertyEditorApplicationStartup _applicationStartup;
|
|
|
|
/// <summary>
|
|
/// we're using a sub -class because this has the logic to prevent it from executing if the application is not configured
|
|
/// </summary>
|
|
private class FileUploadPropertyEditorApplicationStartup : ApplicationEventHandler
|
|
{
|
|
private readonly ImageCropperPropertyEditor _imageCropperPropertyEditor;
|
|
|
|
public FileUploadPropertyEditorApplicationStartup(ImageCropperPropertyEditor imageCropperPropertyEditor)
|
|
{
|
|
_imageCropperPropertyEditor = imageCropperPropertyEditor;
|
|
}
|
|
|
|
/// <summary>
|
|
/// We're going to bind to the MediaService Saving event so that we can populate the umbracoFile size, type, etc... label fields
|
|
/// if we find any attached to the current media item.
|
|
/// </summary>
|
|
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
|
|
{
|
|
MediaService.Saving += _imageCropperPropertyEditor.MediaServiceSaving;
|
|
MediaService.Created += _imageCropperPropertyEditor.MediaServiceCreated;
|
|
ContentService.Copied += _imageCropperPropertyEditor.ContentServiceCopied;
|
|
|
|
MediaService.Deleted += (sender, args) =>
|
|
args.MediaFilesToDelete.AddRange(_imageCropperPropertyEditor.ServiceDeleted(args.DeletedEntities.Cast<ContentBase>()));
|
|
MediaService.EmptiedRecycleBin += (sender, args) =>
|
|
args.Files.AddRange(_imageCropperPropertyEditor.ServiceEmptiedRecycleBin(args.AllPropertyData));
|
|
ContentService.Deleted += (sender, args) =>
|
|
args.MediaFilesToDelete.AddRange(_imageCropperPropertyEditor.ServiceDeleted(args.DeletedEntities.Cast<ContentBase>()));
|
|
ContentService.EmptiedRecycleBin += (sender, args) =>
|
|
args.Files.AddRange(_imageCropperPropertyEditor.ServiceEmptiedRecycleBin(args.AllPropertyData));
|
|
MemberService.Deleted += (sender, args) =>
|
|
args.MediaFilesToDelete.AddRange(_imageCropperPropertyEditor.ServiceDeleted(args.DeletedEntities.Cast<ContentBase>()));
|
|
}
|
|
}
|
|
|
|
public void OnApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
|
|
{
|
|
//wrap
|
|
_applicationStartup.OnApplicationInitialized(umbracoApplication, applicationContext);
|
|
}
|
|
public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
|
|
{
|
|
//wrap
|
|
_applicationStartup.OnApplicationStarting(umbracoApplication, applicationContext);
|
|
}
|
|
public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
|
|
{
|
|
//wrap
|
|
_applicationStartup.OnApplicationStarted(umbracoApplication, applicationContext);
|
|
}
|
|
#endregion
|
|
}
|
|
}
|