diff --git a/src/Orchard.Web/Modules/Orchard.MediaProcessing/Orchard.MediaProcessing.csproj b/src/Orchard.Web/Modules/Orchard.MediaProcessing/Orchard.MediaProcessing.csproj index 874be9b72..5f19f7409 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaProcessing/Orchard.MediaProcessing.csproj +++ b/src/Orchard.Web/Modules/Orchard.MediaProcessing/Orchard.MediaProcessing.csproj @@ -114,8 +114,10 @@ + + diff --git a/src/Orchard.Web/Modules/Orchard.MediaProcessing/Services/IImageProfileManager.cs b/src/Orchard.Web/Modules/Orchard.MediaProcessing/Services/IImageProfileManager.cs new file mode 100644 index 000000000..d56f8ee7b --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaProcessing/Services/IImageProfileManager.cs @@ -0,0 +1,11 @@ +using Orchard.ContentManagement; +using Orchard.MediaProcessing.Models; + +namespace Orchard.MediaProcessing.Services { + public interface IImageProfileManager : IDependency { + string GetImageProfileUrl(string path, string profileName); + string GetImageProfileUrl(string path, string profileName, ContentItem contentItem); + string GetImageProfileUrl(string path, string profileName, FilterRecord customFilter); + string GetImageProfileUrl(string path, string profileName, FilterRecord customFilter, ContentItem contentItem); + } +} diff --git a/src/Orchard.Web/Modules/Orchard.MediaProcessing/Services/ImageProfileManager.cs b/src/Orchard.Web/Modules/Orchard.MediaProcessing/Services/ImageProfileManager.cs new file mode 100644 index 000000000..a7df74f06 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaProcessing/Services/ImageProfileManager.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Web; +using Orchard.ContentManagement; +using Orchard.FileSystems.Media; +using Orchard.Forms.Services; +using Orchard.Logging; +using Orchard.MediaProcessing.Descriptors.Filter; +using Orchard.MediaProcessing.Media; +using Orchard.MediaProcessing.Models; +using Orchard.Tokens; +using Orchard.Utility.Extensions; + +namespace Orchard.MediaProcessing.Services { + public class ImageProfileManager : IImageProfileManager { + private readonly IStorageProvider _storageProvider; + private readonly IImageProcessingFileNameProvider _fileNameProvider; + private readonly IImageProfileService _profileService; + private readonly IImageProcessingManager _processingManager; + private readonly IOrchardServices _services; + private readonly ITokenizer _tokenizer; + + public ImageProfileManager( + IStorageProvider storageProvider, + IImageProcessingFileNameProvider fileNameProvider, + IImageProfileService profileService, + IImageProcessingManager processingManager, + IOrchardServices services, + ITokenizer tokenizer) { + _storageProvider = storageProvider; + _fileNameProvider = fileNameProvider; + _profileService = profileService; + _processingManager = processingManager; + _services = services; + _tokenizer = tokenizer; + + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + public string GetImageProfileUrl(string path, string profileName) { + return GetImageProfileUrl(path, profileName, null, null); + } + + public string GetImageProfileUrl(string path, string profileName, ContentItem contentItem) { + return GetImageProfileUrl(path, profileName, null, contentItem); + } + + public string GetImageProfileUrl(string path, string profileName, FilterRecord customFilter) { + return GetImageProfileUrl(path, profileName, customFilter, null); + } + + public string GetImageProfileUrl(string path, string profileName, FilterRecord customFilter, ContentItem contentItem) { + + // try to load the processed filename from cache + var filePath = _fileNameProvider.GetFileName(profileName, path); + bool process = false; + + // if the filename is not cached, process it + if (string.IsNullOrEmpty(filePath)) { + Logger.Debug("FilePath is null, processing required, profile {0} for image {1}", profileName, path); + + 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)) { + var filePathLastUpdated = _storageProvider.GetFile(filePath).GetLastUpdated(); + + if (pathLastUpdated > filePathLastUpdated) { + Logger.Debug("Original file more recent, processing required, profile {0} for image {1}", profileName, path); + + process = true; + } + } + } + + // todo: regenerate the file if the profile is newer, by deleting the associated filename cache entries. + if (process) { + Logger.Debug("Processing profile {0} for image {1}", profileName, path); + + ImageProfilePart profilePart; + + if (customFilter == null) { + profilePart = _profileService.GetImageProfileByName(profileName); + if (profilePart == null) + return String.Empty; + } + else { + profilePart = _services.ContentManager.New("ImageProfile"); + profilePart.Name = profileName; + profilePart.Filters.Add(customFilter); + } + + using (var image = GetImage(path)) { + + var filterContext = new FilterContext { Media = image, FilePath = _storageProvider.Combine("_Profiles", _storageProvider.Combine(profileName, CreateDefaultFileName(path))) }; + + var tokens = new Dictionary(); + // if a content item is provided, use it while tokenizing + if (contentItem != null) { + tokens.Add("Content", contentItem); + } + + foreach (var filter in profilePart.Filters.OrderBy(f => f.Position)) { + var descriptor = _processingManager.DescribeFilters().SelectMany(x => x.Descriptors).FirstOrDefault(x => x.Category == filter.Category && x.Type == filter.Type); + if (descriptor == null) + continue; + + var tokenized = _tokenizer.Replace(filter.State, tokens); + filterContext.State = FormParametersHelper.ToDynamic(tokenized); + descriptor.Filter(filterContext); + } + + _fileNameProvider.UpdateFileName(profileName, path, filterContext.FilePath); + + if (!filterContext.Saved) { + _storageProvider.TryCreateFolder(_storageProvider.Combine("_Profiles", profilePart.Name)); + var newFile = _storageProvider.OpenOrCreate(filterContext.FilePath); + using (var imageStream = newFile.OpenWrite()) { + using (var sw = new BinaryWriter(imageStream)) { + if (filterContext.Media.CanSeek) { + filterContext.Media.Seek(0, SeekOrigin.Begin); + } + using (var sr = new BinaryReader(filterContext.Media)) { + int count; + var buffer = new byte[8192]; + while ((count = sr.Read(buffer, 0, buffer.Length)) != 0) { + sw.Write(buffer, 0, count); + } + } + } + } + } + + filterContext.Media.Dispose(); + filePath = filterContext.FilePath; + } + } + + // generate a timestamped url to force client caches to update if the file has changed + var publicUrl = _storageProvider.GetPublicUrl(filePath); + var timestamp = _storageProvider.GetFile(filePath).GetLastUpdated().Ticks; + return publicUrl + "?v=" + timestamp.ToString(CultureInfo.InvariantCulture); + } + + // TODO: Update this method once the storage provider has been updated + private Stream GetImage(string path) { + var request = _services.WorkContext.HttpContext.Request; + + var storagePath = _storageProvider.GetStoragePath(path); + if (storagePath != null) { + var file = _storageProvider.GetFile(storagePath); + return file.OpenRead(); + } + + // http://blob.storage-provider.net/my-image.jpg + if (Uri.IsWellFormedUriString(path, UriKind.Absolute)) { + return new WebClient().OpenRead(new Uri(path)); + } + + // ~/Media/Default/images/my-image.jpg + if (VirtualPathUtility.IsAppRelative(path)) { + return new WebClient().OpenRead(new Uri(request.Url, VirtualPathUtility.ToAbsolute(path))); + } + + return null; + } + + private bool TryGetImageLastUpdated(string path, out DateTime lastUpdated) { + var storagePath = _storageProvider.GetStoragePath(path); + if (storagePath != null) { + var file = _storageProvider.GetFile(storagePath); + lastUpdated = file.GetLastUpdated(); + return true; + } + + lastUpdated = DateTime.MinValue; + return false; + } + + private static string CreateDefaultFileName(string path) { + var extention = Path.GetExtension(path); + var newPath = Path.ChangeExtension(path, ""); + newPath = newPath.Replace(@"/", "_"); + return newPath.ToSafeName() + extention; + } + } +} diff --git a/src/Orchard.Web/Modules/Orchard.MediaProcessing/Shapes/MediaShapes.cs b/src/Orchard.Web/Modules/Orchard.MediaProcessing/Shapes/MediaShapes.cs index 01d220aba..16a36ebce 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaProcessing/Shapes/MediaShapes.cs +++ b/src/Orchard.Web/Modules/Orchard.MediaProcessing/Shapes/MediaShapes.cs @@ -2,46 +2,21 @@ using System.Collections.Generic; using System.Globalization; using System.IO; -using System.Linq; -using System.Net; -using System.Web; using Orchard.ContentManagement; using Orchard.DisplayManagement; using Orchard.Environment; -using Orchard.FileSystems.Media; using Orchard.Forms.Services; using Orchard.Logging; -using Orchard.MediaProcessing.Descriptors.Filter; -using Orchard.MediaProcessing.Media; using Orchard.MediaProcessing.Models; using Orchard.MediaProcessing.Services; -using Orchard.Tokens; -using Orchard.Utility.Extensions; namespace Orchard.MediaProcessing.Shapes { public class MediaShapes : IDependency { - private readonly Work _storageProvider; - private readonly Work _fileNameProvider; - private readonly Work _profileService; - private readonly Work _processingManager; - private readonly Work _services; - private readonly Work _tokenizer; + private readonly Work _imageProfileManager; - public MediaShapes( - Work storageProvider, - Work fileNameProvider, - Work profileService, - Work processingManager, - Work services, - Work tokenizer) { - _storageProvider = storageProvider; - _fileNameProvider = fileNameProvider; - _profileService = profileService; - _processingManager = processingManager; - _services = services; - _tokenizer = tokenizer; - Logger = NullLogger.Instance; + public MediaShapes(Work imageProfileManager) { + _imageProfileManager = imageProfileManager; } public ILogger Logger { get; set; } @@ -76,155 +51,12 @@ namespace Orchard.MediaProcessing.Shapes { public void MediaUrl(dynamic Shape, dynamic Display, TextWriter Output, string Profile, string Path, ContentItem ContentItem, FilterRecord CustomFilter) { try { Shape.IgnoreShapeTracer = true; - var services = new Lazy(() => _services.Value); - var storageProvider = new Lazy(() => _storageProvider.Value); - - // try to load the processed filename from cache - var filePath = _fileNameProvider.Value.GetFileName(Profile, Path); - bool process = false; - - // if the filename is not cached, process it - if (string.IsNullOrEmpty(filePath)) { - Logger.Debug("FilePath is null, processing required, profile {0} for image {1}", Profile, Path); - - process = true; - } - - // the processd file doesn't exist anymore, process it - else if (!storageProvider.Value.FileExists(filePath)) { - Logger.Debug("Processed file no longer exists, processing required, profile {0} for image {1}", Profile, Path); - - process = true; - } - - // if the original file is more recent, process it - else { - DateTime pathLastUpdated; - if (TryGetImageLastUpdated(Path, out pathLastUpdated)) { - var filePathLastUpdated = storageProvider.Value.GetFile(filePath).GetLastUpdated(); - - if (pathLastUpdated > filePathLastUpdated) - { - Logger.Debug("Original file more recent, processing required, profile {0} for image {1}", Profile, Path); - - process = true; - } - } - } - - // todo: regenerate the file if the profile is newer, by deleting the associated filename cache entries. - if (process) { - Logger.Debug("Processing profile {0} for image {1}", Profile, Path); - - ImageProfilePart profilePart; - - if (CustomFilter == null) { - profilePart = _profileService.Value.GetImageProfileByName(Profile); - if (profilePart == null) - return; - } - else { - profilePart = services.Value.ContentManager.New("ImageProfile"); - profilePart.Name = Profile; - profilePart.Filters.Add(CustomFilter); - } - - using (var image = GetImage(Path)) { - - var filterContext = new FilterContext { Media = image, FilePath = storageProvider.Value.Combine("_Profiles", storageProvider.Value.Combine(Profile, CreateDefaultFileName(Path))) }; - - var tokens = new Dictionary(); - // if a content item is provided, use it while tokenizing - if (ContentItem != null) { - tokens.Add("Content", ContentItem); - } - - foreach (var filter in profilePart.Filters.OrderBy(f => f.Position)) { - var descriptor = _processingManager.Value.DescribeFilters().SelectMany(x => x.Descriptors).FirstOrDefault(x => x.Category == filter.Category && x.Type == filter.Type); - if (descriptor == null) - continue; - - var tokenized = _tokenizer.Value.Replace(filter.State, tokens); - filterContext.State = FormParametersHelper.ToDynamic(tokenized); - descriptor.Filter(filterContext); - } - - _fileNameProvider.Value.UpdateFileName(Profile, Path, filterContext.FilePath); - - if (!filterContext.Saved) { - storageProvider.Value.TryCreateFolder(storageProvider.Value.Combine("_Profiles", profilePart.Name)); - var newFile = storageProvider.Value.OpenOrCreate(filterContext.FilePath); - using (var imageStream = newFile.OpenWrite()) { - using (var sw = new BinaryWriter(imageStream)) { - if (filterContext.Media.CanSeek) { - filterContext.Media.Seek(0, SeekOrigin.Begin); - } - using (var sr = new BinaryReader(filterContext.Media)) { - int count; - var buffer = new byte[8192]; - while ((count = sr.Read(buffer, 0, buffer.Length)) != 0) { - sw.Write(buffer, 0, count); - } - } - } - } - } - - filterContext.Media.Dispose(); - filePath = filterContext.FilePath; - } - } - - // generate a timestamped url to force client caches to update if the file has changed - var publicUrl = storageProvider.Value.GetPublicUrl(filePath); - var timestamp = storageProvider.Value.GetFile(filePath).GetLastUpdated().Ticks; - Output.Write(publicUrl + "?v=" + timestamp.ToString(CultureInfo.InvariantCulture)); + Output.Write(_imageProfileManager.Value.GetImageProfileUrl(Path, Profile, CustomFilter, ContentItem)); } catch (Exception ex) { Logger.Error(ex, "An error occured while rendering shape {0} for image {1}", Profile, Path); } } - // TODO: Update this method once the storage provider has been updated - private Stream GetImage(string path) { - var request = _services.Value.WorkContext.HttpContext.Request; - - var storagePath = _storageProvider.Value.GetStoragePath(path); - if (storagePath != null) { - var file = _storageProvider.Value.GetFile(storagePath); - return file.OpenRead(); - } - - // http://blob.storage-provider.net/my-image.jpg - if (Uri.IsWellFormedUriString(path, UriKind.Absolute)) { - return new WebClient().OpenRead(new Uri(path)); - } - - // ~/Media/Default/images/my-image.jpg - if (VirtualPathUtility.IsAppRelative(path)) { - return new WebClient().OpenRead(new Uri(request.Url, VirtualPathUtility.ToAbsolute(path))); - } - - return null; - } - - private bool TryGetImageLastUpdated(string path, out DateTime lastUpdated) { - var storagePath = _storageProvider.Value.GetStoragePath(path); - if (storagePath != null) { - var file = _storageProvider.Value.GetFile(storagePath); - lastUpdated = file.GetLastUpdated(); - return true; - } - - lastUpdated = DateTime.MinValue; - return false; - } - - private static string CreateDefaultFileName(string path) { - var extention = Path.GetExtension(path); - var newPath = Path.ChangeExtension(path, ""); - newPath = newPath.Replace(@"/", "_"); - return newPath.ToSafeName() + extention; - } } } \ No newline at end of file