From 2720f6d94a826171d51fbeaf1f3610a69c9e581a Mon Sep 17 00:00:00 2001 From: Dave Reed Date: Fri, 11 Feb 2011 17:24:04 -0800 Subject: [PATCH] Initial version of the generic MediaPicker module (note: currently largely unstyled html, but functional). --HG-- branch : dev --- .../Orchard.MediaPicker/AdminFilter.cs | 26 ++ .../Controllers/HomeController.cs | 46 ++++ .../Modules/Orchard.MediaPicker/Module.txt | 13 + .../Orchard.MediaPicker.csproj | 139 ++++++++++ .../Properties/AssemblyInfo.cs | 34 +++ .../Scripts/MediaBrowser.js | 258 ++++++++++++++++++ .../Scripts/MediaPicker.js | 40 +++ .../Orchard.MediaPicker/Scripts/Web.config | 21 ++ .../Orchard.MediaPicker/Styles/Web.config | 21 ++ .../Styles/mediapicker.css | 25 ++ .../Orchard.MediaPicker/Views/Index.cshtml | 46 ++++ .../Views/Tab_Gallery.cshtml | 109 ++++++++ .../Orchard.MediaPicker/Views/Tab_Url.cshtml | 74 +++++ .../Orchard.MediaPicker/Views/Web.config | 41 +++ .../Orchard.Setup/Services/SetupService.cs | 1 + .../Scripts/plugins/addmedia/addmedia.htm | 35 --- .../Scripts/plugins/addmedia/editor_plugin.js | 1 - .../plugins/addmedia/editor_plugin_src.js | 68 ----- .../Scripts/plugins/addmedia/js/addmedia.js | 92 ------- .../Scripts/plugins/addmedia/langs/en.js | 6 - .../Scripts/plugins/addmedia/langs/en_dlg.js | 12 - .../plugins/mediapicker/editor_plugin.js | 2 + .../plugins/mediapicker/editor_plugin_src.js | 97 +++++++ .../img/picture_add.png | Bin .../Modules/TinyMce/TinyMce.csproj | 11 +- .../TinyMce/Views/Body-Html.Editor.cshtml | 10 +- src/Orchard.sln | 30 +- 27 files changed, 1025 insertions(+), 233 deletions(-) create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/AdminFilter.cs create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Controllers/HomeController.cs create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Module.txt create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Orchard.MediaPicker.csproj create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Properties/AssemblyInfo.cs create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaBrowser.js create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaPicker.js create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/Web.config create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/Web.config create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/mediapicker.css create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Index.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Tab_Gallery.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Tab_Url.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Web.config delete mode 100644 src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/addmedia.htm delete mode 100644 src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/editor_plugin.js delete mode 100644 src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/editor_plugin_src.js delete mode 100644 src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/js/addmedia.js delete mode 100644 src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/langs/en.js delete mode 100644 src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/langs/en_dlg.js create mode 100644 src/Orchard.Web/Modules/TinyMce/Scripts/plugins/mediapicker/editor_plugin.js create mode 100644 src/Orchard.Web/Modules/TinyMce/Scripts/plugins/mediapicker/editor_plugin_src.js rename src/Orchard.Web/Modules/TinyMce/Scripts/plugins/{addmedia => mediapicker}/img/picture_add.png (100%) diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/AdminFilter.cs b/src/Orchard.Web/Modules/Orchard.MediaPicker/AdminFilter.cs new file mode 100644 index 000000000..62b379218 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/AdminFilter.cs @@ -0,0 +1,26 @@ +using System; +using System.Web.Mvc; +using Orchard.Mvc.Filters; +using Orchard.UI.Resources; + +namespace Orchard.MediaPicker { + public class AdminFilter : FilterProvider, IResultFilter { + private readonly IResourceManager _resourceManager; + + public AdminFilter(IResourceManager resourceManager) { + _resourceManager = resourceManager; + } + + public void OnResultExecuting(ResultExecutingContext filterContext) { + // should only run on a full view rendering result + if (!(filterContext.Result is ViewResult) || !Orchard.UI.Admin.AdminFilter.IsApplied(filterContext.RequestContext)) + return; + _resourceManager.Require("script", "OpenAjax"); + _resourceManager.Require("script", "jQuery"); + _resourceManager.Include("script", "~/Modules/Orchard.MediaPicker/Scripts/MediaPicker.js", null); + } + + public void OnResultExecuted(ResultExecutedContext filterContext) { + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Controllers/HomeController.cs b/src/Orchard.Web/Modules/Orchard.MediaPicker/Controllers/HomeController.cs new file mode 100644 index 000000000..f65e43524 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Controllers/HomeController.cs @@ -0,0 +1,46 @@ +using System.Web.Mvc; +using Orchard.Localization; +using Orchard; +using System.Collections.Generic; +using Orchard.Media.Models; +using Orchard.Media.ViewModels; +using Orchard.Media.Services; +using Orchard.Security; +using Orchard.UI.Admin; +using Orchard.Themes; + +namespace Orchard.MediaPicker.Controllers { + [Themed(false)] + public class HomeController : Controller { + private readonly IMediaService _mediaService; + private readonly IAuthorizer _authorizer; + + public IOrchardServices Services { get; set; } + + public HomeController(IOrchardServices services, IMediaService mediaService, IAuthorizer authorizer) { + _authorizer = authorizer; + + Services = services; + _mediaService = mediaService; + + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + public ActionResult Index(string name, string mediaPath) { + // this controller should only be used from the admin panel. + // it is not itself an admincontroller, however, because it needs to be 'unthemed', + // which admincontrollers currently cannot be. + if (!_authorizer.Authorize(StandardPermissions.AccessAdminPanel, T("Can't access the admin"))) { + return new HttpUnauthorizedResult(); + } + + IEnumerable mediaFiles = _mediaService.GetMediaFiles(mediaPath); + IEnumerable mediaFolders = _mediaService.GetMediaFolders(mediaPath); + var model = new MediaFolderEditViewModel { FolderName = name, MediaFiles = mediaFiles, MediaFolders = mediaFolders, MediaPath = mediaPath }; + ViewData["Service"] = _mediaService; + return View(model); + } + } +} diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Module.txt b/src/Orchard.Web/Modules/Orchard.MediaPicker/Module.txt new file mode 100644 index 000000000..70b007878 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Module.txt @@ -0,0 +1,13 @@ +Name: MediaPicker +AntiForgery: enabled +Author: The Orchard Team +Website: http://orchardproject.net +Version: 1.0 +OrchardVersion: 1.0 +Description: Description for the module +Features: + Orchard.MediaPicker: + Name: MediaPicker + Dependencies: Orchard.Media, Orchard.jQuery + Description: UI for browsing for, uploading, or selecting an image for an HTML editor. + Category: Input Editor \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Orchard.MediaPicker.csproj b/src/Orchard.Web/Modules/Orchard.MediaPicker/Orchard.MediaPicker.csproj new file mode 100644 index 000000000..e2d3a3eaf --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Orchard.MediaPicker.csproj @@ -0,0 +1,139 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {43D0EC0B-1955-4566-8D31-7B9102DA1703} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Orchard.MediaPicker + Orchard.MediaPicker + v4.0 + false + + + 3.5 + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + + + 3.5 + + + + False + ..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll + + + + + + + + + + + + + + + + + + + + + {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} + Orchard.Framework + + + {9916839C-39FC-4CEB-A5AF-89CA7E87119F} + Orchard.Core + + + {D9A7B330-CD22-4DA1-A95A-8DE1982AD8EB} + Orchard.Media + + + + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)\..\Manifests + + + + + + + + + + + + False + True + 45979 + / + + + False + True + http://orchard.codeplex.com + False + + + + + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Properties/AssemblyInfo.cs b/src/Orchard.Web/Modules/Orchard.MediaPicker/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..9dd1bc09b --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 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.MediaPicker")] +[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("d33b592c-133c-440b-8841-17e55eb08306")] + +// 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")] diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaBrowser.js b/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaBrowser.js new file mode 100644 index 000000000..0469532f9 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaBrowser.js @@ -0,0 +1,258 @@ +(function ($) { + var currentAspect = {}, + selectedItem, + suppressResize; + + $.extend({ + mediaPicker: { + init: function (data) { + // called by the opener to initiate dialog with existing image data. + // todo: data.img may contain existing image data, populate the fields + var img = data.img; + if (img) { + for (var name in img) { + $("#img-" + name).val(img[name]); + } + suppressResize = true; + $("#img-src").trigger("change"); + } + }, + uploadMedia: uploadMedia, + scalePreview: scalePreview + } + }); + + $("#img-cancel, #lib-cancel").live("click", function () { window.close(); }); + // when url changes, set the preview and loader src + $("#img-src").live("change", function () { + selectImage(getIdPrefix(this), this.value); + }); + $(".media-item").live("click", function () { + if (selectedItem) { + selectedItem.removeClass("selected"); + } + selectedItem = $(this); + selectedItem.addClass("selected"); + selectImage("#lib-", selectedItem.attr("data-imgsrc")); + }); + // maintain aspect ratio when width or height is changed + $("#img-width, #lib-width").live("change", fixAspectHeight); + $("#img-height, #lib-height").live("change", fixAspectWidth); + + $("#img-insert, #lib-insert").live("click", function () { + if ($(this).hasClass("disabled")) return; + publishInsertEvent(this); + }); + + $(function () { + $("#tabs").tabs({ selected: parseInt(location.hash.replace("#", "")) || 0 }); + + // populate width and height when image loads + // note: load event does not bubble so cannot be used with .live + $("#img-loader, #lib-loader").bind("load", syncImage); + + // edit mode has slightly different wording + // elements advertise this with data-edittext attributes, + // the edit text is the element's new val() unless -content is specified. + if (query("editmode") === "true") { + $("[data-edittext]").each(function () { + var self = $(this), + isContent = self.attr("data-edittext-content") === "true", + editText = self.attr("data-edittext"); + if (isContent) { + self.text(editText); + } + else { + self.attr("value", editText); + } + }); + } + + var data = window.mediaPickerData; + if (data) { + window.mediaPickerData = null; + $.mediaPicker.init(data); + } + }); + + function selectImage(prefix, src) { + $(prefix + "preview").width("").height("").attr("src", src); + $(prefix + "loader").attr("src", src); + $(prefix + "src").val(src); + + var disabled = src ? "" : "disabled"; + $(prefix + "insert").attr("disabled", disabled).toggleClass("disabled", !!disabled); + } + + function getIdPrefix(e) { + return "#" + e.id.substr(0, 4); + } + function publishInsertEvent(button) { + var prefix = getIdPrefix(button), + editorId = query("editorId"), + source = query("source"), + img = { + src: $(prefix + "src").val(), + alt: $(prefix + "alt").val(), + "class": $(prefix + "class").val(), + style: $(prefix + "style").val(), + align: $(prefix + "align").val(), + width: $(prefix + "width").val(), + height: $(prefix + "height").val() + }; + img.html = getImageHtml(img); + window.opener.OpenAjax.hub.publish("orchard.admin.pickimage-picked." + source, { + editorId: editorId, + img: img + }); + window.close(); + } + + function parseUnits(value) { + if (/\s*[0-9]+\s*(px)?\s*/i.test(value)) { + return parseInt(value); + } + return NaN; + } + + function fixAspectWidth() { + var prefix = getIdPrefix(this); + if (!$(prefix + "lock:checked").val()) return; + var height = parseUnits(this.value); + if (!isNaN(height)) { + $(prefix + "width").val(Math.round(height * currentAspect[prefix])); + } + } + + function fixAspectHeight() { + var prefix = getIdPrefix(this); + if (!$(prefix + "lock:checked").val()) return; + var width = parseUnits(this.value); + if (!isNaN(width)) { + $(prefix + "height").val(Math.round(width / currentAspect[prefix])); + } + } + + function scalePreview(img) { + // ensures the loaded image preview fits within the preview area + // by scaling it down if not. + var self = $(img), + width = self.width(), + height = self.height(), + aspect = width / height, + maxWidth = self.parent().width(), + maxHeight = self.parent().height(); + if (width > maxWidth) { + width = maxWidth; + height = Math.round(width / aspect); + } + if (height > maxHeight) { + height = maxHeight; + width = Math.round(width * aspect); + } + self.width(width).height(height); + } + + function syncImage() { + // when the image loader loads, we use it to calculate the current image + // aspect ratio, and update the width and height fields. + var prefix = getIdPrefix(this), + self = $(this), + width = self.width(), + height = self.height(); + currentAspect[prefix] = width / height; + // because we just loaded an edited image, leave the width/height + // at their configured values, not the natural size. + if (!suppressResize) { + $(prefix + "width").val(width); + $(prefix + "height").val(height); + } + suppressResize = false; + } + + function getAttr(name, value) { + // get an attribute value, escaping any necessary characters to html entities. + // not an exhastive list, but should cover all the necessary characters for this UI (e.g. you can't really put in newlines). + if (!value && name !== "alt") return ""; + return ' ' + name + '="' + value.replace(/&/g, "&").replace(/"/g, """).replace(//g, ">") + '"'; + } + + function getImageHtml(data) { + return html = '"; + } + + function uploadMedia(form) { + var name = "addmedia__" + (new Date()).getTime(); + $("