Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#6981 Normalized image profile path hash and added profile purging #6984

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,19 @@ namespace Orchard.MediaProcessing.Controllers {
public class AdminController : Controller, IUpdateModel {
private readonly ISiteService _siteService;
private readonly IImageProfileService _profileService;
private readonly IImageProfileManager _imageProfileManager;
private readonly IImageProcessingManager _imageProcessingManager;

public AdminController(
IOrchardServices services,
IShapeFactory shapeFactory,
ISiteService siteService,
IImageProfileService profileService,
IImageProfileManager imageProfileManager,
IImageProcessingManager imageProcessingManager) {
_siteService = siteService;
_profileService = profileService;
_imageProfileManager = imageProfileManager;
_imageProcessingManager = imageProcessingManager;
Services = services;

Expand Down Expand Up @@ -185,6 +188,36 @@ public ActionResult Preview(int id) {
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 (_imageProfileManager.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 (_imageProfileManager.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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ public interface IImageProfileManager : IDependency {
string GetImageProfileUrl(string path, string profileName, FilterRecord customFilter);
string GetImageProfileUrl(string path, string profileName, FilterRecord customFilter, ContentItem contentItem);
string GetImageProfileUrl(string path, string profileName, ContentItem contentItem, params FilterRecord[] customFilters);

bool PurgeImageProfile(int id);
bool PurgeObsoleteImageProfiles();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
using Orchard.MediaProcessing.Models;
using Orchard.Tokens;
using Orchard.Utility.Extensions;
using Orchard.Environment.Configuration;
using Orchard.Caching;

namespace Orchard.MediaProcessing.Services {
public class ImageProfileManager : IImageProfileManager {
Expand All @@ -24,20 +26,26 @@ public class ImageProfileManager : IImageProfileManager {
private readonly IImageProcessingManager _processingManager;
private readonly IOrchardServices _services;
private readonly ITokenizer _tokenizer;
private readonly ISignals _signals;
private readonly IAppConfigurationAccessor _appConfigurationAccessor;

public ImageProfileManager(
IStorageProvider storageProvider,
IImageProcessingFileNameProvider fileNameProvider,
IImageProfileService profileService,
IImageProcessingManager processingManager,
IOrchardServices services,
ITokenizer tokenizer) {
ITokenizer tokenizer,
ISignals signals,
IAppConfigurationAccessor appConfigurationAccessor) {
_storageProvider = storageProvider;
_fileNameProvider = fileNameProvider;
_profileService = profileService;
_processingManager = processingManager;
_services = services;
_tokenizer = tokenizer;
_signals = signals;
_appConfigurationAccessor = appConfigurationAccessor;

Logger = NullLogger.Instance;
}
Expand Down Expand Up @@ -235,14 +243,66 @@ private bool TryGetImageLastUpdated(string path, out DateTime lastUpdated) {
return false;
}

public bool PurgeImageProfile(int id) {
var profile = _profileService.GetImageProfile(id);
try {
var folder = _storageProvider.Combine("_Profiles", GetHexHashCode(profile.Name));
_storageProvider.DeleteFolder(folder);
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 = _profileService.GetAllImageProfiles();
try {
if (profiles != null) {
var validPaths = profiles.Select(profile => _storageProvider.Combine("_Profiles", GetHexHashCode(profile.Name)));
foreach (var folder in _storageProvider.ListFolders("_Profiles").Select(f => f.GetPath())) {
if (!validPaths.Any(v => folder.StartsWith(v))) {
_storageProvider.DeleteFolder(folder);
}
}
}
return true;
}
catch (Exception ex) {
Logger.Warning(ex, "Unable to purge obsolete image profiles");
return false;
}
}

private string FormatProfilePath(string profileName, string path) {

var filenameWithExtension = Path.GetFileName(path) ?? "";
var fileLocation = path.Substring(0, path.Length - filenameWithExtension.Length);
var normalizedPath = ShouldNormalizePath() ? NormalizePath(path) : path;
var filenameWithExtension = Path.GetFileName(normalizedPath) ?? "";
var fileLocation = normalizedPath.Substring(0, normalizedPath.Length - filenameWithExtension.Length);

return _storageProvider.Combine(
_storageProvider.Combine(profileName.GetHashCode().ToString("x").ToLowerInvariant(), fileLocation.GetHashCode().ToString("x").ToLowerInvariant()),
_storageProvider.Combine(GetHexHashCode(profileName), GetHexHashCode(fileLocation)),
filenameWithExtension);
}

private string GetHexHashCode(string value) {
return value.GetHashCode().ToString("x").ToLowerInvariant();
}

private string NormalizePath(string path) {
//Slice at the protocol, if any. E.g. http:// or https://.
var index = path.IndexOf("//", StringComparison.OrdinalIgnoreCase);
//Slice at the first directory after the protocol or at 0 if no protocol specified.
index = path.IndexOf("/", index < 0 ? 0 : index + 2, StringComparison.OrdinalIgnoreCase);
//Return path from the first directory, replacing lcase 'media' (Azure container) with ucase 'Media' (filestorage).
return path.Substring(index < 1 ? 0 : index).Replace("media", "Media");
}

private bool ShouldNormalizePath() {
var normalizePath = _appConfigurationAccessor.GetConfiguration("Orchard.MediaProcessing.NormalizePath");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How often is it called? Shouldn't it be cached?

return string.IsNullOrEmpty(normalizePath) || normalizePath.Equals("true", StringComparison.OrdinalIgnoreCase);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down