mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 11:44:58 +08:00
Importing Media Processing module
--HG-- branch : 1.x extra : rebase_source : 52bc8a9f38bce13bb52e4bad5df730985924e76b
This commit is contained in:
17
src/Orchard.Web/Modules/Orchard.MediaProcessing/AdminMenu.cs
Normal file
17
src/Orchard.Web/Modules/Orchard.MediaProcessing/AdminMenu.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Orchard.Localization;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.MediaProcessing {
|
||||
public class AdminMenu : INavigationProvider {
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public string MenuName {
|
||||
get { return "admin"; }
|
||||
}
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add(T("Media"), "6", item => item.Add(T("Profiles"), "5", i => i.Action("Index", "Admin", new {area = "Orchard.MediaProcessing"}).Permission(StandardPermissions.SiteOwner)));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,196 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Core.Contents.Controllers;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.MediaProcessing.Descriptors.Filter;
|
||||
using Orchard.MediaProcessing.Models;
|
||||
using Orchard.MediaProcessing.Services;
|
||||
using Orchard.MediaProcessing.ViewModels;
|
||||
using Orchard.Security;
|
||||
using Orchard.Settings;
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.UI.Notify;
|
||||
|
||||
namespace Orchard.MediaProcessing.Controllers {
|
||||
[ValidateInput(false)]
|
||||
public class AdminController : Controller, IUpdateModel {
|
||||
private readonly ISiteService _siteService;
|
||||
private readonly IImageProfileService _profileService;
|
||||
private readonly IImageProcessingManager _imageProcessingManager;
|
||||
|
||||
public AdminController(
|
||||
IOrchardServices services,
|
||||
IShapeFactory shapeFactory,
|
||||
ISiteService siteService,
|
||||
IImageProfileService profileService,
|
||||
IImageProcessingManager imageProcessingManager) {
|
||||
_siteService = siteService;
|
||||
_profileService = profileService;
|
||||
_imageProcessingManager = imageProcessingManager;
|
||||
Services = services;
|
||||
|
||||
T = NullLocalizer.Instance;
|
||||
Shape = shapeFactory;
|
||||
}
|
||||
|
||||
private dynamic Shape { get; set; }
|
||||
public IOrchardServices Services { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ActionResult Index(AdminIndexOptions options, PagerParameters pagerParameters) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to list media profiles")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
|
||||
|
||||
// default options
|
||||
if (options == null)
|
||||
options = new AdminIndexOptions();
|
||||
|
||||
var profiles = Services.ContentManager.Query("ImageProfile");
|
||||
|
||||
var pagerShape = Shape.Pager(pager).TotalItemCount(profiles.Count());
|
||||
|
||||
profiles = profiles.Join<ImageProfilePartRecord>().OrderBy(u => u.Name);
|
||||
|
||||
var results = profiles
|
||||
.Slice(pager.GetStartIndex(), pager.PageSize)
|
||||
.ToList();
|
||||
|
||||
var model = new AdminIndexViewModel {
|
||||
ImageProfiles = results.Select(x => new ImageProfileEntry {
|
||||
ImageProfile = x.As<ImageProfilePart>().Record,
|
||||
ImageProfileId = x.Id,
|
||||
Name = x.As<ImageProfilePart>().Name
|
||||
}).ToList(),
|
||||
Options = options,
|
||||
Pager = pagerShape
|
||||
};
|
||||
|
||||
// maintain previous route data when generating page links
|
||||
var routeData = new RouteData();
|
||||
routeData.Values.Add("Options.Filter", options.Filter);
|
||||
|
||||
pagerShape.RouteData(routeData);
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[FormValueRequired("submit.BulkEdit")]
|
||||
public ActionResult Index(FormCollection input) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage media profiles")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var viewModel = new AdminIndexViewModel {ImageProfiles = new List<ImageProfileEntry>(), Options = new AdminIndexOptions()};
|
||||
UpdateModel(viewModel);
|
||||
|
||||
var checkedItems = viewModel.ImageProfiles.Where(c => c.IsChecked);
|
||||
|
||||
switch (viewModel.Options.BulkAction) {
|
||||
case ImageProfilesBulkAction.None:
|
||||
break;
|
||||
case ImageProfilesBulkAction.Delete:
|
||||
foreach (var checkedItem in checkedItems) {
|
||||
_profileService.DeleteImageProfile(checkedItem.ImageProfileId);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
public ActionResult Edit(int id) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to edit media profiles")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var profile = _profileService.GetImageProfile(id);
|
||||
var viewModel = new AdminEditViewModel {
|
||||
Id = profile.Id,
|
||||
Name = profile.Name
|
||||
};
|
||||
|
||||
var filterEntries = new List<FilterEntry>();
|
||||
var allFilters = _imageProcessingManager.DescribeFilters().SelectMany(x => x.Descriptors).ToList();
|
||||
|
||||
foreach (var filter in profile.Filters.OrderBy(f => f.Position)) {
|
||||
var category = filter.Category;
|
||||
var type = filter.Type;
|
||||
|
||||
var f = allFilters.FirstOrDefault(x => category == x.Category && type == x.Type);
|
||||
if (f != null) {
|
||||
filterEntries.Add(
|
||||
new FilterEntry {
|
||||
Category = f.Category,
|
||||
Type = f.Type,
|
||||
FilterRecordId = filter.Id,
|
||||
DisplayText = String.IsNullOrWhiteSpace(filter.Description) ? f.Display(new FilterContext {State = FormParametersHelper.ToDynamic(filter.State)}).Text : filter.Description
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.Filters = filterEntries;
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Delete(int id) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage media profiles")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var profile = _profileService.GetImageProfile(id);
|
||||
|
||||
if (profile == null) {
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
Services.ContentManager.Remove(profile.ContentItem);
|
||||
Services.Notifier.Information(T("Image Profile {0} deleted", profile.Name));
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
public ActionResult Move(string direction, int id, int filterId) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage media profiles")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
switch (direction) {
|
||||
case "up":
|
||||
_profileService.MoveUp(filterId);
|
||||
break;
|
||||
case "down":
|
||||
_profileService.MoveDown(filterId);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("direction");
|
||||
}
|
||||
|
||||
return RedirectToAction("Edit", new {id});
|
||||
}
|
||||
|
||||
public ActionResult Preview(int id) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage media profiles")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
|
||||
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
|
||||
}
|
||||
|
||||
public void AddModelError(string key, LocalizedString errorMessage) {
|
||||
ModelState.AddModelError(key, errorMessage.ToString());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.Caching;
|
||||
using Orchard.Data;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.FileSystems.Media;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.MediaProcessing.Models;
|
||||
using Orchard.MediaProcessing.Services;
|
||||
using Orchard.MediaProcessing.ViewModels;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Admin;
|
||||
using Orchard.UI.Notify;
|
||||
using FormParametersHelper = Orchard.Forms.Services.FormParametersHelper;
|
||||
|
||||
namespace Orchard.MediaProcessing.Controllers {
|
||||
[ValidateInput(false), Admin]
|
||||
public class FilterController : Controller {
|
||||
public FilterController(
|
||||
IOrchardServices services,
|
||||
IFormManager formManager,
|
||||
IShapeFactory shapeFactory,
|
||||
IImageProcessingManager processingManager,
|
||||
IRepository<FilterRecord> repository,
|
||||
IImageProfileService profileService,
|
||||
ISignals signals) {
|
||||
Services = services;
|
||||
_formManager = formManager;
|
||||
_processingManager = processingManager;
|
||||
_filterRepository = repository;
|
||||
_profileService = profileService;
|
||||
_signals = signals;
|
||||
Shape = shapeFactory;
|
||||
}
|
||||
|
||||
public IOrchardServices Services { get; set; }
|
||||
private readonly IFormManager _formManager;
|
||||
private readonly IImageProcessingManager _processingManager;
|
||||
private readonly IRepository<FilterRecord> _filterRepository;
|
||||
private readonly IImageProfileService _profileService;
|
||||
private readonly ISignals _signals;
|
||||
private readonly IStorageProvider _storageProvider;
|
||||
public Localizer T { get; set; }
|
||||
public dynamic Shape { get; set; }
|
||||
|
||||
public ActionResult Add(int id) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage image profiles")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var viewModel = new FilterAddViewModel {Id = id, Filters = _processingManager.DescribeFilters()};
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
public ActionResult Delete(int id, int filterId) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage image profiles")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var filter = _filterRepository.Get(filterId);
|
||||
if (filter == null) {
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
_filterRepository.Delete(filter);
|
||||
Services.Notifier.Information(T("Filter deleted"));
|
||||
|
||||
return RedirectToAction("Edit", "Admin", new {id});
|
||||
}
|
||||
|
||||
public ActionResult Edit(int id, string category, string type, int filterId = -1) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage image profiles")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var filter = _processingManager.DescribeFilters().SelectMany(x => x.Descriptors).FirstOrDefault(x => x.Category == category && x.Type == type);
|
||||
|
||||
if (filter == null) {
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
// build the form, and let external components alter it
|
||||
var form = filter.Form == null ? null : _formManager.Build(filter.Form);
|
||||
|
||||
string description = "";
|
||||
|
||||
// bind form with existing values).
|
||||
if (filterId != -1) {
|
||||
var profile = _profileService.GetImageProfile(id);
|
||||
var filterRecord = profile.Filters.FirstOrDefault(f => f.Id == filterId);
|
||||
if (filterRecord != null) {
|
||||
description = filterRecord.Description;
|
||||
var parameters = FormParametersHelper.FromString(filterRecord.State);
|
||||
_formManager.Bind(form, new DictionaryValueProvider<string>(parameters, CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
var viewModel = new FilterEditViewModel {Id = id, Description = description, Filter = filter, Form = form};
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("Edit")]
|
||||
public ActionResult EditPost(int id, string category, string type, [DefaultValue(-1)] int filterId, FormCollection formCollection) {
|
||||
var profile = _profileService.GetImageProfile(id);
|
||||
|
||||
var filter = _processingManager.DescribeFilters().SelectMany(x => x.Descriptors).FirstOrDefault(x => x.Category == category && x.Type == type);
|
||||
|
||||
var model = new FilterEditViewModel();
|
||||
TryUpdateModel(model);
|
||||
|
||||
// validating form values
|
||||
_formManager.Validate(new ValidatingContext {FormName = filter.Form, ModelState = ModelState, ValueProvider = ValueProvider});
|
||||
|
||||
if (ModelState.IsValid) {
|
||||
var filterRecord = profile.Filters.FirstOrDefault(f => f.Id == filterId);
|
||||
|
||||
// add new filter record if it's a newly created filter
|
||||
if (filterRecord == null) {
|
||||
filterRecord = new FilterRecord {
|
||||
Category = category,
|
||||
Type = type,
|
||||
Position = profile.Filters.Count
|
||||
};
|
||||
profile.Filters.Add(filterRecord);
|
||||
}
|
||||
|
||||
var dictionary = formCollection.AllKeys.ToDictionary(key => key, formCollection.Get);
|
||||
|
||||
// save form parameters
|
||||
filterRecord.State = FormParametersHelper.ToString(dictionary);
|
||||
filterRecord.Description = model.Description;
|
||||
|
||||
// set profile as updated
|
||||
profile.ModifiedUtc = DateTime.UtcNow;
|
||||
profile.FileNames.Clear();
|
||||
_signals.Trigger("MediaProcessing_" + profile.Name + "_Saved");
|
||||
|
||||
return RedirectToAction("Edit", "Admin", new {id});
|
||||
}
|
||||
|
||||
// model is invalid, display it again
|
||||
var form = _formManager.Build(filter.Form);
|
||||
|
||||
_formManager.Bind(form, formCollection);
|
||||
var viewModel = new FilterEditViewModel {Id = id, Description = model.Description, Filter = filter, Form = form};
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.MediaProcessing.Descriptors.Filter {
|
||||
public class DescribeFilterContext {
|
||||
private readonly Dictionary<string, DescribeFilterFor> _describes = new Dictionary<string, DescribeFilterFor>();
|
||||
|
||||
public IEnumerable<TypeDescriptor<FilterDescriptor>> Describe() {
|
||||
return _describes.Select(kp => new TypeDescriptor<FilterDescriptor> {
|
||||
Category = kp.Key,
|
||||
Name = kp.Value.Name,
|
||||
Description = kp.Value.Description,
|
||||
Descriptors = kp.Value.Types
|
||||
});
|
||||
}
|
||||
|
||||
public DescribeFilterFor For(string category) {
|
||||
return For(category, null, null);
|
||||
}
|
||||
|
||||
public DescribeFilterFor For(string category, LocalizedString name, LocalizedString description) {
|
||||
DescribeFilterFor describeFor;
|
||||
if (!_describes.TryGetValue(category, out describeFor)) {
|
||||
describeFor = new DescribeFilterFor(category, name, description);
|
||||
_describes[category] = describeFor;
|
||||
}
|
||||
return describeFor;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.MediaProcessing.Descriptors.Filter {
|
||||
public class DescribeFilterFor {
|
||||
private readonly string _category;
|
||||
|
||||
public DescribeFilterFor(string category, LocalizedString name, LocalizedString description) {
|
||||
Types = new List<FilterDescriptor>();
|
||||
_category = category;
|
||||
Name = name;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public LocalizedString Name { get; private set; }
|
||||
public LocalizedString Description { get; private set; }
|
||||
public List<FilterDescriptor> Types { get; private set; }
|
||||
|
||||
public DescribeFilterFor Element(string type, LocalizedString name, LocalizedString description, Action<FilterContext> filter, Func<FilterContext, LocalizedString> display, string form = null) {
|
||||
Types.Add(new FilterDescriptor { Type = type, Name = name, Description = description, Category = _category, Filter = filter, Display = display, Form = form });
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
namespace Orchard.MediaProcessing.Descriptors.Filter {
|
||||
public class FilterContext {
|
||||
public dynamic State { get; set; }
|
||||
public Image Image { get; set; }
|
||||
public ImageFormat ImageFormat { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public bool Saved { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.MediaProcessing.Descriptors.Filter {
|
||||
public class FilterDescriptor {
|
||||
public string Category { get; set; }
|
||||
public string Type { get; set; }
|
||||
public LocalizedString Name { get; set; }
|
||||
public LocalizedString Description { get; set; }
|
||||
public Action<FilterContext> Filter { get; set; }
|
||||
public string Form { get; set; }
|
||||
public Func<FilterContext, LocalizedString> Display { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.MediaProcessing.Descriptors {
|
||||
public class TypeDescriptor<T> {
|
||||
public string Category { get; set; }
|
||||
public LocalizedString Name { get; set; }
|
||||
public LocalizedString Description { get; set; }
|
||||
public IEnumerable<T> Descriptors { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Drivers;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Localization;
|
||||
using Orchard.MediaProcessing.Models;
|
||||
using Orchard.MediaProcessing.Services;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
namespace Orchard.MediaProcessing.Drivers {
|
||||
|
||||
public class ImageProfilePartDriver : ContentPartDriver<ImageProfilePart> {
|
||||
private readonly IImageProfileService _imageProfileService;
|
||||
|
||||
private const string TemplateName = "Parts.MediaProcessing.ImageProfilePart";
|
||||
|
||||
public ImageProfilePartDriver(IImageProfileService imageProfileService) {
|
||||
_imageProfileService = imageProfileService;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
protected override string Prefix {
|
||||
get { return "MediaProcessing"; }
|
||||
}
|
||||
|
||||
protected override DriverResult Display(ImageProfilePart part, string displayType, dynamic shapeHelper) {
|
||||
return ContentShape("Parts_MediaProcessing_ImageProfile",
|
||||
() => shapeHelper.Parts_MediaProcessing_ImageProfile(Name: part.Name));
|
||||
}
|
||||
|
||||
protected override DriverResult Editor(ImageProfilePart part, dynamic shapeHelper) {
|
||||
return ContentShape("Parts_MediaProcessing_ImageProfile_Edit",
|
||||
() => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: part, Prefix: Prefix));
|
||||
}
|
||||
|
||||
protected override DriverResult Editor(ImageProfilePart part, IUpdateModel updater, dynamic shapeHelper) {
|
||||
var currentName = part.Name;
|
||||
updater.TryUpdateModel(part, Prefix, null, null);
|
||||
if (String.IsNullOrWhiteSpace(part.Name)) {
|
||||
updater.AddModelError("Name", T("The Name can't be empty."));
|
||||
}
|
||||
if (currentName != part.Name && _imageProfileService.GetImageProfileByName(part.Name) != null) {
|
||||
updater.AddModelError("Name", T("A profile with the same Name already exists."));
|
||||
}
|
||||
if (part.Name != part.Name.ToSafeName()) {
|
||||
updater.AddModelError("Name", T("The Name can only contain letters and numbers without spaces"));
|
||||
}
|
||||
|
||||
return Editor(part, shapeHelper);
|
||||
}
|
||||
|
||||
protected override void Exporting(ImageProfilePart part, ExportContentContext context) {
|
||||
var element = context.Element(part.PartDefinition.Name);
|
||||
element.Add(
|
||||
new XElement("Filters",
|
||||
part.Filters.Select(filter =>
|
||||
new XElement("Filter",
|
||||
new XAttribute("Description", filter.Description ?? ""),
|
||||
new XAttribute("Category", filter.Category ?? ""),
|
||||
new XAttribute("Type", filter.Type ?? ""),
|
||||
new XAttribute("Position", filter.Position),
|
||||
new XAttribute("State", filter.State ?? "")
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected override void Importing(ImageProfilePart part, ImportContentContext context) {
|
||||
var element = context.Data.Element(part.PartDefinition.Name);
|
||||
|
||||
part.Record.Filters = element.Element("Filters").Elements("Filter").Select(filter =>
|
||||
|
||||
new FilterRecord {
|
||||
Description = filter.Attribute("Description").Value,
|
||||
Category = filter.Attribute("Category").Value,
|
||||
Type = filter.Attribute("Type").Value,
|
||||
Position = Convert.ToInt32(filter.Attribute("Position").Value),
|
||||
State = filter.Attribute("State").Value
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Data;
|
||||
using Orchard.MediaProcessing.Models;
|
||||
|
||||
namespace Orchard.MediaProcessing.Handlers {
|
||||
public class ImageProfilePartHandler : ContentHandler {
|
||||
public ImageProfilePartHandler(IRepository<ImageProfilePartRecord> repository) {
|
||||
Filters.Add(StorageFilter.For(repository));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
using Orchard.FileSystems.Media;
|
||||
|
||||
namespace Orchard.MediaProcessing.Media {
|
||||
public static class StorageProviderExtensions {
|
||||
public static bool FileExists(this IStorageProvider storageProvider, string path) {
|
||||
try {
|
||||
storageProvider.GetFile(path);
|
||||
return true;
|
||||
}
|
||||
catch {}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void TryDeleteFolder(this IStorageProvider storageProvider, string path) {
|
||||
try {
|
||||
storageProvider.DeleteFolder(path);
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
|
||||
public static IStorageFile OpenOrCreate(this IStorageProvider storageProvider, string path) {
|
||||
try {
|
||||
return storageProvider.CreateFile(path);
|
||||
}
|
||||
catch {}
|
||||
return storageProvider.GetFile(path);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.Data.Migration;
|
||||
|
||||
namespace Orchard.MediaProcessing {
|
||||
public class Migrations : DataMigrationImpl {
|
||||
public int Create() {
|
||||
SchemaBuilder.CreateTable("ImageProfilePartRecord",
|
||||
table => table
|
||||
.Column<string>("Name", c => c.WithLength(255))
|
||||
.ContentPartRecord()
|
||||
);
|
||||
|
||||
ContentDefinitionManager.AlterTypeDefinition("ImageProfile",
|
||||
cfg => cfg
|
||||
.WithPart("ImageProfilePart")
|
||||
.WithPart("CommonPart", p => p.WithSetting("OwnerEditorSettings.ShowOwnerEditor", "false"))
|
||||
.WithPart("IdentityPart")
|
||||
);
|
||||
|
||||
SchemaBuilder.CreateTable("FilterRecord",
|
||||
table => table
|
||||
.Column<int>("Id", c => c.PrimaryKey().Identity())
|
||||
.Column<string>("Category", c => c.WithLength(64))
|
||||
.Column<string>("Type", c => c.WithLength(64))
|
||||
.Column<string>("Description", c => c.WithLength(255))
|
||||
.Column<string>("State", c => c.Unlimited())
|
||||
.Column<int>("Position")
|
||||
.Column<int>("ImageProfilePartRecord_id")
|
||||
);
|
||||
|
||||
SchemaBuilder.CreateTable("FileNameRecord",
|
||||
table => table
|
||||
.Column<int>("Id", c => c.PrimaryKey().Identity())
|
||||
// Note: path and file name set to unlimited length, should have a sensible length
|
||||
.Column<string>("Path", c => c.Unlimited())
|
||||
.Column<string>("FileName", c => c.Unlimited())
|
||||
.Column<int>("ImageProfilePartRecord_id")
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
namespace Orchard.MediaProcessing.Models {
|
||||
public class FileNameRecord {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual string Path { get; set; }
|
||||
public virtual string FileName { get; set; }
|
||||
|
||||
// Parent property
|
||||
public virtual ImageProfilePartRecord ImageProfilePartRecord { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
namespace Orchard.MediaProcessing.Models {
|
||||
public class FilterRecord {
|
||||
public virtual int Id { get; set; }
|
||||
|
||||
public virtual string Description { get; set; }
|
||||
public virtual string Category { get; set; }
|
||||
public virtual string Type { get; set; }
|
||||
public virtual int Position { get; set; }
|
||||
public virtual string State { get; set; }
|
||||
|
||||
// Parent property
|
||||
public virtual ImageProfilePartRecord ImageProfilePartRecord { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
|
||||
namespace Orchard.MediaProcessing.Models {
|
||||
public class ImageProfilePart : ContentPart<ImageProfilePartRecord> {
|
||||
|
||||
public string Name {
|
||||
get { return Record.Name; }
|
||||
set { Record.Name = value; }
|
||||
}
|
||||
|
||||
public DateTime? ModifiedUtc {
|
||||
get { return this.As<ICommonPart>().ModifiedUtc; }
|
||||
set { this.As<ICommonPart>().ModifiedUtc = value; }
|
||||
}
|
||||
|
||||
public IList<FilterRecord> Filters {
|
||||
get { return Record.Filters; }
|
||||
}
|
||||
|
||||
public IList<FileNameRecord> FileNames {
|
||||
get { return Record.FileNames; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using Orchard.Data.Conventions;
|
||||
|
||||
namespace Orchard.MediaProcessing.Models {
|
||||
public class ImageProfilePartRecord : ContentPartRecord {
|
||||
public ImageProfilePartRecord() {
|
||||
Filters = new List<FilterRecord>();
|
||||
FileNames = new List<FileNameRecord>();
|
||||
}
|
||||
|
||||
public virtual string Name { get; set; }
|
||||
|
||||
[CascadeAllDeleteOrphan, Aggregate]
|
||||
[XmlArray("FilterRecords")]
|
||||
public virtual IList<FilterRecord> Filters { get; set; }
|
||||
|
||||
[CascadeAllDeleteOrphan, Aggregate]
|
||||
[XmlArray("FileNameRecords")]
|
||||
public virtual IList<FileNameRecord> FileNames { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
Name: Media Processing
|
||||
AntiForgery: enabled
|
||||
Author: The Orchard Team
|
||||
Website: http://orchardproject.net
|
||||
Version: 1.0
|
||||
OrchardVersion: 1.0
|
||||
Description: Module for processing Media e.g. image resizing
|
||||
Category: Media
|
||||
Dependencies: Orchard.Media, Orchard.Forms
|
@@ -0,0 +1,193 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{08191FCD-7258-4F19-95FB-AEC3DE77B2EB}</ProjectGuid>
|
||||
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Orchard.MediaProcessing</RootNamespace>
|
||||
<AssemblyName>Orchard.MediaProcessing</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<MvcBuildViews>false</MvcBuildViews>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<OldToolsVersion>4.0</OldToolsVersion>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<TargetFrameworkProfile />
|
||||
<UseIISExpress>false</UseIISExpress>
|
||||
<IISExpressSSLPort />
|
||||
<IISExpressAnonymousAuthentication />
|
||||
<IISExpressWindowsAuthentication />
|
||||
<IISExpressUseClassicPipelineMode />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Web.DynamicData" />
|
||||
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.Abstractions" />
|
||||
<Reference Include="System.Web.Routing" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Web.config" />
|
||||
<Content Include="Views\Web.config" />
|
||||
<Content Include="Scripts\Web.config" />
|
||||
<Content Include="Styles\Web.config" />
|
||||
<Content Include="Properties\AssemblyInfo.cs" />
|
||||
<Content Include="Module.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
|
||||
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
|
||||
<Name>Orchard.Framework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Core\Orchard.Core.csproj">
|
||||
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
|
||||
<Name>Orchard.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Forms\Orchard.Forms.csproj">
|
||||
<Project>{642A49D7-8752-4177-80D6-BFBBCFAD3DE0}</Project>
|
||||
<Name>Orchard.Forms</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Media\Orchard.Media.csproj">
|
||||
<Project>{D9A7B330-CD22-4DA1-A95A-8DE1982AD8EB}</Project>
|
||||
<Name>Orchard.Media</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Controllers\FilterController.cs" />
|
||||
<Compile Include="Descriptors\Filter\DescribeFilterContext.cs" />
|
||||
<Compile Include="Descriptors\Filter\DescribeFilterFor.cs" />
|
||||
<Compile Include="Descriptors\Filter\FilterContext.cs" />
|
||||
<Compile Include="Descriptors\Filter\FilterDescriptor.cs" />
|
||||
<Compile Include="Descriptors\TypeDescriptor.cs" />
|
||||
<Compile Include="Drivers\ImageProfilePartDriver.cs" />
|
||||
<Compile Include="Handlers\ImageProfilePartHandler.cs" />
|
||||
<Compile Include="Media\StorageProviderExtensions.cs" />
|
||||
<Compile Include="Migrations.cs" />
|
||||
<Compile Include="Models\FileNameRecord.cs" />
|
||||
<Compile Include="Models\FilterRecord.cs" />
|
||||
<Compile Include="Models\ImageProfilePart.cs" />
|
||||
<Compile Include="Models\ImageProfilePartRecord.cs" />
|
||||
<Compile Include="Providers\Filters\CropFilter.cs" />
|
||||
<Compile Include="Providers\Filters\ImageFormatFilter.cs" />
|
||||
<Compile Include="Providers\Filters\ResizeFilter.cs" />
|
||||
<Compile Include="Services\IImageFilterProvider.cs" />
|
||||
<Compile Include="Services\IImageProcessingFileNameProvider.cs" />
|
||||
<Compile Include="Services\IImageProfileService.cs" />
|
||||
<Compile Include="Services\IImageProcessingManager.cs" />
|
||||
<Compile Include="Services\ImageProcessingFileNameProvider.cs" />
|
||||
<Compile Include="Services\ImageProfileService.cs" />
|
||||
<Compile Include="Services\ImageProcessingManager.cs" />
|
||||
<Compile Include="Shapes\MediaShapes.cs" />
|
||||
<Compile Include="ViewModels\AdminCreateViewModel.cs" />
|
||||
<Compile Include="ViewModels\AdminEditViewModel.cs" />
|
||||
<Compile Include="ViewModels\AdminIndexViewModel.cs" />
|
||||
<Compile Include="ViewModels\FilterAddViewModel.cs" />
|
||||
<Compile Include="ViewModels\FilterEditViewModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\Edit.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\Index.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\Preview.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Filter\Add.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Filter\Edit.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\EditorTemplates\Parts.MediaProcessing.ImageProfilePart.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Placement.info" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target> -->
|
||||
<Target Name="AfterBuild" DependsOnTargets="AfterBuildCompiler">
|
||||
<PropertyGroup>
|
||||
<AreasManifestDir>$(ProjectDir)\..\Manifests</AreasManifestDir>
|
||||
</PropertyGroup>
|
||||
<!-- If this is an area child project, uncomment the following line:
|
||||
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Child" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
|
||||
-->
|
||||
<!-- If this is an area parent project, uncomment the following lines:
|
||||
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Parent" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
|
||||
<CopyAreaManifests ManifestPath="$(AreasManifestDir)" CrossCopy="false" RenameViews="true" />
|
||||
-->
|
||||
</Target>
|
||||
<Target Name="AfterBuildCompiler" Condition="'$(MvcBuildViews)'=='true'">
|
||||
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />
|
||||
</Target>
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>45979</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>True</UseCustomServer>
|
||||
<CustomServerUrl>http://orchard.codeplex.com</CustomServerUrl>
|
||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
@@ -0,0 +1,9 @@
|
||||
<Placement>
|
||||
<Place Parts_MediaProcessing_ImageProfile_Edit="Content:before.5"/>
|
||||
<Match DisplayType="Detail">
|
||||
<Place Parts_MediaProcessing_ImageProfile="Header:5"/>
|
||||
</Match>
|
||||
<Match DisplayType="Summary">
|
||||
<Place Parts_MediaProcessing_ImageProfile="Header:5"/>
|
||||
</Match>
|
||||
</Placement>
|
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Orchard.ImageProcessing")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyProduct("Orchard")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("8ac0ba94-3401-4a83-8d40-5a3aa569d4ff")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
|
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.MediaProcessing.Descriptors.Filter;
|
||||
using Orchard.MediaProcessing.Services;
|
||||
|
||||
namespace Orchard.MediaProcessing.Providers.Filters {
|
||||
public class CropFilter : IImageFilterProvider {
|
||||
public CropFilter() {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public void Describe(DescribeFilterContext describe) {
|
||||
describe.For("Transform", T("Transform"), T("Transform"))
|
||||
.Element("Crop", T("Crop"), T("Crops to a fixed height and width"),
|
||||
ApplyFilter,
|
||||
DisplayFilter,
|
||||
"CropFilter"
|
||||
);
|
||||
}
|
||||
|
||||
public void ApplyFilter(FilterContext context) {
|
||||
var newHeight = int.Parse(context.State.Height);
|
||||
newHeight = newHeight > 0 ? newHeight : context.Image.Height;
|
||||
var heightFactor = (float) context.Image.Height/newHeight;
|
||||
|
||||
var newWidth = int.Parse(context.State.Width);
|
||||
newWidth = newWidth > 0 ? newWidth : context.Image.Width;
|
||||
var widthFactor = context.Image.Width/newWidth;
|
||||
|
||||
if (widthFactor != heightFactor) {
|
||||
if (widthFactor > heightFactor) {
|
||||
newHeight = Convert.ToInt32(context.Image.Height/widthFactor);
|
||||
}
|
||||
else {
|
||||
newWidth = Convert.ToInt32(context.Image.Width/heightFactor);
|
||||
}
|
||||
}
|
||||
|
||||
var newImage = new Bitmap(newWidth, newHeight);
|
||||
using (var graphics = Graphics.FromImage(newImage)) {
|
||||
graphics.DrawImage(context.Image, 0, 0, new Rectangle(0, 0, newWidth, newHeight), GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
context.Image = newImage;
|
||||
}
|
||||
|
||||
public LocalizedString DisplayFilter(FilterContext context) {
|
||||
return T("Crop to {0}px high x {1}px wide", context.State.Height, context.State.Width);
|
||||
}
|
||||
}
|
||||
|
||||
public class CropFilterForms : IFormProvider {
|
||||
protected dynamic Shape { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public CropFilterForms(
|
||||
IShapeFactory shapeFactory) {
|
||||
Shape = shapeFactory;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public void Describe(DescribeContext context) {
|
||||
Func<IShapeFactory, object> form =
|
||||
shape => {
|
||||
var f = Shape.Form(
|
||||
Id: "ImageCropFilter",
|
||||
_Height: Shape.Textbox(
|
||||
Id: "height", Name: "Height",
|
||||
Title: T("Height"),
|
||||
Description: T("The height in pixels, 0 to allow any value."),
|
||||
Classes: new[] {"text-small"}),
|
||||
_Width: Shape.Textbox(
|
||||
Id: "width", Name: "Width",
|
||||
Title: T("Width"),
|
||||
Description: T("The width in pixels, 0 to allow any value."),
|
||||
Classes: new[] {"text-small"}));
|
||||
|
||||
return f;
|
||||
};
|
||||
|
||||
context.Form("CropFilter", form);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.MediaProcessing.Descriptors.Filter;
|
||||
using Orchard.MediaProcessing.Services;
|
||||
|
||||
namespace Orchard.MediaProcessing.Providers.Filters {
|
||||
public class ImageFormatFilter : IImageFilterProvider {
|
||||
public ImageFormatFilter() {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public void Describe(DescribeFilterContext describe) {
|
||||
describe.For("Transform", T("Transform"), T("Transform"))
|
||||
.Element("ImageFormat", T("ImageFormat"), T("Changes the image file format"),
|
||||
ApplyFilter,
|
||||
DisplayFilter,
|
||||
"ImageFormatFilter"
|
||||
);
|
||||
}
|
||||
|
||||
public void ApplyFilter(FilterContext context) {
|
||||
context.ImageFormat = ImageFormatConverter.ToImageFormat((ImageFormats)Enum.Parse(typeof (ImageFormats), (string)context.State.ImageFormat));
|
||||
context.FilePath = Path.ChangeExtension(context.FilePath, context.ImageFormat.ToString().ToLower());
|
||||
}
|
||||
|
||||
public LocalizedString DisplayFilter(FilterContext context) {
|
||||
return T("Convert to {0}", context.State.ImageFormat.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public class ImageFormatFilterForms : IFormProvider {
|
||||
protected dynamic Shape { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ImageFormatFilterForms(
|
||||
IShapeFactory shapeFactory) {
|
||||
Shape = shapeFactory;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public void Describe(DescribeContext context) {
|
||||
Func<IShapeFactory, object> form =
|
||||
shape => {
|
||||
var f = Shape.Form(
|
||||
Id: "ImageFormatFilter",
|
||||
_ImageFormat: Shape.SelectList(
|
||||
Id: "imageformat",
|
||||
Name: "ImageFormat"
|
||||
));
|
||||
|
||||
foreach (var item in Enum.GetValues(typeof (ImageFormats))) {
|
||||
var name = Enum.GetName(typeof (ImageFormats), item);
|
||||
f._ImageFormat.Add(new SelectListItem {Value = item.ToString(), Text = name});
|
||||
}
|
||||
|
||||
return f;
|
||||
};
|
||||
|
||||
context.Form("ImageFormatFilter", form);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ImageFormats {
|
||||
Bmp,
|
||||
Gif,
|
||||
Jpeg,
|
||||
Png
|
||||
}
|
||||
|
||||
public class ImageFormatConverter {
|
||||
public static ImageFormat ToImageFormat(ImageFormats format) {
|
||||
switch (format) {
|
||||
case ImageFormats.Bmp:
|
||||
return ImageFormat.Bmp;
|
||||
case ImageFormats.Gif:
|
||||
return ImageFormat.Gif;
|
||||
case ImageFormats.Jpeg:
|
||||
return ImageFormat.Jpeg;
|
||||
case ImageFormats.Png:
|
||||
return ImageFormat.Png;
|
||||
}
|
||||
return ImageFormat.Jpeg;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.MediaProcessing.Descriptors.Filter;
|
||||
using Orchard.MediaProcessing.Services;
|
||||
|
||||
namespace Orchard.MediaProcessing.Providers.Filters {
|
||||
public class ResizeFilter : IImageFilterProvider {
|
||||
public ResizeFilter() {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public void Describe(DescribeFilterContext describe) {
|
||||
describe.For("Transform", T("Transform"), T("Transform"))
|
||||
.Element("Resize", T("Resize"), T("Resizes to a fixed height and width"),
|
||||
ApplyFilter,
|
||||
DisplayFilter,
|
||||
"ResizeFilter"
|
||||
);
|
||||
}
|
||||
|
||||
public void ApplyFilter(FilterContext context) {
|
||||
var newHeight = int.Parse((string)context.State.Height);
|
||||
newHeight = newHeight > 0 ? newHeight : context.Image.Height;
|
||||
var heightFactor = (float) context.Image.Height/newHeight;
|
||||
|
||||
var newWidth = int.Parse((string)context.State.Width);
|
||||
newWidth = newWidth > 0 ? newWidth : context.Image.Width;
|
||||
var widthFactor = context.Image.Width/newWidth;
|
||||
|
||||
if (widthFactor != heightFactor) {
|
||||
if (widthFactor > heightFactor) {
|
||||
newHeight = Convert.ToInt32(context.Image.Height/widthFactor);
|
||||
}
|
||||
else {
|
||||
newWidth = Convert.ToInt32(context.Image.Width/heightFactor);
|
||||
}
|
||||
}
|
||||
|
||||
var newImage = new Bitmap(newWidth, newHeight);
|
||||
using (var graphics = Graphics.FromImage(newImage)) {
|
||||
graphics.CompositingQuality = CompositingQuality.HighSpeed;
|
||||
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
graphics.CompositingMode = CompositingMode.SourceCopy;
|
||||
graphics.DrawImage(context.Image, 0, 0, newWidth, newHeight);
|
||||
}
|
||||
|
||||
context.Image = newImage;
|
||||
}
|
||||
|
||||
public LocalizedString DisplayFilter(FilterContext context) {
|
||||
return T("Resize to {0}px high x {1}px wide", context.State.Height, context.State.Width);
|
||||
}
|
||||
}
|
||||
|
||||
public class ResizeFilterForms : IFormProvider {
|
||||
protected dynamic Shape { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ResizeFilterForms(
|
||||
IShapeFactory shapeFactory) {
|
||||
Shape = shapeFactory;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public void Describe(DescribeContext context) {
|
||||
Func<IShapeFactory, object> form =
|
||||
shape => {
|
||||
var f = Shape.Form(
|
||||
Id: "ImageResizeFilter",
|
||||
_Height: Shape.Textbox(
|
||||
Id: "height", Name: "Height",
|
||||
Title: T("Height"),
|
||||
Description: T("The height in pixels, 0 to allow any value."),
|
||||
Classes: new[] {"text-small"}),
|
||||
_Width: Shape.Textbox(
|
||||
Id: "width", Name: "Width",
|
||||
Title: T("Width"),
|
||||
Description: T("The width in pixels, 0 to allow any value."),
|
||||
Classes: new[] {"text-small"}));
|
||||
return f;
|
||||
};
|
||||
|
||||
context.Form("ResizeFilter", form);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="webpages:Enabled" value="false" />
|
||||
</appSettings>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
<!-- iis6 - for any request in this location, return via managed static file handler -->
|
||||
<add path="*" verb="*" type="System.Web.StaticFileHandler" />
|
||||
</httpHandlers>
|
||||
</system.web>
|
||||
<system.webServer>
|
||||
<staticContent>
|
||||
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
|
||||
</staticContent>
|
||||
|
||||
<handlers accessPolicy="Script,Read">
|
||||
<!--
|
||||
iis7 - for any request to a file exists on disk, return it via native http module.
|
||||
accessPolicy 'Script' is to allow for a managed 404 page.
|
||||
-->
|
||||
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
</configuration>
|
@@ -0,0 +1,7 @@
|
||||
using Orchard.MediaProcessing.Descriptors.Filter;
|
||||
|
||||
namespace Orchard.MediaProcessing.Services {
|
||||
public interface IImageFilterProvider : IDependency {
|
||||
void Describe(DescribeFilterContext describe);
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
namespace Orchard.MediaProcessing.Services {
|
||||
public interface IImageProcessingFileNameProvider : IDependency {
|
||||
string GetFileName(string profile, string path);
|
||||
void UpdateFileName(string profile, string path, string fileName);
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.MediaProcessing.Descriptors;
|
||||
using Orchard.MediaProcessing.Descriptors.Filter;
|
||||
|
||||
namespace Orchard.MediaProcessing.Services {
|
||||
public interface IImageProcessingManager : IDependency {
|
||||
IEnumerable<TypeDescriptor<FilterDescriptor>> DescribeFilters();
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.MediaProcessing.Models;
|
||||
|
||||
namespace Orchard.MediaProcessing.Services {
|
||||
public interface IImageProfileService : IDependency {
|
||||
ImageProfilePart GetImageProfile(int id);
|
||||
ImageProfilePart GetImageProfileByName(string name);
|
||||
IEnumerable<ImageProfilePart> GetAllImageProfiles();
|
||||
ImageProfilePart CreateImageProfile(string name);
|
||||
void DeleteImageProfile(int id);
|
||||
void MoveUp(int filterId);
|
||||
void MoveDown(int filterId);
|
||||
}
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Orchard.Caching;
|
||||
using Orchard.Data;
|
||||
using Orchard.MediaProcessing.Models;
|
||||
|
||||
namespace Orchard.MediaProcessing.Services {
|
||||
public class ImageProcessingFileNameProvider : IImageProcessingFileNameProvider {
|
||||
private readonly IImageProfileService _imageProfileService;
|
||||
private readonly ICacheManager _cacheManager;
|
||||
private readonly ISignals _signals;
|
||||
|
||||
public ImageProcessingFileNameProvider(IImageProfileService imageProfileService, ICacheManager cacheManager, ISignals signals) {
|
||||
_imageProfileService = imageProfileService;
|
||||
_cacheManager = cacheManager;
|
||||
_signals = signals;
|
||||
}
|
||||
|
||||
public string GetFileName(string profile, string path) {
|
||||
var fileNames = GetFileNames(profile);
|
||||
string fileName;
|
||||
if (!fileNames.TryGetValue(path, out fileName)) {
|
||||
var profilePart = _imageProfileService.GetImageProfileByName(profile);
|
||||
if (profilePart != null) {
|
||||
var fileNameRecord = profilePart.FileNames.FirstOrDefault(f => f.Path == path);
|
||||
if (fileNameRecord == null) {
|
||||
return null;
|
||||
}
|
||||
fileNames.Add(path, fileNameRecord.FileName);
|
||||
return fileNameRecord.FileName;
|
||||
}
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void UpdateFileName(string profile, string path, string fileName) {
|
||||
var fileNames = GetFileNames(profile);
|
||||
string existingFileName;
|
||||
if (fileNames.TryGetValue(path, out existingFileName) && existingFileName == fileName) {
|
||||
return;
|
||||
}
|
||||
fileNames[path] = fileName;
|
||||
var profilePart = _imageProfileService.GetImageProfileByName(profile);
|
||||
var fileNameRecord = profilePart.FileNames.FirstOrDefault(f => f.Path == path);
|
||||
if (fileNameRecord == null) {
|
||||
fileNameRecord = new FileNameRecord {
|
||||
Path = path,
|
||||
ImageProfilePartRecord = profilePart.Record
|
||||
};
|
||||
profilePart.FileNames.Add(fileNameRecord);
|
||||
}
|
||||
fileNameRecord.FileName = fileName;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetFileNames(string profile) {
|
||||
return _cacheManager.Get("MediaProcessing_" + profile, ctx => {
|
||||
ctx.Monitor(_signals.When("MediaProcessing_" + profile + "_Saved"));
|
||||
return new Dictionary<string, string>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Localization;
|
||||
using Orchard.MediaProcessing.Descriptors;
|
||||
using Orchard.MediaProcessing.Descriptors.Filter;
|
||||
|
||||
namespace Orchard.MediaProcessing.Services {
|
||||
public class ImageProcessingManager : IImageProcessingManager {
|
||||
private readonly IEnumerable<IImageFilterProvider> _filterProviders;
|
||||
|
||||
public ImageProcessingManager(
|
||||
IEnumerable<IImageFilterProvider> filterProviders) {
|
||||
_filterProviders = filterProviders;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public IEnumerable<TypeDescriptor<FilterDescriptor>> DescribeFilters() {
|
||||
var context = new DescribeFilterContext();
|
||||
|
||||
foreach (var provider in _filterProviders) {
|
||||
provider.Describe(context);
|
||||
}
|
||||
return context.Describe();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,90 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Data;
|
||||
using Orchard.Localization;
|
||||
using Orchard.MediaProcessing.Models;
|
||||
|
||||
namespace Orchard.MediaProcessing.Services {
|
||||
public class ImageProfileService : IImageProfileService {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IRepository<FilterRecord> _filterRepository;
|
||||
|
||||
public ImageProfileService(IContentManager contentManager, IRepository<FilterRecord> filterRepository) {
|
||||
_contentManager = contentManager;
|
||||
_filterRepository = filterRepository;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ImageProfilePart GetImageProfile(int id) {
|
||||
return _contentManager.Get<ImageProfilePart>(id);
|
||||
}
|
||||
|
||||
public ImageProfilePart GetImageProfileByName(string name) {
|
||||
return _contentManager.Query<ImageProfilePart, ImageProfilePartRecord>().Where(x => x.Name == name).Slice(0, 1).FirstOrDefault();
|
||||
}
|
||||
|
||||
public IEnumerable<ImageProfilePart> GetAllImageProfiles() {
|
||||
return _contentManager.Query<ImageProfilePart, ImageProfilePartRecord>().List();
|
||||
}
|
||||
|
||||
public ImageProfilePart CreateImageProfile(string name) {
|
||||
var contentItem = _contentManager.New("ImageProfile");
|
||||
var profile = contentItem.As<ImageProfilePart>();
|
||||
profile.Name = name;
|
||||
|
||||
_contentManager.Create(contentItem);
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
public void DeleteImageProfile(int id) {
|
||||
var profile = _contentManager.Get(id);
|
||||
|
||||
if (profile != null) {
|
||||
_contentManager.Remove(profile);
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveUp(int filterId) {
|
||||
var filter = _filterRepository.Get(filterId);
|
||||
|
||||
// look for the previous action in order in same rule
|
||||
var previous = _filterRepository.Table
|
||||
.Where(x => x.Position < filter.Position && x.ImageProfilePartRecord.Id == filter.ImageProfilePartRecord.Id)
|
||||
.OrderByDescending(x => x.Position)
|
||||
.FirstOrDefault();
|
||||
|
||||
// nothing to do if already at the top
|
||||
if (previous == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// switch positions
|
||||
var temp = previous.Position;
|
||||
previous.Position = filter.Position;
|
||||
filter.Position = temp;
|
||||
}
|
||||
|
||||
public void MoveDown(int filterId) {
|
||||
var filter = _filterRepository.Get(filterId);
|
||||
|
||||
// look for the next action in order in same rule
|
||||
var next = _filterRepository.Table
|
||||
.Where(x => x.Position > filter.Position && x.ImageProfilePartRecord.Id == filter.ImageProfilePartRecord.Id)
|
||||
.OrderBy(x => x.Position)
|
||||
.FirstOrDefault();
|
||||
|
||||
// nothing to do if already at the end
|
||||
if (next == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// switch positions
|
||||
var temp = next.Position;
|
||||
next.Position = filter.Position;
|
||||
filter.Position = temp;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
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.Services;
|
||||
using Orchard.Mvc.Html;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
namespace Orchard.MediaProcessing.Shapes {
|
||||
public class MediaShapes : IDependency {
|
||||
private readonly Work<IStorageProvider> _storageProvider;
|
||||
private readonly Work<IImageProcessingFileNameProvider> _fileNameProvider;
|
||||
private readonly Work<IImageProfileService> _profileService;
|
||||
private readonly Work<IImageProcessingManager> _processingManager;
|
||||
private readonly Work<IOrchardServices> _services;
|
||||
|
||||
public MediaShapes(Work<IStorageProvider> storageProvider, Work<IImageProcessingFileNameProvider> fileNameProvider, Work<IImageProfileService> profileService, Work<IImageProcessingManager> processingManager, Work<IOrchardServices> services) {
|
||||
_storageProvider = storageProvider;
|
||||
_fileNameProvider = fileNameProvider;
|
||||
_profileService = profileService;
|
||||
_processingManager = processingManager;
|
||||
_services = services;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
[Shape]
|
||||
public void ImageUrl(dynamic Display, TextWriter Output, string Profile, string Path) {
|
||||
var filePath = _fileNameProvider.Value.GetFileName(Profile, Path);
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
try {
|
||||
var profilePart = _profileService.Value.GetImageProfileByName(Profile);
|
||||
if (profilePart == null)
|
||||
return;
|
||||
|
||||
var image = GetImage(Path);
|
||||
var filterContext = new FilterContext {Image = image, ImageFormat = image.RawFormat, FilePath = _storageProvider.Value.Combine(Profile, CreateDefaultFileName(Path))};
|
||||
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;
|
||||
filterContext.State = FormParametersHelper.ToDynamic(filter.State);
|
||||
descriptor.Filter(filterContext);
|
||||
}
|
||||
|
||||
_fileNameProvider.Value.UpdateFileName(Profile, Path, filterContext.FilePath);
|
||||
|
||||
if (!filterContext.Saved) {
|
||||
_storageProvider.Value.TryCreateFolder(profilePart.Name);
|
||||
var newFile = _storageProvider.Value.OpenOrCreate(filterContext.FilePath);
|
||||
using (var imageStream = newFile.OpenWrite()) {
|
||||
filterContext.Image.Save(imageStream, filterContext.ImageFormat);
|
||||
}
|
||||
}
|
||||
filePath = filterContext.FilePath;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Error(ex, "An error occured while processing {0} for image {1}", Profile, Path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Output.Write(_storageProvider.Value.GetPublicUrl(filePath));
|
||||
}
|
||||
|
||||
// TODO: Update this method once the storage provider has been updated
|
||||
private Image GetImage(string path) {
|
||||
// http://blob.storage-provider.net/my-image.jpg
|
||||
if (Uri.IsWellFormedUriString(path, UriKind.Absolute)) {
|
||||
var webClient = new WebClient();
|
||||
return new Bitmap(webClient.OpenRead(new Uri(path)));
|
||||
}
|
||||
// ~/Media/Default/images/my-image.jpg
|
||||
if (VirtualPathUtility.IsAppRelative(path)) {
|
||||
var webClient = new WebClient();
|
||||
return new Bitmap(webClient.OpenRead(new Uri(_services.Value.WorkContext.HttpContext.Request.Url, VirtualPathUtility.ToAbsolute(path))));
|
||||
}
|
||||
// images/my-image.jpg
|
||||
var file = _storageProvider.Value.GetFile(path);
|
||||
return new Bitmap(file.OpenRead());
|
||||
}
|
||||
|
||||
private static string CreateDefaultFileName(string path) {
|
||||
var extention = Path.GetExtension(path);
|
||||
var newPath = Path.ChangeExtension(path, "");
|
||||
newPath = newPath.Replace(@"/", "_");
|
||||
return newPath.ToSafeName() + extention;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="webpages:Enabled" value="false" />
|
||||
</appSettings>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
<!-- iis6 - for any request in this location, return via managed static file handler -->
|
||||
<add path="*" verb="*" type="System.Web.StaticFileHandler" />
|
||||
</httpHandlers>
|
||||
</system.web>
|
||||
<system.webServer>
|
||||
<staticContent>
|
||||
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
|
||||
</staticContent>
|
||||
|
||||
<handlers accessPolicy="Script,Read">
|
||||
<!--
|
||||
iis7 - for any request to a file exists on disk, return it via native http module.
|
||||
accessPolicy 'Script' is to allow for a managed 404 page.
|
||||
-->
|
||||
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
</configuration>
|
@@ -0,0 +1,8 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Orchard.MediaProcessing.ViewModels {
|
||||
public class AdminCreateViewModel {
|
||||
[Required, StringLength(1024)]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Orchard.MediaProcessing.ViewModels {
|
||||
public class AdminEditViewModel {
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required, StringLength(1024)]
|
||||
public string Name { get; set; }
|
||||
|
||||
public IEnumerable<FilterEntry> Filters { get; set; }
|
||||
}
|
||||
|
||||
public class FilterEntry {
|
||||
public int FilterRecordId { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string DisplayText { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.MediaProcessing.Models;
|
||||
|
||||
namespace Orchard.MediaProcessing.ViewModels {
|
||||
|
||||
public class AdminIndexViewModel {
|
||||
public IList<ImageProfileEntry> ImageProfiles { get; set; }
|
||||
public AdminIndexOptions Options { get; set; }
|
||||
public dynamic Pager { get; set; }
|
||||
}
|
||||
|
||||
public class ImageProfileEntry {
|
||||
public ImageProfilePartRecord ImageProfile { get; set; }
|
||||
public bool IsChecked { get; set; }
|
||||
|
||||
public int ImageProfileId { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
public class AdminIndexOptions {
|
||||
public string Search { get; set; }
|
||||
public ImageProfilesFilter Filter { get; set; }
|
||||
public ImageProfilesBulkAction BulkAction { get; set; }
|
||||
}
|
||||
|
||||
public enum ImageProfilesFilter {
|
||||
All
|
||||
}
|
||||
|
||||
public enum ImageProfilesBulkAction {
|
||||
None,
|
||||
Delete
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.MediaProcessing.Descriptors;
|
||||
using Orchard.MediaProcessing.Descriptors.Filter;
|
||||
|
||||
namespace Orchard.MediaProcessing.ViewModels {
|
||||
public class FilterAddViewModel {
|
||||
public int Id { get; set; }
|
||||
public IEnumerable<TypeDescriptor<FilterDescriptor>> Filters { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
using Orchard.MediaProcessing.Descriptors.Filter;
|
||||
|
||||
namespace Orchard.MediaProcessing.ViewModels {
|
||||
public class FilterEditViewModel {
|
||||
public int Id { get; set; }
|
||||
public string Description { get; set; }
|
||||
public FilterDescriptor Filter { get; set; }
|
||||
public dynamic Form { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
@model Orchard.MediaProcessing.ViewModels.AdminEditViewModel
|
||||
@{
|
||||
Layout.Title = T("Edit Media Profile - {0}", Model.Name).Text;
|
||||
}
|
||||
|
||||
@using (Html.BeginFormAntiForgeryPost())
|
||||
{
|
||||
@Html.ValidationSummary()
|
||||
|
||||
<fieldset class="bulk-actions">
|
||||
<h2>@T("Filters")</h2>
|
||||
</fieldset>
|
||||
|
||||
<div class="manage">
|
||||
@Html.ActionLink(T("Add a new Filter").Text, "Add", new { controller = "Filter", id = Model.Id }, new { @class = "button primaryAction" })
|
||||
</div>
|
||||
|
||||
<table class="items">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" >@T("Description")</th>
|
||||
<th scope="col" class="actions"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var filter in Model.Filters)
|
||||
{
|
||||
<tr>
|
||||
<td>@filter.DisplayText</td>
|
||||
<td>
|
||||
@Html.ActionLink(T("Edit").Text, "Edit", new { controller = "Filter", id = Model.Id, category = filter.Category, type = filter.Type, filterId = filter.FilterRecordId }) |
|
||||
@Html.ActionLink(T("Delete").Text, "Delete", new { controller = "Filter", id = Model.Id, filterId = filter.FilterRecordId }, new { itemprop = "RemoveUrl UnsafeUrl" }) |
|
||||
@if (filter != Model.Filters.First()) {
|
||||
@Html.ActionLink(T("Up").Text, "Move", new { controller = "Admin", id = Model.Id, direction = "up", filterId = filter.FilterRecordId }) @:|
|
||||
}
|
||||
@if (filter != Model.Filters.Last()) {
|
||||
@Html.ActionLink(T("Down").Text, "Move", new { controller = "Admin", id = Model.Id, direction = "down", filterId = filter.FilterRecordId })
|
||||
}
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
|
||||
<fieldset>
|
||||
@*@Html.ActionLink(T("Preview").ToString(), "Preview", new { Model.Id }, new { @class = "button" })*@
|
||||
@Html.ActionLink(T("Close").ToString(), "Index", new { }, new { @class = "button" })
|
||||
</fieldset>
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
@model Orchard.MediaProcessing.ViewModels.AdminIndexViewModel
|
||||
@using Orchard.MediaProcessing.ViewModels
|
||||
@{
|
||||
Layout.Title = T("Profiles").ToString();
|
||||
|
||||
var index = 0;
|
||||
|
||||
var pageSizes = new List<int?>() { 10, 50, 100 };
|
||||
var defaultPageSize = WorkContext.CurrentSite.PageSize;
|
||||
if(!pageSizes.Contains(defaultPageSize)) {
|
||||
pageSizes.Add(defaultPageSize);
|
||||
}
|
||||
}
|
||||
|
||||
@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>
|
||||
|
||||
<fieldset class="bulk-actions">
|
||||
<label for="publishActions">@T("Actions:")</label>
|
||||
<select id="publishActions" name="@Html.NameOf(m => m.Options.BulkAction)">
|
||||
@Html.SelectOption(Model.Options.BulkAction, ImageProfilesBulkAction.None, T("Choose action...").ToString())
|
||||
@Html.SelectOption(Model.Options.BulkAction, ImageProfilesBulkAction.Delete, T("Delete").ToString())
|
||||
</select>
|
||||
<button type="submit" name="submit.BulkEdit" value="@T("Apply")">@T("Apply")</button>
|
||||
</fieldset>
|
||||
<fieldset class="bulk-actions">
|
||||
<input type="hidden" name="Page" value="1" />
|
||||
<label for="pageSize">@T("Show:")</label>
|
||||
<select id="pageSize" name="PageSize">
|
||||
@Html.SelectOption((int)Model.Pager.PageSize, 0, T("All").ToString())
|
||||
@foreach(int size in pageSizes.OrderBy(p => p)) {
|
||||
@Html.SelectOption((int)Model.Pager.PageSize, size, size.ToString())
|
||||
}
|
||||
</select>
|
||||
<button type="submit" name="submit.Filter" value="@T("Filter")">@T("Filter")</button>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<table class="items">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="checkbox"> ↓</th>
|
||||
<th scope="col">@T("Name")</th>
|
||||
<th scope="col" class="actions"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var entry in Model.ImageProfiles) {
|
||||
<tr>
|
||||
<td>
|
||||
<input type="hidden" value="@Model.ImageProfiles[index].ImageProfileId" name="@Html.NameOf(m => m.ImageProfiles[index].ImageProfileId)"/>
|
||||
<input type="checkbox" value="true" name="@Html.NameOf(m => m.ImageProfiles[index].IsChecked)"/>
|
||||
</td>
|
||||
<td>
|
||||
@Html.ActionLink(entry.Name, "Edit", new { id = entry.ImageProfileId })
|
||||
</td>
|
||||
<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("Delete").ToString(), "Delete", new { id = entry.ImageProfileId }, new { itemprop = "RemoveUrl UnsafeUrl" }) |
|
||||
@*@Html.ActionLink(T("Preview").ToString(), "Preview", new { id = entry.ImageProfileId })*@
|
||||
</td>
|
||||
</tr>
|
||||
index++;
|
||||
}
|
||||
</table>
|
||||
@Display(Model.Pager)
|
||||
</fieldset>
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
@model Orchard.MediaProcessing.Models.ImageProfilePart
|
||||
<fieldset>
|
||||
@Html.LabelFor(m => m.Name, T("Name"))
|
||||
@Html.TextBoxFor(m => m.Name, new {@class = "text"})
|
||||
</fieldset>
|
@@ -0,0 +1,27 @@
|
||||
@model Orchard.MediaProcessing.ViewModels.FilterAddViewModel
|
||||
@{
|
||||
Layout.Title = T("Add a Filter");
|
||||
}
|
||||
|
||||
@if (!Model.Filters.Any()) {
|
||||
<h3>@T("There are no currently available filters")</h3>
|
||||
}
|
||||
|
||||
@foreach (var filter in Model.Filters.OrderBy(x => x.Name.Text)) {
|
||||
<h2>@filter.Name</h2>
|
||||
|
||||
<table class="items">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="width:300px">@T("Name")</th>
|
||||
<th scope="col">@T("Description")</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var descriptor in filter.Descriptors) {
|
||||
<tr>
|
||||
<td>@Html.ActionLink(descriptor.Name.Text, "Edit", new { id = Model.Id, category = descriptor.Category, type = descriptor.Type })</td>
|
||||
<td>@descriptor.Description</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
@model Orchard.MediaProcessing.ViewModels.FilterEditViewModel
|
||||
@{
|
||||
Layout.Title = T("Edit Filter - {0}", Model.Filter.Name);
|
||||
}
|
||||
|
||||
@Html.ValidationSummary()
|
||||
|
||||
<h2>@Model.Filter.Name</h2>
|
||||
<span class="hint">@Model.Filter.Description</span>
|
||||
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
|
||||
<fieldset>
|
||||
@Html.LabelFor(m => m.Description, T("Description"))
|
||||
@Html.TextBoxFor(m => m.Description, new { @class = "textMedium" })
|
||||
<span class="hint">@T("You may optionally give a description to this filter, to be used in the dashboard screens.")</span>
|
||||
</fieldset>
|
||||
|
||||
@* Render the dynamic form *@
|
||||
if (Model.Form != null) {
|
||||
<fieldset>
|
||||
@DisplayChildren(Model.Form)
|
||||
</fieldset>
|
||||
}
|
||||
|
||||
@Display.TokenHint()
|
||||
|
||||
<fieldset>
|
||||
<button class="primaryAction" type="submit">@T("Save")</button>
|
||||
</fieldset>
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="webpages:Enabled" value="false" />
|
||||
</appSettings>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
</httpHandlers>
|
||||
|
||||
<!--
|
||||
Enabling request validation in view pages would cause validation to occur
|
||||
after the input has already been processed by the controller. By default
|
||||
MVC performs request validation before a controller processes the input.
|
||||
To change this behavior apply the ValidateInputAttribute to a
|
||||
controller or action.
|
||||
-->
|
||||
<pages
|
||||
validateRequest="false"
|
||||
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
|
||||
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
|
||||
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<controls>
|
||||
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" namespace="System.Web.Mvc" tagPrefix="mvc" />
|
||||
</controls>
|
||||
</pages>
|
||||
</system.web>
|
||||
|
||||
<system.webServer>
|
||||
<validation validateIntegratedModeConfiguration="false"/>
|
||||
<handlers>
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
|
||||
<bindingRedirect oldVersion="2.0.0.0" newVersion="3.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
41
src/Orchard.Web/Modules/Orchard.MediaProcessing/Web.config
Normal file
41
src/Orchard.Web/Modules/Orchard.MediaProcessing/Web.config
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
|
||||
<configSections>
|
||||
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||
<remove name="host" />
|
||||
<remove name="pages" />
|
||||
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<system.web.webPages.razor>
|
||||
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||
<pages pageBaseType="Orchard.Mvc.ViewEngines.Razor.WebViewPage">
|
||||
<namespaces>
|
||||
<add namespace="System.Web.Mvc" />
|
||||
<add namespace="System.Web.Mvc.Ajax" />
|
||||
<add namespace="System.Web.Mvc.Html" />
|
||||
<add namespace="System.Web.Routing" />
|
||||
<add namespace="System.Web.WebPages" />
|
||||
<add namespace="System.Linq"/>
|
||||
<add namespace="System.Collections.Generic"/>
|
||||
<add namespace="Orchard.Mvc.Html"/>
|
||||
</namespaces>
|
||||
</pages>
|
||||
</system.web.webPages.razor>
|
||||
|
||||
<system.web>
|
||||
<compilation targetFramework="4.0">
|
||||
<assemblies>
|
||||
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
|
||||
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||
<add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
</assemblies>
|
||||
</compilation>
|
||||
</system.web>
|
||||
|
||||
</configuration>
|
@@ -146,7 +146,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpgradeTo16", "Orchard.Web\
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Workflows", "Orchard.Web\Modules\Orchard.Workflows\Orchard.Workflows.csproj", "{7059493C-8251-4764-9C1E-2368B8B485BC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Scripting.CSharp", "Orchard.Web\Modules\Orchard.Scripting.CSharp\Orchard.Scripting.CSharp.csproj", "{5D13EF34-8B39-4EC5-847F-E12892ACF841}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.MediaProcessing", "Orchard.Web\Modules\Orchard.MediaProcessing\Orchard.MediaProcessing.csproj", "{08191FCD-7258-4F19-95FB-AEC3DE77B2EB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -819,16 +819,16 @@ Global
|
||||
{7059493C-8251-4764-9C1E-2368B8B485BC}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7059493C-8251-4764-9C1E-2368B8B485BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7059493C-8251-4764-9C1E-2368B8B485BC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5D13EF34-8B39-4EC5-847F-E12892ACF841}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5D13EF34-8B39-4EC5-847F-E12892ACF841}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU
|
||||
{5D13EF34-8B39-4EC5-847F-E12892ACF841}.Coverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5D13EF34-8B39-4EC5-847F-E12892ACF841}.Coverage|Any CPU.Build.0 = Release|Any CPU
|
||||
{5D13EF34-8B39-4EC5-847F-E12892ACF841}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5D13EF34-8B39-4EC5-847F-E12892ACF841}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5D13EF34-8B39-4EC5-847F-E12892ACF841}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5D13EF34-8B39-4EC5-847F-E12892ACF841}.FxCop|Any CPU.Build.0 = Release|Any CPU
|
||||
{5D13EF34-8B39-4EC5-847F-E12892ACF841}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5D13EF34-8B39-4EC5-847F-E12892ACF841}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{08191FCD-7258-4F19-95FB-AEC3DE77B2EB}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{08191FCD-7258-4F19-95FB-AEC3DE77B2EB}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU
|
||||
{08191FCD-7258-4F19-95FB-AEC3DE77B2EB}.Coverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{08191FCD-7258-4F19-95FB-AEC3DE77B2EB}.Coverage|Any CPU.Build.0 = Release|Any CPU
|
||||
{08191FCD-7258-4F19-95FB-AEC3DE77B2EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{08191FCD-7258-4F19-95FB-AEC3DE77B2EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{08191FCD-7258-4F19-95FB-AEC3DE77B2EB}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{08191FCD-7258-4F19-95FB-AEC3DE77B2EB}.FxCop|Any CPU.Build.0 = Release|Any CPU
|
||||
{08191FCD-7258-4F19-95FB-AEC3DE77B2EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{08191FCD-7258-4F19-95FB-AEC3DE77B2EB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -884,7 +884,7 @@ Global
|
||||
{3BD22132-D538-48C6-8854-F71333C798EB} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{8A9FDB57-342D-49C2-BAFC-D885AAE5CC7C} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{7059493C-8251-4764-9C1E-2368B8B485BC} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{5D13EF34-8B39-4EC5-847F-E12892ACF841} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{08191FCD-7258-4F19-95FB-AEC3DE77B2EB} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{ABC826D4-2FA1-4F2F-87DE-E6095F653810} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||
{F112851D-B023-4746-B6B1-8D2E5AD8F7AA} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||
{6CB3EB30-F725-45C0-9742-42599BA8E8D2} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||
|
Reference in New Issue
Block a user