diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 94ac400be7..f599c1f051 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -350,6 +350,8 @@ + + diff --git a/src/Umbraco.Web.UI/umbraco_client/ContextMenu/Css/jquery.contextMenu.css b/src/Umbraco.Web.UI/umbraco_client/ContextMenu/Css/jquery.contextMenu.css new file mode 100644 index 0000000000..1a57a33180 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco_client/ContextMenu/Css/jquery.contextMenu.css @@ -0,0 +1,157 @@ +/*! + * jQuery contextMenu - Plugin for simple contextMenu handling + * + * Version: 1.5.22 + * + * Authors: Rodney Rehm, Addy Osmani (patches for FF) + * Web: http://medialize.github.com/jQuery-contextMenu/ + * + * Licensed under + * MIT License http://www.opensource.org/licenses/mit-license + * GPL v3 http://opensource.org/licenses/GPL-3.0 + * + */ + +.context-menu-list +{ + /*margin:0; + padding:0; + + min-width: 120px; + max-width: 250px; + display: inline-block; + position: absolute; + list-style-type: none; + + border: 1px solid #DDD; + background: #EEE; + + -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); + -moz-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); + -ms-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); + -o-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); + + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 11px;*/ + + background: #f0f0f0 url(../../Tree/Themes/umbraco/contextMenuBg.gif) repeat-y left; + border: 1px solid #979797; + padding: 2px 3px; + min-width: 130px; + max-width: 250px; + font-family: Arial,Lucida Grande; + font-size: 11px; + margin: 0px; + display: inline-block; + position: absolute; + list-style-type: none; +} + +.context-menu-item +{ + border-left: none; + background: transparent; + height: 26px; + line-height: 26px; + padding: 0 0 0 35px; + margin: 0; + border: 1px solid transparent; +} + +.context-menu-separator { + padding-bottom:0; + border-bottom: 1px solid #DDD; +} + +.context-menu-item > label > input, +.context-menu-item > label > textarea { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.context-menu-item.hover { + cursor: pointer; + background: #D5EFFC; + border:1px solid #99DEFD; +} + +.context-menu-item.disabled { + color: #666; +} + +.context-menu-input.hover, +.context-menu-item.disabled.hover { + cursor: default; + background-color: #EEE; +} + +.context-menu-submenu:after { + content: ">"; + color: #666; + position: absolute; + top: 0; + right: 3px; + z-index: 1; +} + +/* icons + #protip: + In case you want to use sprites for icons (which I would suggest you do) have a look at + http://css-tricks.com/13224-pseudo-spriting/ to get an idea of how to implement + .context-menu-item.icon:before {} + */ +.context-menu-item.icon { min-height: 18px; background-repeat: no-repeat; background-position: 4px 4px; } +.context-menu-item.icon-edit { background-image: url(../../../umbraco/images/copy.small.png); } +.context-menu-item.icon-cut { background-image: url(images/cut.png); } +.context-menu-item.icon-copy { background-image: url(images/page_white_copy.png); } +.context-menu-item.icon-paste { background-image: url(images/page_white_paste.png); } +.context-menu-item.icon-delete { background-image: url(../../../umbraco/images/delete.small.png); } +.context-menu-item.icon-add { background-image: url(images/page_white_add.png); } +.context-menu-item.icon-quit { background-image: url(images/door.png); } + +/* vertically align inside labels */ +.context-menu-input > label > * { vertical-align: top; } + +/* position checkboxes and radios as icons */ +.context-menu-input > label > input[type="checkbox"], +.context-menu-input > label > input[type="radio"] { + margin-left: -17px; +} +.context-menu-input > label > span { + margin-left: 5px; +} + +.context-menu-input > label, +.context-menu-input > label > input[type="text"], +.context-menu-input > label > textarea, +.context-menu-input > label > select { + display: block; + width: 100%; + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -o-box-sizing: border-box; + box-sizing: border-box; +} + +.context-menu-input > label > textarea { + height: 100px; +} +.context-menu-item > .context-menu-list { + display: none; + /* re-positioned by js */ + right: -5px; + top: 5px; +} + +.context-menu-item.hover > .context-menu-list { + display: block; +} + +.context-menu-accesskey { + text-decoration: underline; +} diff --git a/src/Umbraco.Web.UI/umbraco_client/ContextMenu/Js/jquery.contextMenu.js b/src/Umbraco.Web.UI/umbraco_client/ContextMenu/Js/jquery.contextMenu.js new file mode 100644 index 0000000000..2b4da5417d --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco_client/ContextMenu/Js/jquery.contextMenu.js @@ -0,0 +1,1585 @@ +/*! + * jQuery contextMenu - Plugin for simple contextMenu handling + * + * Version: 1.5.22 + * + * Authors: Rodney Rehm, Addy Osmani (patches for FF) + * Web: http://medialize.github.com/jQuery-contextMenu/ + * + * Licensed under + * MIT License http://www.opensource.org/licenses/mit-license + * GPL v3 http://opensource.org/licenses/GPL-3.0 + * + */ + +(function($, undefined){ + + // TODO: - + // ARIA stuff: menuitem, menuitemcheckbox und menuitemradio + // create
, , and of course . + * Everything else will be imported as an html node, which is not interfaced with contextMenu. + */ + + // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#concept-command + switch (nodeName) { + // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#the-menu-element + case 'menu': + item = {name: $node.attr('label'), items: {}}; + counter = menuChildren(item.items, $node.children(), counter); + break; + + // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-a-element-to-define-a-command + case 'a': + // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-button-element-to-define-a-command + case 'button': + item = { + name: $node.text(), + disabled: !!$node.attr('disabled'), + callback: (function(){ return function(){ $node.click(); }; })() + }; + break; + + // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-command-element-to-define-a-command + + case 'menuitem': + case 'command': + switch ($node.attr('type')) { + case undefined: + case 'command': + case 'menuitem': + item = { + name: $node.attr('label'), + disabled: !!$node.attr('disabled'), + callback: (function(){ return function(){ $node.click(); }; })() + }; + break; + + case 'checkbox': + item = { + type: 'checkbox', + disabled: !!$node.attr('disabled'), + name: $node.attr('label'), + selected: !!$node.attr('checked') + }; + break; + + case 'radio': + item = { + type: 'radio', + disabled: !!$node.attr('disabled'), + name: $node.attr('label'), + radio: $node.attr('radiogroup'), + value: $node.attr('id'), + selected: !!$node.attr('checked') + }; + break; + + default: + item = undefined; + } + break; + + case 'hr': + item = '-------'; + break; + + case 'input': + switch ($node.attr('type')) { + case 'text': + item = { + type: 'text', + name: label || inputLabel(node), + disabled: !!$node.attr('disabled'), + value: $node.val() + }; + break; + + case 'checkbox': + item = { + type: 'checkbox', + name: label || inputLabel(node), + disabled: !!$node.attr('disabled'), + selected: !!$node.attr('checked') + }; + break; + + case 'radio': + item = { + type: 'radio', + name: label || inputLabel(node), + disabled: !!$node.attr('disabled'), + radio: !!$node.attr('name'), + value: $node.val(), + selected: !!$node.attr('checked') + }; + break; + + default: + item = undefined; + break; + } + break; + + case 'select': + item = { + type: 'select', + name: label || inputLabel(node), + disabled: !!$node.attr('disabled'), + selected: $node.val(), + options: {} + }; + $node.children().each(function(){ + item.options[this.value] = $(this).text(); + }); + break; + + case 'textarea': + item = { + type: 'textarea', + name: label || inputLabel(node), + disabled: !!$node.attr('disabled'), + value: $node.val() + }; + break; + + case 'label': + break; + + default: + item = {type: 'html', html: $node.clone(true)}; + break; + } + + if (item) { + counter++; + items['key' + counter] = item; + } + }); + + return counter; +} + +// convert html5 menu +$.contextMenu.fromMenu = function(element) { + var $this = $(element), + items = {}; + + menuChildren(items, $this.children()); + + return items; +}; + +// make defaults accessible +$.contextMenu.defaults = defaults; +$.contextMenu.types = types; + +})(jQuery); diff --git a/src/Umbraco.Web.UI/umbraco_client/FolderBrowser/Js/folderbrowser.js b/src/Umbraco.Web.UI/umbraco_client/FolderBrowser/Js/folderbrowser.js index d8af24117b..b0ce3788b2 100644 --- a/src/Umbraco.Web.UI/umbraco_client/FolderBrowser/Js/folderbrowser.js +++ b/src/Umbraco.Web.UI/umbraco_client/FolderBrowser/Js/folderbrowser.js @@ -1,7 +1,7 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls"); -(function ($, Base) { +(function ($, Base, window, document, undefined) { Umbraco.Controls.FolderBrowser = Base.extend({ @@ -12,62 +12,127 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls"); _opts: null, _viewModel: null, - _getChildNodes: function () { - _this = this; + _getChildNodes: function () + { + var self = this; - $.getJSON(_this._opts.basePath + "/FolderBrowserService/GetChildNodes/" + _this._parentId + "/" + _this._viewModel.filterTerm(), function(data) { + $.getJSON(self._opts.basePath + "/FolderBrowserService/GetChildNodes/" + self._parentId + "/" + self._viewModel.filterTerm(), function (data) { if (data != undefined && data.length > 0) { - ko.mapping.fromJS(data, {}, _this._viewModel.items); + ko.mapping.fromJS(data, {}, self._viewModel.items); } else { - _this._viewModel.items([]); + self._viewModel.items([]); } }); }, - // Constructor - constructor: function (el, opts) { + _getItemById: function (id) + { + var self = this; + + var results = ko.utils.arrayFilter(self._viewModel.items(), function (item) { + return item.Id() === id; + }); - _this = this; + return results.length == 1 ? results[0] : null; + }, + + _deleteItem: function (id) + { + var self = this; - // Store el info - this._el = el; - this._elId = el.id; + var item = self._getItemById(id); + if (item === null) + throw Error("No item found with the id: " + id); - // Grab parent id from element - this._parentId = $(el).data("parentid"); + if (confirm(window.top.uiKeys['defaultdialogs_confirmdelete'] + ' "' + item.Name() + '"?\n\n')) + { + $(window.top).trigger("nodeDeleting", []); - // Merge options with default - this._opts = $.extend({ - // Default options go here - }, opts); + var safePath = "," + item.Path() + ","; + if (safePath.indexOf(",-20,") != -1 || safePath.indexOf(",-21,") != -1) + { + window.top.umbraco.presentation.webservices.legacyAjaxCalls.DeleteContentPermanently( + item.Id(), + "media", + function () { + //raise nodeDeleted event + $(window.top).trigger("nodeDeleted", []); + + //TODO: Reload current open node in tree + + // Reload nodes + self._getChildNodes(); + }); + } + else + { + window.top.umbraco.presentation.webservices.legacyAjaxCalls.Delete( + item.Id(), "", + "media", + function() { + //raise nodeDeleted event + $(window.top).trigger("nodeDeleted", []); + + //TODO: Reload current open node in tree + + // Reload nodes + self._getChildNodes(); + }, + function(error) { + //raise public error event + $(window.top).trigger("publicError", [error]); + + //TODO: Reload current open node in tree + + // Reload nodes + self._getChildNodes(); + }); + } + } + }, + + _initViewModel: function () + { + var self = this; + // Setup the viewmode; - this._viewModel = $.extend({}, { - parent: this, + self._viewModel = $.extend({}, { + parent: self, filterTerm: ko.observable(''), items: ko.observableArray([]), queued: ko.observableArray([]) }); - this._viewModel.filterTerm.subscribe(function(newValue) { - _this._getChildNodes(); + self._viewModel.filterTerm.subscribe(function (newValue) { + self._getChildNodes(); }); - + }, + + _initToolbar: function () + { + var self = this; + // Inject the upload button into the toolbar var button = $(""); - button.click(function(e) { + button.click(function (e) { e.preventDefault(); $(".upload-overlay").show(); }); - - $(".tabpage:first-child .menubar td[id$='tableContainerButtons'] .sl nobr").after(button); + $(".tabpage:first-child .menubar td[id$='tableContainerButtons'] .sl nobr").after(button); + }, + + _initOverlay: function () + { + var self = this; + // Inject the upload overlay var instructions = 'draggable' in document.createElement('span') ? "Drag files here to upload \ Or, click the button below to chose the items to upload" : "Click the browse button below to chose the items to upload"; - + var overlay = $("" + "" + instructions + @@ -85,7 +150,7 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls"); ""); $("body").prepend(overlay); - + // Create uploader $("#fileupload").fileUploader({ dropTarget: ".upload-overlay", @@ -99,43 +164,43 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls"); size: data.size, progress: ko.observable(data.progress), cancel: function () { - if(this.progress() < 100) + if (this.progress() < 100) $("#fileupload").fileUploader("cancelItem", this.itemId); else - _this._viewModel.queued.remove(this); + self._viewModel.queued.remove(this); } }; - + // Store item back in context for easy access later - data.context = file; - + data.context = file; + // Push bindable item into queue - _this._viewModel.queued.push(file); + self._viewModel.queued.push(file); }, onDone: function (data) { - switch(data.status) { + switch (data.status) { case 'success': - //_this._viewModel.queued.remove(data.context); + //self._viewModel.queued.remove(data.context); break; case 'error': - _this._viewModel.queued.remove(data.context); + self._viewModel.queued.remove(data.context); break; case 'canceled': - _this._viewModel.queued.remove(data.context); + self._viewModel.queued.remove(data.context); break; } }, - onProgress: function(data) { + onProgress: function (data) { data.context.progress(data.progress); } }); - + // Hook up uploader buttons - $(".upload-overlay .upload").click(function(e) { + $(".upload-overlay .upload").click(function (e) { e.preventDefault(); $("#fileupload").fileUploader("uploadAll"); }); - + $(".upload-overlay .cancel").click(function (e) { e.preventDefault(); $("#fileupload").fileUploader("cancelAll"); @@ -145,23 +210,69 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls"); $(".umbFolderBrowser").live('dragenter dragover', function (e) { $(".upload-overlay").show(); }); - + $(".upload-overlay").live('dragleave dragexit', function (e) { $(this).hide(); - }).click(function() { + }).click(function () { $(this).hide(); }); - $(".upload-panel").click(function(e) { + $(".upload-panel").click(function (e) { e.stopPropagation(); }); + }, + + _initContextMenu: function () + { + var self = this; + + // Setup context menus + $.contextMenu({ + selector: '.umbFolderBrowser .items li', + callback: function (key, options) { + var id = options.$trigger.data("id"); + switch (key) { + case "delete": + self._deleteItem(id); + break; + } + }, + items: { + "edit": { name: "Edit", icon: "edit" }, + "delete": { name: "Delete", icon: "delete" } + }, + animation: { show: "fadeIn", hide: "fadeOut" } + }); + }, + + // Constructor + constructor: function (el, opts) + { + var self = this; + + // Store el info + self._el = el; + self._elId = el.id; + + // Grab parent id from element + self._parentId = $(el).data("parentid"); + + // Merge options with default + self._opts = $.extend({ + // Default options go here + }, opts); + + self._initViewModel(); + self._initToolbar(); + self._initOverlay(); + self._initContextMenu(); // Bind the viewmodel - ko.applyBindings(this._viewModel, el); - ko.applyBindings(this._viewModel, overlay.get(0)); + ko.applyBindings(self._viewModel, el); + ko.applyBindings(self._viewModel, $(".upload-overlay").get(0)); // Grab children media items - this._getChildNodes(); + self._getChildNodes(); } // Public @@ -195,4 +306,4 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls"); return $(this).data("api"); }; -})(jQuery, base2.Base) \ No newline at end of file +})(jQuery, base2.Base, window, document) \ No newline at end of file diff --git a/src/Umbraco.Web/UI/Controls/FolderBrowser.cs b/src/Umbraco.Web/UI/Controls/FolderBrowser.cs index 87df2a4859..0e3a30ea15 100644 --- a/src/Umbraco.Web/UI/Controls/FolderBrowser.cs +++ b/src/Umbraco.Web/UI/Controls/FolderBrowser.cs @@ -10,12 +10,14 @@ using umbraco.cms.businesslogic.media; namespace Umbraco.Web.UI.Controls { - [ClientDependency(ClientDependencyType.Css, "FolderBrowser/css/folderbrowser.css", "UmbracoClient")] + [ClientDependency(ClientDependencyType.Css, "ContextMenu/Css/jquery.contextMenu.css", "UmbracoClient")] + [ClientDependency(ClientDependencyType.Css, "FolderBrowser/Css/folderbrowser.css", "UmbracoClient")] [ClientDependency(ClientDependencyType.Javascript, "ui/jquery.js", "UmbracoClient", Priority = 1)] [ClientDependency(ClientDependencyType.Javascript, "ui/base2.js", "UmbracoClient", Priority = 1)] [ClientDependency(ClientDependencyType.Javascript, "ui/knockout.js", "UmbracoClient", Priority = 2)] [ClientDependency(ClientDependencyType.Javascript, "ui/knockout.mapping.js", "UmbracoClient", Priority = 3)] - [ClientDependency(ClientDependencyType.Javascript, "FileUploader/js/jquery.fileUploader.js", "UmbracoClient", Priority = 3)] + [ClientDependency(ClientDependencyType.Javascript, "ContextMenu/Js/jquery.contextMenu.js", "UmbracoClient", Priority = 3)] + [ClientDependency(ClientDependencyType.Javascript, "FileUploader/js/jquery.fileUploader.js", "UmbracoClient", Priority = 4)] [ClientDependency(ClientDependencyType.Javascript, "FolderBrowser/js/folderbrowser.js", "UmbracoClient", Priority = 10)] [ToolboxData("<{0}:FolderBrowser runat=server>{0}:FolderBrowser>")] public class FolderBrowser : WebControl @@ -112,7 +114,7 @@ namespace Umbraco.Web.UI.Controls // Create thumbnails container sb.Append("" + - "" + + "" + ""); panel.Controls.Add(new LiteralControl(sb.ToString())); diff --git a/src/Umbraco.Web/WebServices/FolderBrowserService.cs b/src/Umbraco.Web/WebServices/FolderBrowserService.cs index b42d6ef014..60c3e8b45b 100644 --- a/src/Umbraco.Web/WebServices/FolderBrowserService.cs +++ b/src/Umbraco.Web/WebServices/FolderBrowserService.cs @@ -49,6 +49,7 @@ namespace Umbraco.Web.WebServices data.Add(new { Id = child.Id, + Path = child.Path, Name = child.Text, MediaTypeAlias = child.ContentType.Alias, EditUrl = string.Format("editMedia.aspx?id={0}", child.Id),
Or, click the button below to chose the items to upload