mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-07-15 16:48:32 +08:00
* #6981 Normalized image profile path hash and added profile purging * Profile purge functions rather belong in IImageProfileService * Deleting an Image Profile now also removes all its files too * Comment formatting * Caching the value of the "Orchard.MediaProcessing.NormalizePath" app setting in ImageProfileManager * Code styling in ImageProfileManager * Formatting and code styling ImageProfileManager --------- Co-authored-by: Arjan Noordende <arjan@zumey.com>
This commit is contained in:
parent
6eab0a1260
commit
62038ed1bb
@ -154,7 +154,7 @@ namespace Orchard.MediaProcessing.Controllers {
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
Services.ContentManager.Remove(profile.ContentItem);
|
||||
_profileService.DeleteImageProfile(id);
|
||||
Services.Notifier.Success(T("Image Profile {0} deleted", profile.Name));
|
||||
|
||||
return RedirectToAction("Index");
|
||||
@ -185,6 +185,36 @@ namespace Orchard.MediaProcessing.Controllers {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Purge(int id) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage media profiles")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
if (_profileService.PurgeImageProfile(id)) {
|
||||
Services.Notifier.Information(T("The Image Profile has been purged"));
|
||||
}
|
||||
else {
|
||||
Services.Notifier.Warning(T("Unable to purge the Image Profile, it may already have been purged"));
|
||||
}
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult PurgeObsolete() {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage media profiles")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
if (_profileService.PurgeObsoleteImageProfiles()) {
|
||||
Services.Notifier.Information(T("The obsolete Image Profiles have been purged"));
|
||||
}
|
||||
else {
|
||||
Services.Notifier.Warning(T("Unable to purge the obsolete Image Profiles"));
|
||||
}
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
|
||||
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
|
||||
}
|
||||
|
@ -10,5 +10,12 @@ namespace Orchard.MediaProcessing.Services {
|
||||
void DeleteImageProfile(int id);
|
||||
void MoveUp(int filterId);
|
||||
void MoveDown(int filterId);
|
||||
bool PurgeImageProfile(int id);
|
||||
bool PurgeObsoleteImageProfiles();
|
||||
}
|
||||
|
||||
public static class ImageProfileServiceExtensions {
|
||||
public static string GetNameHashCode(this IImageProfileService service, string name) =>
|
||||
name.GetHashCode().ToString("x").ToLowerInvariant();
|
||||
}
|
||||
}
|
@ -66,7 +66,7 @@ namespace Orchard.MediaProcessing.Services {
|
||||
// path is the publicUrl of the media, so it might contain url-encoded chars
|
||||
|
||||
// try to load the processed filename from cache
|
||||
var filePath = _fileNameProvider.GetFileName(profileName, System.Web.HttpUtility.UrlDecode(path));
|
||||
var filePath = _fileNameProvider.GetFileName(profileName, HttpUtility.UrlDecode(path));
|
||||
bool process = false;
|
||||
|
||||
// Before checking everything else, ensure that the content item that needs to be processed has a ImagePart.
|
||||
@ -79,10 +79,10 @@ namespace Orchard.MediaProcessing.Services {
|
||||
if (checkForProfile) {
|
||||
//after reboot the app cache is empty so we reload the image in the cache if it exists in the _Profiles folder
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
var profileFilePath = _storageProvider.Combine("_Profiles", FormatProfilePath(profileName, System.Web.HttpUtility.UrlDecode(path)));
|
||||
var profileFilePath = _storageProvider.Combine("_Profiles", FormatProfilePath(profileName, HttpUtility.UrlDecode(path)));
|
||||
|
||||
if (_storageProvider.FileExists(profileFilePath)) {
|
||||
_fileNameProvider.UpdateFileName(profileName, System.Web.HttpUtility.UrlDecode(path), profileFilePath);
|
||||
_fileNameProvider.UpdateFileName(profileName, HttpUtility.UrlDecode(path), profileFilePath);
|
||||
filePath = profileFilePath;
|
||||
}
|
||||
}
|
||||
@ -93,18 +93,14 @@ namespace Orchard.MediaProcessing.Services {
|
||||
|
||||
process = true;
|
||||
}
|
||||
|
||||
// the processd file doesn't exist anymore, process it
|
||||
else if (!_storageProvider.FileExists(filePath)) {
|
||||
Logger.Debug("Processed file no longer exists, processing required, profile {0} for image {1}", profileName, path);
|
||||
|
||||
process = true;
|
||||
}
|
||||
|
||||
// if the original file is more recent, process it
|
||||
else {
|
||||
DateTime pathLastUpdated;
|
||||
if (TryGetImageLastUpdated(path, out pathLastUpdated)) {
|
||||
else if (TryGetImageLastUpdated(path, out DateTime pathLastUpdated)) {
|
||||
var filePathLastUpdated = _storageProvider.GetFile(filePath).GetLastUpdated();
|
||||
|
||||
if (pathLastUpdated > filePathLastUpdated) {
|
||||
@ -114,7 +110,7 @@ namespace Orchard.MediaProcessing.Services {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
else {
|
||||
// Since media with no ImagePart have no profile, filePath is null, so it's set again to its original path on the storage provider.
|
||||
if (string.IsNullOrWhiteSpace(filePath)) {
|
||||
filePath = _storageProvider.GetStoragePath(path);
|
||||
@ -129,9 +125,11 @@ namespace Orchard.MediaProcessing.Services {
|
||||
|
||||
if (customFilters == null || !customFilters.Any(c => c != null)) {
|
||||
profilePart = _profileService.GetImageProfileByName(profileName);
|
||||
if (profilePart == null)
|
||||
return String.Empty;
|
||||
} else {
|
||||
if (profilePart == null) {
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
else {
|
||||
profilePart = _services.ContentManager.New<ImageProfilePart>("ImageProfile");
|
||||
profilePart.Name = profileName;
|
||||
foreach (var customFilter in customFilters) {
|
||||
@ -142,13 +140,13 @@ namespace Orchard.MediaProcessing.Services {
|
||||
// prevent two requests from processing the same file at the same time
|
||||
// this is only thread safe at the machine level, so there is a try/catch later
|
||||
// to handle cross machines concurrency
|
||||
lock (String.Intern(path)) {
|
||||
lock (string.Intern(path)) {
|
||||
using (var image = GetImage(path)) {
|
||||
if (image == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var filterContext = new FilterContext { Media = image, FilePath = _storageProvider.Combine("_Profiles", FormatProfilePath(profileName, System.Web.HttpUtility.UrlDecode(path))) };
|
||||
var filterContext = new FilterContext { Media = image, FilePath = _storageProvider.Combine("_Profiles", FormatProfilePath(profileName, HttpUtility.UrlDecode(path))) };
|
||||
|
||||
var tokens = new Dictionary<string, object>();
|
||||
// if a content item is provided, use it while tokenizing
|
||||
@ -166,7 +164,7 @@ namespace Orchard.MediaProcessing.Services {
|
||||
descriptor.Filter(filterContext);
|
||||
}
|
||||
|
||||
_fileNameProvider.UpdateFileName(profileName, System.Web.HttpUtility.UrlDecode(path), filterContext.FilePath);
|
||||
_fileNameProvider.UpdateFileName(profileName, HttpUtility.UrlDecode(path), filterContext.FilePath);
|
||||
|
||||
if (!filterContext.Saved) {
|
||||
try {
|
||||
@ -187,7 +185,8 @@ namespace Orchard.MediaProcessing.Services {
|
||||
// the storage provider may have altered the filepath
|
||||
filterContext.FilePath = newFile.GetPath();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
Logger.Error(e, "A profile could not be processed: " + path);
|
||||
}
|
||||
}
|
||||
@ -215,14 +214,14 @@ namespace Orchard.MediaProcessing.Services {
|
||||
try {
|
||||
var file = _storageProvider.GetFile(storagePath);
|
||||
return file.OpenRead();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
Logger.Error(e, "path:" + path + " storagePath:" + storagePath);
|
||||
}
|
||||
}
|
||||
|
||||
// http://blob.storage-provider.net/my-image.jpg
|
||||
Uri absoluteUri;
|
||||
if (Uri.TryCreate(path, UriKind.Absolute, out absoluteUri)) {
|
||||
if (Uri.TryCreate(path, UriKind.Absolute, out Uri absoluteUri)) {
|
||||
return new WebClient().OpenRead(absoluteUri);
|
||||
}
|
||||
|
||||
@ -248,12 +247,11 @@ namespace Orchard.MediaProcessing.Services {
|
||||
}
|
||||
|
||||
private string FormatProfilePath(string profileName, string path) {
|
||||
|
||||
var filenameWithExtension = Path.GetFileName(path) ?? "";
|
||||
var fileLocation = path.Substring(0, path.Length - filenameWithExtension.Length);
|
||||
|
||||
return _storageProvider.Combine(
|
||||
_storageProvider.Combine(profileName.GetHashCode().ToString("x").ToLowerInvariant(), fileLocation.GetHashCode().ToString("x").ToLowerInvariant()),
|
||||
_storageProvider.Combine(_profileService.GetNameHashCode(profileName), _profileService.GetNameHashCode(fileLocation)),
|
||||
filenameWithExtension);
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Caching;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Data;
|
||||
using Orchard.Localization;
|
||||
using Orchard.FileSystems.Media;
|
||||
using Orchard.Logging;
|
||||
using Orchard.MediaProcessing.Models;
|
||||
|
||||
namespace Orchard.MediaProcessing.Services {
|
||||
public class ImageProfileService : IImageProfileService {
|
||||
public class ImageProfileService : Component, IImageProfileService {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly ICacheManager _cacheManager;
|
||||
private readonly IRepository<FilterRecord> _filterRepository;
|
||||
private readonly ISignals _signals;
|
||||
private readonly IStorageProvider _storageProvider;
|
||||
|
||||
public ImageProfileService(
|
||||
IContentManager contentManager,
|
||||
ICacheManager cacheManager,
|
||||
IRepository<FilterRecord> filterRepository,
|
||||
ISignals signals) {
|
||||
ISignals signals,
|
||||
IStorageProvider storageProvider) {
|
||||
_contentManager = contentManager;
|
||||
_cacheManager = cacheManager;
|
||||
_filterRepository = filterRepository;
|
||||
_signals = signals;
|
||||
_storageProvider = storageProvider;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ImageProfilePart GetImageProfile(int id) {
|
||||
return _contentManager.Get<ImageProfilePart>(id);
|
||||
}
|
||||
@ -70,6 +73,7 @@ namespace Orchard.MediaProcessing.Services {
|
||||
var profile = _contentManager.Get(id);
|
||||
|
||||
if (profile != null) {
|
||||
DeleteImageProfileFolder(profile.As<ImageProfilePart>().Name);
|
||||
_contentManager.Remove(profile);
|
||||
}
|
||||
}
|
||||
@ -115,5 +119,43 @@ namespace Orchard.MediaProcessing.Services {
|
||||
next.Position = filter.Position;
|
||||
filter.Position = temp;
|
||||
}
|
||||
|
||||
public bool PurgeImageProfile(int id) {
|
||||
var profile = GetImageProfile(id);
|
||||
try {
|
||||
DeleteImageProfileFolder(profile.Name);
|
||||
profile.FileNames.Clear();
|
||||
_signals.Trigger("MediaProcessing_Saved_" + profile.Name);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Warning(ex, "Unable to purge image profile '{0}'", profile.Name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool PurgeObsoleteImageProfiles() {
|
||||
var profiles = GetAllImageProfiles();
|
||||
try {
|
||||
if (profiles != null) {
|
||||
var validPaths = profiles.Select(profile => _storageProvider.Combine("_Profiles", this.GetNameHashCode(profile.Name)));
|
||||
foreach (var folder in _storageProvider.ListFolders("_Profiles").Select(f => f.GetPath())) {
|
||||
if (!validPaths.Any(folder.StartsWith)) {
|
||||
_storageProvider.DeleteFolder(folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Warning(ex, "Unable to purge obsolete image profiles");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteImageProfileFolder(string profileName) {
|
||||
var folder = _storageProvider.Combine("_Profiles", this.GetNameHashCode(profileName));
|
||||
_storageProvider.DeleteFolder(folder);
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,10 @@
|
||||
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
@Html.ValidationSummary()
|
||||
<div class="manage">@Html.ActionLink(T("Add a new Media Profile").ToString(), "Create", new { Area = "Contents", id = "ImageProfile", returnurl = HttpContext.Current.Request.RawUrl }, new { @class = "button primaryAction" })</div>
|
||||
<div class="manage">
|
||||
@Html.ActionLink(T("Purge Obsolete").ToString(), "PurgeObsolete", null, new { itemprop = "UnsafeUrl", @class = "button remove", data_unsafe_url = @T("Are you sure you wish to purge all obsolete profile images and force all dynamic profile images to be regenerated?").ToString() })
|
||||
@Html.ActionLink(T("Add a new Media Profile").ToString(), "Create", new { Area = "Contents", id = "ImageProfile", returnurl = HttpContext.Current.Request.RawUrl }, new { @class = "button primaryAction" })
|
||||
</div>
|
||||
|
||||
<fieldset class="bulk-actions">
|
||||
<label for="publishActions">@T("Actions:")</label>
|
||||
@ -56,6 +59,7 @@
|
||||
<td>
|
||||
@Html.ActionLink(T("Properties").ToString(), "Edit", new { Area = "Contents", id = entry.ImageProfileId, returnurl = HttpContext.Current.Request.RawUrl }) |
|
||||
@Html.ActionLink(T("Edit").ToString(), "Edit", new { id = entry.ImageProfileId }) |
|
||||
@Html.ActionLink(T("Purge").ToString(), "Purge", new { id = entry.ImageProfileId }, new { itemprop = "UnsafeUrl", data_unsafe_url = @T("Are you sure you wish to purge all images for this profile?").ToString() }) |
|
||||
@Html.ActionLink(T("Delete").ToString(), "Delete", new { id = entry.ImageProfileId }, new { itemprop = "RemoveUrl UnsafeUrl" })
|
||||
@*@Html.ActionLink(T("Preview").ToString(), "Preview", new { id = entry.ImageProfileId })*@
|
||||
</td>
|
||||
|
Loading…
Reference in New Issue
Block a user