using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
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;
using Umbraco.Core.Strings;
using Umbraco.Web.Media;
namespace Umbraco.Web.PropertyEditors
{
[DataEditor(
Constants.PropertyEditors.Aliases.UploadField,
"File upload",
"fileupload",
Group = Constants.PropertyEditors.Groups.Media,
Icon = "icon-download-alt")]
public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator
{
private readonly IMediaFileSystem _mediaFileSystem;
private readonly IContentSettings _contentSettings;
private readonly UploadAutoFillProperties _uploadAutoFillProperties;
private readonly IDataTypeService _dataTypeService;
private readonly ILocalizationService _localizationService;
private readonly ILocalizedTextService _localizedTextService;
public FileUploadPropertyEditor(ILogger logger, IMediaFileSystem mediaFileSystem, IContentSettings contentSettings, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper)
: base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper)
{
_mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem));
_contentSettings = contentSettings;
_dataTypeService = dataTypeService;
_localizationService = localizationService;
_localizedTextService = localizedTextService;
_uploadAutoFillProperties = new UploadAutoFillProperties(_mediaFileSystem, logger, contentSettings);
}
///
/// Creates the corresponding property value editor.
///
/// The corresponding property value editor.
protected override IDataValueEditor CreateValueEditor()
{
var editor = new FileUploadPropertyValueEditor(Attribute, _mediaFileSystem, _dataTypeService, _localizationService, _localizedTextService, ShortStringHelper, _contentSettings);
editor.Validators.Add(new UploadFileTypeValidator(_localizedTextService, _contentSettings));
return editor;
}
public bool TryGetMediaPath(string alias, object value, out string mediaPath)
{
if (alias == Alias)
{
mediaPath = value?.ToString();
return true;
}
mediaPath = null;
return false;
}
///
/// Gets a value indicating whether a property is an upload field.
///
/// The property.
/// A value indicating whether a property is an upload field, and (optionally) has a non-empty value.
private static bool IsUploadField(IProperty property)
{
return property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.UploadField;
}
///
/// Ensures any files associated are removed
///
///
internal IEnumerable ServiceDeleted(IEnumerable deletedEntities)
{
return deletedEntities.SelectMany(x => x.Properties)
.Where(IsUploadField)
.SelectMany(GetFilePathsFromPropertyValues)
.Distinct();
}
///
/// Look through all property values stored against the property and resolve any file paths stored
///
///
///
private IEnumerable GetFilePathsFromPropertyValues(IProperty prop)
{
var propVals = prop.Values;
foreach (var propertyValue in propVals)
{
//check if the published value contains data and return it
var propVal = propertyValue.PublishedValue;
if (propVal != null && propVal is string str1 && !str1.IsNullOrWhiteSpace())
yield return _mediaFileSystem.GetRelativePath(str1);
//check if the edited value contains data and return it
propVal = propertyValue.EditedValue;
if (propVal != null && propVal is string str2 && !str2.IsNullOrWhiteSpace())
yield return _mediaFileSystem.GetRelativePath(str2);
}
}
///
/// After a content has been copied, also copy uploaded files.
///
/// The event sender.
/// The event arguments.
internal void ContentServiceCopied(IContentService sender, Core.Events.CopyEventArgs args)
{
// get the upload field properties with a value
var properties = args.Original.Properties.Where(IsUploadField);
// copy files
var isUpdated = false;
foreach (var property in properties)
{
//copy each of the property values (variants, segments) to the destination
foreach (var propertyValue in property.Values)
{
var propVal = property.GetValue(propertyValue.Culture, propertyValue.Segment);
if (propVal == null || !(propVal is string str) || str.IsNullOrWhiteSpace()) continue;
var sourcePath = _mediaFileSystem.GetRelativePath(str);
var copyPath = _mediaFileSystem.CopyFile(args.Copy, property.PropertyType, sourcePath);
args.Copy.SetValue(property.Alias, _mediaFileSystem.GetUrl(copyPath), propertyValue.Culture, propertyValue.Segment);
isUpdated = true;
}
}
// if updated, re-save the copy with the updated value
if (isUpdated)
sender.Save(args.Copy);
}
///
/// After a media has been created, auto-fill the properties.
///
/// The event sender.
/// The event arguments.
internal void MediaServiceCreated(IMediaService sender, Core.Events.NewEventArgs args)
{
AutoFillProperties(args.Entity);
}
///
/// After a media has been saved, auto-fill the properties.
///
/// The event sender.
/// The event arguments.
internal void MediaServiceSaving(IMediaService sender, Core.Events.SaveEventArgs args)
{
foreach (var entity in args.SavedEntities)
AutoFillProperties(entity);
}
///
/// After a content item has been saved, auto-fill the properties.
///
/// The event sender.
/// The event arguments.
internal void ContentServiceSaving(IContentService sender, Core.Events.SaveEventArgs args)
{
foreach (var entity in args.SavedEntities)
AutoFillProperties(entity);
}
///
/// Auto-fill properties (or clear).
///
private void AutoFillProperties(IContentBase model)
{
var properties = model.Properties.Where(IsUploadField);
foreach (var property in properties)
{
var autoFillConfig = _contentSettings.GetConfig(property.Alias);
if (autoFillConfig == null) continue;
foreach (var pvalue in property.Values)
{
var svalue = property.GetValue(pvalue.Culture, pvalue.Segment) as string;
if (string.IsNullOrWhiteSpace(svalue))
_uploadAutoFillProperties.Reset(model, autoFillConfig, pvalue.Culture, pvalue.Segment);
else
_uploadAutoFillProperties.Populate(model, autoFillConfig, _mediaFileSystem.GetRelativePath(svalue), pvalue.Culture, pvalue.Segment);
}
}
}
}
}