This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resource-editor.git
commit 6b1ffb3be7ff07782a3d34593869a6317ac6fd3c Author: Sandro Boehme <[email protected]> AuthorDate: Mon Jan 26 23:40:28 2015 +0000 SLING-4239 (Resource Editor :: improved add node dialog, added cheat sheets, new background image) patch applied git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1654912 13f79535-47bb-0310-9956-ffa450edef68 --- src/main/img/info.png | Bin 0 -> 3943 bytes .../ResourceProviderBasedResourceDecorator.java | 6 +- src/main/less/reseditor/forms.less | 5 + src/main/less/reseditor/reseditor.less | 46 +++- .../resource-editor-static-content/css/select2.png | Bin 0 -> 613 bytes .../img/info_black.png | Bin 0 -> 271 bytes .../img/info_darkgray.png | Bin 0 -> 737 bytes .../img/info_lightgray.png | Bin 0 -> 380 bytes .../js/reseditor/MainController.js | 80 +++++- .../js/reseditor/tree/AddNodeController.js | 292 +++++++++++++++------ .../js/reseditor/tree/JSTreeAdapter.js | 15 +- .../js/reseditor/tree/TreeController.js | 50 +++- .../SLING-INF/libs/sling/resource-editor/html.jsp | 181 ++++++++----- .../libs/sling/resource-editor/nodes.json.incl.jsp | 35 +-- .../libs/sling/resource-editor/rootnodes.json.jsp | 42 +-- .../libs/sling/resource-editor/servlet-nodes.json | 7 + 16 files changed, 551 insertions(+), 208 deletions(-) diff --git a/src/main/img/info.png b/src/main/img/info.png new file mode 100644 index 0000000..05ce264 Binary files /dev/null and b/src/main/img/info.png differ diff --git a/src/main/java/org/apache/sling/reseditor/resource/ResourceProviderBasedResourceDecorator.java b/src/main/java/org/apache/sling/reseditor/resource/ResourceProviderBasedResourceDecorator.java index 6ca9ebf..813ddd7 100644 --- a/src/main/java/org/apache/sling/reseditor/resource/ResourceProviderBasedResourceDecorator.java +++ b/src/main/java/org/apache/sling/reseditor/resource/ResourceProviderBasedResourceDecorator.java @@ -70,8 +70,10 @@ public class ResourceProviderBasedResourceDecorator implements ResourceDecorator private Resource getResourceEditorResourceWrapper(Resource resource, String resolutionPathInfo) { Resource result = null; ResourceMetadata resourceMetadata = resource.getResourceMetadata(); - boolean isResourceEditorProviderResource = resourceMetadata != null ? resourceMetadata.containsKey(ResEditorResourceProvider.RESOURCE_EDITOR_PROVIDER_RESOURCE) : false; - if (resolutionPathInfo != null && isResourceEditorProviderResource) { + boolean isResourceEditorProviderResource = resourceMetadata != null ? resourceMetadata.containsKey(ResEditorResourceProvider.RESOURCE_EDITOR_PROVIDER_RESOURCE) : false; + boolean isHTMLResource = resolutionPathInfo != null && resolutionPathInfo.endsWith("html"); + boolean isJSONResource = resolutionPathInfo != null && resolutionPathInfo.endsWith("json"); + if ((isHTMLResource || isJSONResource) && isResourceEditorProviderResource) { result = new ResourceWrapper(resource) { @Override public String getResourceType() { diff --git a/src/main/less/reseditor/forms.less b/src/main/less/reseditor/forms.less index 61e5520..86e5c90 100644 --- a/src/main/less/reseditor/forms.less +++ b/src/main/less/reseditor/forms.less @@ -29,4 +29,9 @@ .form-group { margin-bottom: 0; +} + +// vertical-form is a custom class +.modal-body.vertical-form label { + display: block; } \ No newline at end of file diff --git a/src/main/less/reseditor/reseditor.less b/src/main/less/reseditor/reseditor.less index b93634b..28c1e2a 100755 --- a/src/main/less/reseditor/reseditor.less +++ b/src/main/less/reseditor/reseditor.less @@ -73,9 +73,12 @@ body background: -o-linear-gradient(-45deg, rgba(0,0,0,0.4) 0%,rgba(0,0,0,0.7) 100%); /* Opera 11.10+ */ background: -ms-linear-gradient(-45deg, rgba(0,0,0,0.4) 0%,rgba(0,0,0,0.7) 100%); /* IE10+ */ background: linear-gradient(-45deg, rgba(0,0,0,0.4) 0%,rgba(0,0,0,0.7) 100%); /* W3C */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#a0000000', endColorstr='#c9000000',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */ } +.ie9filter-plate-div { + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#a0000000', endColorstr='#c9000000',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */ + height: 100%; +} .plate-box-shadow { box-shadow: 5px 5px 5px #888; -moz-box-shadow: 5px 5px 5px #888; @@ -101,7 +104,7 @@ body .plate { .plate-background; .plate-box-shadow; - .rounded; + border-radius: 7px; .plate-text-shadow; overflow: hidden; @@ -305,4 +308,43 @@ input.jstree-rename-input{ .jstree-default .jstree-anchor { height: 22px; line-height: 20px; +} + +.info-content-container { + padding: 15px; + text-shadow: none; +} + +#addNodeDialog .info-icon { + margin-right: 10px; + margin-top: 4px; +} +#tree-info-icon { + margin-right: 10px; + margin-top: 5px; +} + +.info-icon { + height: 15px; + width: 16px; + display: inline-block; +} +.info-icon-dark { + background-image: url(../img/info_darkgray.png); +} +.info-icon-dark:hover { + background-image: url(../img/info_black.png); +} + +.info-icon-lightgray { + background-image: url(../img/info_lightgray.png); +} +.info-icon-lightgray:hover { + background-image: url(../img/info_black.png); +} +.modal-body.vertical-form .form-group { + margin-bottom: 10px; +} +#addNodeDialog .form-group label a{ + font-weight: normal; } \ No newline at end of file diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/css/select2.png b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/css/select2.png new file mode 100644 index 0000000..1d804ff Binary files /dev/null and b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/css/select2.png differ diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/img/info_black.png b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/img/info_black.png new file mode 100644 index 0000000..46f5124 Binary files /dev/null and b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/img/info_black.png differ diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/img/info_darkgray.png b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/img/info_darkgray.png new file mode 100644 index 0000000..6d81336 Binary files /dev/null and b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/img/info_darkgray.png differ diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/img/info_lightgray.png b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/img/info_lightgray.png new file mode 100644 index 0000000..76d078a Binary files /dev/null and b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/img/info_lightgray.png differ diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/MainController.js b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/MainController.js index bc9fd51..538cbbe 100644 --- a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/MainController.js +++ b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/MainController.js @@ -47,12 +47,88 @@ org.apache.sling.reseditor.MainController = (function() { }); }; + // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys + if (!Object.keys) { + Object.keys = (function() { + 'use strict'; + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'), + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ], + dontEnumsLength = dontEnums.length; + + return function(obj) { + if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); + } + + var result = [], prop, i; + + for (prop in obj) { + if (hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + + if (hasDontEnumBug) { + for (i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + return result; + }; + }()); + }; + + /* adding an indexOf function if it's not available */ + if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { + "use strict"; + if (this == null) { + throw new TypeError(); + } + var t = Object(this); + var len = t.length >>> 0; + if (len === 0) { + return -1; + } + var n = 0; + if (arguments.length > 1) { + n = Number(arguments[1]); + if (n != n) { // shortcut for verifying if it's NaN + n = 0; + } else if (n != 0 && n != Infinity && n != -Infinity) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + } + if (n >= len) { + return -1; + } + var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); + for (; k < len; k++) { + if (k in t && t[k] === searchElement) { + return k; + } + } + return -1; + } + } + MainController.prototype.getNodeTypes = function(){ return this.settings.nodeTypes; } MainController.prototype.getContextPath = function(){ - return this.settings.contextPath; + return this.settings.contextPath=="" ? "/" : this.settings.contextPath; } MainController.prototype.encodeURL = function(unencodedURL){ @@ -110,4 +186,4 @@ org.apache.sling.reseditor.MainController = (function() { } return MainController; -}()); \ No newline at end of file +}()); diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/tree/AddNodeController.js b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/tree/AddNodeController.js index 1f63018..4aabe8e 100644 --- a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/tree/AddNodeController.js +++ b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/tree/AddNodeController.js @@ -31,109 +31,239 @@ org.apache.sling.reseditor = org.apache.sling.reseditor || {}; org.apache.sling.reseditor.AddNodeController = (function() { function AddNodeController(settings, mainController){ - var thatAddNodeController = this; this.settings = settings; this.mainController = mainController; this.lastAddNodeURL = ""; + this.dialogShown = false; + this.showAllNodeTypes = false; + this.nodeTypeObjects = []; + this.nodeType=""; + var thatAddNodeController = this; $(document).ready(function() { $('#addNodeDialog .submit').click(function(){ thatAddNodeController.addNode(); }); - - var nodeTypeObjects = jQuery.map( mainController.getNodeTypes(), function( nt, i ) { - return {id: nt, text: nt}; + $('#addNodeDialog').on('shown.bs.modal', function () { + thatAddNodeController.dialogShown = true; + $('#nodeName').select2("open"); + }) + $('#addNodeDialog').on('hide.bs.modal', function () { + thatAddNodeController.dialogShown = false; + }) + $('#addNodeDialog .info-icon').click(function () { + $('#addNodeDialog .info-content').slideToggle(); }); - - $("#nodeType").select2({ - placeholder: "Node Type", - allowClear: true, - data: nodeTypeObjects + $('#addNodeDialog .info-content .close').click(function () { + $('#addNodeDialog .info-content').slideToggle(); + }); + $('#addNodeDialog .nt-toggle').click(function () { + thatAddNodeController.toggleApplicableNodeTypes(); + }); + $("body").on('keydown', function (e) { + // see http://www.javascripter.net/faq/keycodes.htm + var aKey = 65; + if (e.ctrlKey && aKey==e.which) { + if (thatAddNodeController.dialogShown){ + thatAddNodeController.addNode(); + } + } }) + }); + }; + + AddNodeController.prototype.addNode = function() { + var thatAddNodeController = this; + var nodeName = $("#nodeName").val().trim(); + var nodeType = $("#nodeType").val(); + var resourceTypeData = $("#resourceType").select2('data'); + var resourceType = resourceTypeData != null ? resourceTypeData.text.trim() : ""; + + var data = {"_charset_": "utf-8"}; + if ("" != nodeType){ + data["jcr:primaryType"] = nodeType; + } + var canAddResourceType = nodeType == "" ? true : this.mainController.ntManager.getNodeType(nodeType).canAddProperty("sling:resourceType", "String"); + if ("" != resourceType && canAddResourceType){ + data["sling:resourceType"] = resourceType; + } + var targetURL = (this.lastAddNodeURL=="/") ? "/" : this.lastAddNodeURL+"/"; + targetURL = this.mainController.decodeFromHTML(targetURL); + if ("" != nodeName) { + targetURL += nodeName; + } + if (targetURL=="/"){ + //adding a node without a specified name to the root node + targetURL = "/*"; + } + var encodedTargetURL = this.mainController.encodeURL(targetURL); - function format(element) { - return "<span><span class=\"search-choice-close\"></span>"+element.text+"</span>"; + $.ajax({ + type: 'POST', + url: encodedTargetURL, + dataType: "json", + data: data + }) + .done(function() { + $('#addNodeDialog').modal("hide"); + var htmlDecodedLastAddNodeURL = thatAddNodeController.mainController.decodeFromHTML(thatAddNodeController.lastAddNodeURL); + thatAddNodeController.mainController.redirectTo(htmlDecodedLastAddNodeURL); + }) + .fail(function(errorJson) { + $('#addNodeDialog').modal("hide"); + thatAddNodeController.mainController.displayAlert(errorJson); + }); + + } + + AddNodeController.prototype.toggleApplicableNodeTypes = function() { + if (this.showAllNodeTypes){ + this.showAllNodeTypes=false; + $('#addNodeDialog .form-group.node-type .nt-dependency-description').text("applicable together with node name"); + $('#addNodeDialog .form-group.node-type .nt-toggle').text("show generally applicable"); + } else { + this.showAllNodeTypes=true; + $('#addNodeDialog .form-group.node-type .nt-dependency-description').text("generally applicable"); + $('#addNodeDialog .form-group.node-type .nt-toggle').text("show applicable together with node name"); + } + var nodeType = mainController.ntManager.getNodeType(this.nodeTypeName); + var appliCnTypesByNodeName = nodeType.getApplicableCnTypesPerCnDef(false /*include mixins*/); + var nodeNameList = Object.keys(appliCnTypesByNodeName); + this.nodeTypeObjects = getNodeTypesByDependenyState.call(this, nodeNameList, appliCnTypesByNodeName, this.nodeTypeObjects); + } + + function getNodeTypesByDependenyState(nodeNameList, appliCnTypesByNodeName, nodeTypeObjects){ + var allAppliCnTypes = getAllApplicableCnTypesSorted(nodeNameList, appliCnTypesByNodeName); + if (this.showAllNodeTypes){ + return jQuery.map( allAppliCnTypes, function( nt, i ) { + return {id: nt, text: nt}; + }); + } else { + return getNodeTypesByNodeName(appliCnTypesByNodeName, nodeTypeObjects, allAppliCnTypes); + } + } + + function getAllApplicableCnTypesSorted(nodeNameList, appliCnTypesByNodeName){ + var allAppliCnTypes = []; + for (var nodeNameIndex in nodeNameList) { + var nodeName = nodeNameList[nodeNameIndex]; + for (var nodeType in appliCnTypesByNodeName[nodeName]){ + if (allAppliCnTypes.indexOf(nodeType)<0){ + allAppliCnTypes.push(nodeType); + } } + } + allAppliCnTypes.sort(); + return allAppliCnTypes; + } + + function getNodeTypesByNodeName(appliCnTypesByNodeName, nodeTypeObjects, allAppliCnTypes){ + var nodeName = $("#nodeName").val(); + if ("" === nodeName || typeof appliCnTypesByNodeName[nodeName] === "undefined"){ + return nodeTypeObjects = jQuery.map(allAppliCnTypes, function( nt, i ) { + return {id: nt, text: nt}; + }); + } else if (typeof appliCnTypesByNodeName[nodeName] != "undefined"){ + var nodeTypes = Object.keys(appliCnTypesByNodeName[nodeName]); + return nodeTypeObjects = jQuery.map(nodeTypes, function( nt, i ) { + return {id: nt, text: nt}; + }); + } else if (typeof appliCnTypesByNodeName["*"] != "undefined"){ + var nodeTypes = Object.keys(appliCnTypesByNodeName["*"]); + return nodeTypeObjects = jQuery.map(nodeTypes, function( nt, i ) { + return {id: nt, text: nt}; + }); + } +// wenn node name leer, dann alle nt anzeigen + } + + AddNodeController.prototype.openAddNodeDialog = function(resourcePath, nodeTypeName) { + var thatAddNodeController = this; + var resTypeHelpElement = $('#addNodeDialog .resource-type-not-allowed'); + resTypeHelpElement.hide(); + this.nodeTypeName = nodeTypeName; + var nodeType = mainController.ntManager.getNodeType(this.nodeTypeName); + var appliCnTypesByNodeName = nodeType.getApplicableCnTypesPerCnDef(false /*include mixins*/); + var nodeNameListStar = Object.keys(appliCnTypesByNodeName); + var nodeHelpElement = $('#addNodeDialog .only-listed-node-names-allowed'); + var indexOfResidualDef = nodeNameListStar.indexOf("*"); + var residualsDefined = indexOfResidualDef >= 0; + if (residualsDefined){ + nodeNameListStar.splice(indexOfResidualDef, 1); + nodeHelpElement.parents(".form-group").addClass("has-warning"); + nodeHelpElement.hide(); + } else { + nodeHelpElement.parents(".form-group").addClass("has-warning"); + nodeHelpElement.show(); + } + nodeNameListStar.sort(); + var nodeNameObjects = jQuery.map(nodeNameListStar, function( nt, i ) { + return {id: nt, text: nt}; + }); + $('#nodeName').select2('data', null); + $("#nodeName").select2({ + placeholder: "Enter or select a node name", + allowClear: true, + data: nodeNameObjects, + createSearchChoice: function(searchTerm){ + return {id:searchTerm, text:searchTerm}; + } + }); + + var nodeNameList = Object.keys(appliCnTypesByNodeName); + nodeNameList.sort(); + thatAddNodeController.nodeTypeObjects = getNodeTypesByDependenyState.call(thatAddNodeController, nodeNameList, appliCnTypesByNodeName, thatAddNodeController.nodeTypeObjects); + $("#nodeName").on("change", function(e) { + thatAddNodeController.nodeTypeObjects = getNodeTypesByDependenyState.call(thatAddNodeController, nodeNameList, appliCnTypesByNodeName, thatAddNodeController.nodeTypeObjects); + }); + + $("#nodeType").on("change", function(e) { + var nodeTypeName = $("#nodeType").val(); + var nodeType = mainController.ntManager.getNodeType(nodeTypeName); + var canAddResourceType = nodeType.canAddProperty("sling:resourceType", "String"); + if (canAddResourceType){ + resTypeHelpElement.hide(); + } else { + resTypeHelpElement.parents(".form-group").addClass("has-warning"); + resTypeHelpElement.show(); + } + }); + + + $('#nodeType').select2('data', null); + $("#nodeType").select2({ + placeholder: "Select a node type", + allowClear: true, + data: function() { + return { results: thatAddNodeController.nodeTypeObjects } ; // Use the global variable to populate the list + } + }); + + $('#addNodeDialog').modal('show'); + + var contextPath = this.mainController.getContextPath() == "/" && resourcePath.length>0 && resourcePath.charAt(0)=="/" ? "" : this.mainController.getContextPath(); + this.lastAddNodeURL = contextPath+resourcePath; - var data=[]; + $('#resourceType').select2('data', null); + var contextPath = this.mainController.getContextPath(); + contextPath = "/" === contextPath ? "" : contextPath; + var url = contextPath+"/libs/sling/resource-editor/servlet-nodes/resource-types.json"; + $.getJSON(url, function( origData ) { + var data = jQuery.map( origData, function( n, i ) { + return ( {id:i, text:n} ); + }); + var select2 = $("#resourceType").select2({ - placeholder: "Resource Type", + placeholder: "Enter or select a resource type", allowClear: true, - formatResult: format, data: data, createSearchChoice: function(searchTerm){ return {id:searchTerm, text:searchTerm}; } }).data("select2"); - - // To get called on a click in the result list: - // http://stackoverflow.com/a/15637696/1743551 - select2.onSelect = (function(fn) { - return function(data, options) { - var target; - - if (options != null) { - target = $(options.target); - } - - if (target && target.hasClass('search-choice-close')) { - alert('click!'); - } else { - return fn.apply(this, arguments); - } - } - })(select2.onSelect); }); - - AddNodeController.prototype.addNode = function() { - var thatAddNodeController = this; - var nodeName = $("#nodeName").val().trim(); - var nodeType = $("#nodeType").val(); - var resourceType = $("#resourceType").val().trim(); - - var data = {"_charset_": "utf-8"}; - if ("" != nodeType){ - data["jcr:primaryType"] = nodeType; - } - if ("" != resourceType){ - data["sling:resourceType"] = resourceType; - } - var targetURL = (this.lastAddNodeURL=="/") ? "/" : this.lastAddNodeURL+"/"; - targetURL = this.mainController.decodeFromHTML(targetURL); - if ("" != nodeName) { - targetURL += nodeName; - } - if (targetURL=="/"){ - //adding a node without a specified name to the root node - targetURL = "/*"; - } - var encodedTargetURL = this.mainController.encodeURL(targetURL); - - $.ajax({ - type: 'POST', - url: encodedTargetURL, - dataType: "json", - data: data - }) - .done(function() { - $('#addNodeDialog').modal("hide"); - var htmlDecodedLastAddNodeURL = thatAddNodeController.mainController.decodeFromHTML(thatAddNodeController.lastAddNodeURL); - thatAddNodeController.mainController.redirectTo(htmlDecodedLastAddNodeURL); - }) - .fail(function(errorJson) { - $('#addNodeDialog').modal("hide"); - thatAddNodeController.mainController.displayAlert(errorJson); - }); - - } - }; - - AddNodeController.prototype.openAddNodeDialog = function(resourcePath) { - $('#addNodeDialog').modal({}); - var contextPath = this.mainController.getContextPath() == "/" && resourcePath=="/" ? "" : this.mainController.getContextPath(); - this.lastAddNodeURL = contextPath+resourcePath; } return AddNodeController; -}()); \ No newline at end of file +}()); diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/tree/JSTreeAdapter.js b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/tree/JSTreeAdapter.js index 9e3d4e1..cbe5103 100644 --- a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/tree/JSTreeAdapter.js +++ b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/tree/JSTreeAdapter.js @@ -66,6 +66,9 @@ $(document).ready(function() { treeController.openElement($("#tree > ul > li[nodename=''] > ul"), paths); } selectingNodeWhileOpeningTree=false; + // position the info-icon + $('#tree-info-icon').show(); + $('#root i:first').before($('#tree-info-icon')); }) // call `.jstree` with the options object .jstree({ @@ -98,6 +101,7 @@ $(document).ready(function() { "check_move" : function (m) { // you find the member description here // http://www.jstree.com/documentation/core.html#_get_move + // TODO refactor to the new jsTree version var src_li = m.o; var src_nt = mainController.getNTFromLi(src_li); var src_nodename = src_li.attr("nodename"); @@ -131,7 +135,7 @@ $(document).ready(function() { treeController.renameNode(e, data); }).bind("move_node.jstree", function (e, data) { // see http://www.jstree.com/documentation/core ._get_move() - // refactor to the new jsTree version + // TODO refactor to the new jsTree version var src_li = data.rslt.o; var src_path = ""+settings.contextPath+src_li.children("a").attr("target"); var dest_li = data.rslt.np; // new parent .cr - same as np, but if a root node is created this is -1 @@ -159,11 +163,8 @@ $(document).ready(function() { }); }).on('hover_node.jstree', function (event, nodeObj) { $('#'+nodeObj.node.id+' a:first').focus(); - }).on('keydown.jstree', '.jstree-anchor', function (e) { - // see http://www.javascripter.net/faq/keycodes.htm - if (46==e.which) { - treeController.deleteNodes(); - } + }).on('keydown.jstree', 'a.jstree-anchor', function (e) { + treeController.configureKeyListeners(e); }).on('select_node.jstree', function (e, data) { ; }); @@ -171,4 +172,4 @@ $(document).ready(function() { }; return JSTreeAdapter; -}()); \ No newline at end of file +}()); diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/tree/TreeController.js b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/tree/TreeController.js index d073c16..2bd9c28 100644 --- a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/tree/TreeController.js +++ b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/reseditor/tree/TreeController.js @@ -41,23 +41,48 @@ org.apache.sling.reseditor.TreeController = (function() { this.addNodeController = new org.apache.sling.reseditor.AddNodeController(addNodeControllerSettings, mainController); $(document).ready(function() { - $("#tree").on("click", "li.jstree-node>a.jstree-anchor>i.open-icon",function(e, data) { - thatTreeController.openNodeTarget(e); + $("#tree").on("click", "#root",function(e) { + var target = $(e.target); + if (target.hasClass("open-icon")){ + thatTreeController.openNodeTarget(e); + } else if (target.hasClass("add-icon")){ + thatTreeController.openAddNodeDialog(target.parents("li")); + }else if (target.hasClass("remove-icon")){ + thatTreeController.deleteSingleNode(target.parents("li")); + } }); - $("#tree").on("click", "li.jstree-node>a.jstree-anchor>i.add-icon",function(e, data) { - thatTreeController.openAddNodeDialog($(e.target).parents("li")); + $("#tree").on("dblclick", "#root",function(e) { + var target = $(e.target); + if (target.hasClass("jstree-anchor") || target.hasClass("node-type")){ + var id = target.parents("li:first").attr("id"); + thatTreeController.openRenameNodeDialog(id); + } }); - $("#tree").on("click", "li.jstree-node>a.jstree-anchor>i.remove-icon",function(e, data) { - thatTreeController.deleteSingleNode($(e.target).parents("li")); + $("#tree-info-icon").on("click", function(e, data) { + $('#sidebar .info-content-container').slideToggle(); }); - - $("#tree").on("dblclick", "li.jstree-node>a.jstree-anchor",function(e, data) { - var id = $(e.target).parents("li:first").attr("id"); - thatTreeController.openRenameNodeDialog(id); + $("#sidebar .info-content-container .close").on("click", function(e, data) { + $('#sidebar .info-content-container').slideToggle(); }); }); }; + TreeController.prototype.configureKeyListeners = function(e) { + // see http://www.javascripter.net/faq/keycodes.htm + var del = 46; + var c = 67; + switch(e.which) { + case del: + treeController.deleteNodes(); + break; + case c: + this.openAddNodeDialog($(e.target).parents("li")); + break; + } + + } + + TreeController.prototype.openNodeTarget = function(e) { var url = $(e.target).parent().attr("href"); url = this.mainController.decodeFromHTML(url); @@ -230,7 +255,8 @@ org.apache.sling.reseditor.TreeController = (function() { TreeController.prototype.openAddNodeDialog = function(li) { var thatTreeController = this; var resourcePath = this.getPathFromLi(li); - this.addNodeController.openAddNodeDialog(resourcePath); + var nodeTypeName = li.attr("nodetype"); + this.addNodeController.openAddNodeDialog(resourcePath, nodeTypeName); } /* @@ -240,4 +266,4 @@ org.apache.sling.reseditor.TreeController = (function() { */ return TreeController; -}()); \ No newline at end of file +}()); diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor/html.jsp b/src/main/resources/SLING-INF/libs/sling/resource-editor/html.jsp index 23cb659..5ec2743 100644 --- a/src/main/resources/SLING-INF/libs/sling/resource-editor/html.jsp +++ b/src/main/resources/SLING-INF/libs/sling/resource-editor/html.jsp @@ -162,76 +162,96 @@ new org.apache.sling.reseditor.JSTreeAdapter(jsTreeAdapterSettings, treeControll <div class="row"> <div class="col-sm-4"> <div id="sidebar" class="plate"> - <div id="tree" class="root" ></div> + <div class="ie9filter-plate-div"> + <div style="display:none;" class="info-content-container" > + <div class="well well-sm info-content"> + <button type="button" class="close"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> + <h4>Cheat Sheet</h4> + <p>You can use</p> + <ul> + <li>the arrow keys <kbd>◀</kbd>,<kbd>▶</kbd>,<kbd>▲</kbd>,<kbd>▼</kbd> for navigation. You can keep them pressed for faster navigation.</li> + <li>the <kbd>space</kbd> key for selecting nodes.</li> + <li>the <kbd>cmd</kbd> key for Mac or <kbd>ctrl</kbd> key for non Mac systems for multi selecting single nodes.</li> + <li>the <kbd>shift</kbd> key for multi selecting a list of nodes.</li> + <li>the <kbd>del</kbd> key for deleting the selected nodes.</li> + <li>the <kbd>c</kbd> key on a node when the tree has the focus for opening the dialog to add a child node.</li> + <li>a double click to rename a node. Special JCR characters like ':' are not allowed as node names.</li> + </ul> + </div> + </div> + <div id="tree" class="root" ></div> + </div> </div> </div> <div class="col-sm-8"> <div id="outer_content" class="plate"> - <div id="inner_content_margin"> - <form action="not_configured_yet.change.properties" method="post"> - <c:set var="resourceIsNode" scope="request" value="<%=resource.adaptTo(Node.class) !=null %>"/> - <c:choose> - <c:when test="${resourceIsNode}" > - <%-- - For some reason I get the following exception when using the JSTL expression '${currentNode.properties}' - instead of the scriptlet code 'currentNode.getProperties()': - org.apache.sling.scripting.jsp.jasper.JasperException: Unable to compile class for JSP: - org.apache.sling.scripting.jsp.jasper.el.JspValueExpression cannot be resolved to a type - see https://issues.apache.org/jira/browse/SLING-2455 - --%> - <c:forEach var="property" items="<%=currentNode.getProperties()%>"> - <% Property property = (Property) pageContext.getAttribute("property");%> - <fieldset> - <label class="proplabel" for='${property.name}'>${property.name} [<%=PropertyType.nameFromValue(property.getType())%>${property.multiple ? ' multiple' : ''}]</label> - <c:choose> - <c:when test="${property.multiple}" > - <fieldset class="propmultival_fieldset"> - <div> </div> - <c:forEach var="value" items="<%=property.getValues()%>"> - <c:choose> - <c:when test="${property.type == PropertyType.BINARY}" > - <p>I'm a binary property</p> + <div class="ie9filter-plate-div"> + <div id="inner_content_margin"> + <form action="not_configured_yet.change.properties" method="post"> + <c:set var="resourceIsNode" scope="request" value="<%=resource.adaptTo(Node.class) !=null %>"/> + <c:choose> + <c:when test="${resourceIsNode}" > + <%-- + For some reason I get the following exception when using the JSTL expression '${currentNode.properties}' + instead of the scriptlet code 'currentNode.getProperties()': + org.apache.sling.scripting.jsp.jasper.JasperException: Unable to compile class for JSP: + org.apache.sling.scripting.jsp.jasper.el.JspValueExpression cannot be resolved to a type + see https://issues.apache.org/jira/browse/SLING-2455 + --%> + <c:forEach var="property" items="<%=currentNode.getProperties()%>"> + <% Property property = (Property) pageContext.getAttribute("property");%> + <fieldset> + <label class="proplabel" for='${property.name}'>${property.name} [<%=PropertyType.nameFromValue(property.getType())%>${property.multiple ? ' multiple' : ''}]</label> + <c:choose> + <c:when test="${property.multiple}" > + <fieldset class="propmultival_fieldset"> + <div> </div> + <c:forEach var="value" items="<%=property.getValues()%>"> + <c:choose> + <c:when test="${property.type == PropertyType.BINARY}" > + <p>I'm a binary property</p> + </c:when> + <c:otherwise> + <input class="propinputmultival form-control" value="${value.string}"/> + </c:otherwise> + </c:choose> + </c:forEach> + </fieldset> + </c:when> + <c:when test="${false}" > + </c:when> + <c:otherwise> + <c:choose> + <c:when test="<%=property.getType() == PropertyType.BINARY%>" > + <c:choose> + <c:when test='<%=currentNode.getParent().isNodeType("nt:file") %>'> + <a class="propinput" href="<%= request.getContextPath() %>${resource.parent.path}">Download</a> + </c:when> + <c:otherwise> + <a class="propinput" href="<%= request.getContextPath() %>${resource.path}.property.download?property=${property.name}">View (choose "Save as..." to download)</a> + </c:otherwise> + </c:choose> </c:when> <c:otherwise> - <input class="propinputmultival form-control" value="${value.string}"/> + <input class="propinput form-control" id="${property.name}" name="${property.name}" value="${property.string}"/> </c:otherwise> </c:choose> - </c:forEach> - </fieldset> - </c:when> - <c:when test="${false}" > - </c:when> - <c:otherwise> - <c:choose> - <c:when test="<%=property.getType() == PropertyType.BINARY%>" > - <c:choose> - <c:when test='<%=currentNode.getParent().isNodeType("nt:file") %>'> - <a class="propinput" href="<%= request.getContextPath() %>${resource.parent.path}">Download</a> - </c:when> - <c:otherwise> - <a class="propinput" href="<%= request.getContextPath() %>${resource.path}.property.download?property=${property.name}">View (choose "Save as..." to download)</a> - </c:otherwise> - </c:choose> - </c:when> - <c:otherwise> - <input class="propinput form-control" id="${property.name}" name="${property.name}" value="${property.string}"/> </c:otherwise> - </c:choose> - </c:otherwise> - </c:choose> - </fieldset> - </c:forEach> - </c:when> - <c:otherwise> - <c:forEach var="property" items="<%=resource.adaptTo(ValueMap.class)%>"> - <fieldset> - <label class="proplabel" for='${property.key}'>${property.key}</label> - <input class="propinput form-control" id="${property.key}" name="${property.key}" value="${property.value}"/> - </fieldset> - </c:forEach> - </c:otherwise> - </c:choose> - </form> + </c:choose> + </fieldset> + </c:forEach> + </c:when> + <c:otherwise> + <c:forEach var="property" items="<%=resource.adaptTo(ValueMap.class)%>"> + <fieldset> + <label class="proplabel" for='${property.key}'>${property.key}</label> + <input class="propinput form-control" id="${property.key}" name="${property.key}" value="${property.value}"/> + </fieldset> + </c:forEach> + </c:otherwise> + </c:choose> + </form> + </div> </div> </div> </div> @@ -244,26 +264,55 @@ new org.apache.sling.reseditor.JSTreeAdapter(jsTreeAdapterSettings, treeControll </div> </div> </div> + <span id="tree-info-icon" class="info-icon info-icon-lightgray pull-right clearfix" style="display:none;"></span> <!-- Add node dialog --> <div class="modal fade" id="addNodeDialog" tabindex="-1" role="dialog" aria-labelledby="addNodeDialogLabel" aria-hidden="true"> <div class="modal-dialog modal-sm"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> - <h4 class="modal-title" id="addNodeDialogLabel">Add Node</h4> + <h4 class="modal-title" id="addNodeDialogLabel">Add Child Node + <span class="info-icon info-icon-dark pull-right"></span> + </h4> </div> - <div class="modal-body"> + <div class="modal-body vertical-form"> + <div id="cheatsheet"> + <div id="cheatsheet-content" style="display:none;" class="well well-sm info-content"> + <button type="button" class="close"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> + <h3>Cheat Sheet</h3> + <h4>Shortcuts</h4> + <p>You can use the</p> + <ul> + <li><kbd>c</kbd> key on a node when the tree has the focus for opening the dialog to add a child node.</li> + <li><kbd>esc</kbd> key to close the dialog.</li> + <li><kbd><kbd>ctrl</kbd>+<kbd>a</kbd></kbd> keys to submit the form.</li> + <li><kbd>tab</kbd> key to navigate from one form element to the next one and <kbd><kbd>shift</kbd>+<kbd>tab</kbd></kbd> to navigate back.</li> + </ul> + <h4>Defaults</h4> + <p>Defaults are used for empty fields if you submit the dialog.</p> + <h4>New dropdown entries</h4> + <ul> + <li>New node names are allowed for most of the node types.</li> + <li>It's not allowed to enter new node type names.</li> + <li>You can enter new values for resource types if the node type allows for the 'sling:resourceType' String property.</li> + </ul> + <h4>Searching dropdown entries</h4> + <p>Entering text in the dropdown boxes searches for entries that contain that text.</p> + </div> + </div> <div class="form-group"> <label for="nodeName">Node Name</label> - <input name="nodeName" type="text" class="form-control" id="nodeName" placeholder="Node Name"> + <input name="nodeName" type="hidden" id="nodeName"> + <span class="only-listed-node-names-allowed help-block" style="display:none;">Only the node names listed in the drop down box are allowed.</span> </div> - <div class="form-group"> - <label for="nodeType">Node Type</label> + <div class="form-group node-type"> + <label for="nodeType">Node Type - <span class="nt-dependency-description">applicable together with node name</span> (<a class="nt-toggle" href="javascript:void(0)">show generally applicable</a>)</label> <input name="jcr:primaryType" type="hidden" id="nodeType"> </div> <div class="form-group"> <label for="resourceType">Sling Resource Type</label> <input name="sling:resourceType" type="hidden" id="resourceType"> + <span class="resource-type-not-allowed help-block" style="display:none;">The selected node type does not allow the resulting node to have a Sling resource type property.</span> </div> </div> <div class="modal-footer"> diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor/nodes.json.incl.jsp b/src/main/resources/SLING-INF/libs/sling/resource-editor/nodes.json.incl.jsp index 16881a6..94dd98c 100644 --- a/src/main/resources/SLING-INF/libs/sling/resource-editor/nodes.json.incl.jsp +++ b/src/main/resources/SLING-INF/libs/sling/resource-editor/nodes.json.incl.jsp @@ -1,15 +1,20 @@ - -[ - <c:forEach var="theResource" items="<%=resource.listChildren()%>" varStatus="status"> - <%--Hiding the resource provider root. --%> - <c:if test="${theResource.path != '/reseditor'}"> - <% Resource theResource = (Resource) pageContext.getAttribute("theResource");%> - { - "text": "<i class=\"jstree-icon node-icon open-icon\"></i><i class=\"jstree-icon node-icon remove-icon\"></i><i class=\"jstree-icon node-icon add-icon\"></i>${fn:escapeXml(theResource.name)} [<span class=\"node-type\">${theResource.resourceType}</span>]", - "li_attr": { "nodename" : "${fn:escapeXml(theResource.name)}" }, - "a_attr": { "href" : "/reseditor${fn:escapeXml(theResource.path)}.html" }, - "children" : <%= theResource.listChildren().hasNext() %> <%--${theResource.listChildren().hasNext()} will work in Servlet 3.0 --%> - }${!status.last ? ',': ''} - </c:if> - </c:forEach> -] + +[ + <c:forEach var="theResource" items="<%=resource.listChildren()%>" varStatus="status"> + <c:set var="resourceIsNode" scope="request" value="<%=resource.adaptTo(Node.class) !=null %>"/> + <%--Hiding the resource provider root. --%> + <c:if test="${theResource.path != '/reseditor'}"> + <% Resource theResource = (Resource) pageContext.getAttribute("theResource"); + Node node = theResource.adaptTo(Node.class); + String nodeType = (node !=null) ? node.getPrimaryNodeType().getName() : ""; + pageContext.setAttribute("nodeType", nodeType); + %> + { + "text": "<i class=\"jstree-icon node-icon open-icon\"></i><i class=\"jstree-icon node-icon remove-icon\"></i><i class=\"jstree-icon node-icon add-icon\"></i>${fn:escapeXml(theResource.name)} [<span class=\"node-type\">${theResource.resourceType}</span>]", + "li_attr": { "nodename" : "${fn:escapeXml(theResource.name)}", "nodetype" :"${nodeType}" }, + "a_attr": { "href" : "/reseditor${fn:escapeXml(theResource.path)}.html" }, + "children" : <%= theResource.listChildren().hasNext() %> <%--${theResource.listChildren().hasNext()} will work in Servlet 3.0 --%> + }${!status.last ? ',': ''} + </c:if> + </c:forEach> +] diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor/rootnodes.json.jsp b/src/main/resources/SLING-INF/libs/sling/resource-editor/rootnodes.json.jsp index 074f124..0840a1e 100644 --- a/src/main/resources/SLING-INF/libs/sling/resource-editor/rootnodes.json.jsp +++ b/src/main/resources/SLING-INF/libs/sling/resource-editor/rootnodes.json.jsp @@ -1,22 +1,22 @@ -<%@ page session="false"%> -<%@ page isELIgnored="false"%> -<%@ page import="javax.jcr.*,org.apache.sling.api.resource.Resource"%> -<%@ page import="java.util.Iterator"%> -<%@ page import="java.util.LinkedList, java.util.List"%> - -<%@ page language="java" contentType="application/json; charset=UTF-8" pageEncoding="UTF-8"%> - -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> -<%@ taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0"%> -<sling:defineObjects /> -<% response.setContentType("application/json"); %> -[{ - "id" : "/", - "state" : {"opened":true, "disabled": false, "selected": false}, - "text" : "<i class=\"jstree-icon node-icon open-icon\"></i><i class=\"jstree-icon node-icon add-icon\"></i> /", - "li_attr" :{ "nodename" : "${theResource.name}" }, - "a_attr" :{ "href" : "<%= request.getContextPath() %>/reseditor/.html" }, - "children" : - <%@ include file="nodes.json.incl.jsp" %> +<%@ page session="false"%> +<%@ page isELIgnored="false"%> +<%@ page import="javax.jcr.*,org.apache.sling.api.resource.Resource"%> +<%@ page import="java.util.Iterator"%> +<%@ page import="java.util.LinkedList, java.util.List"%> + +<%@ page language="java" contentType="application/json; charset=UTF-8" pageEncoding="UTF-8"%> + +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> +<%@ taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0"%> +<sling:defineObjects /> +<% response.setContentType("application/json"); %> +[{ + "id" : "root", + "state" : {"opened":true, "disabled": false, "selected": false}, + "text" : "<i class=\"jstree-icon node-icon open-icon\"></i><i class=\"jstree-icon node-icon add-icon\"></i> /", + "li_attr" :{ "nodename" : "${currentNode.name}", "nodetype" :"${currentNode.primaryNodeType.name}" }, + "a_attr" :{ "href" : "<%= request.getContextPath() %>/reseditor/.html" }, + "children" : + <%@ include file="nodes.json.incl.jsp" %> }] \ No newline at end of file diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor/servlet-nodes.json b/src/main/resources/SLING-INF/libs/sling/resource-editor/servlet-nodes.json new file mode 100644 index 0000000..1e5c3ed --- /dev/null +++ b/src/main/resources/SLING-INF/libs/sling/resource-editor/servlet-nodes.json @@ -0,0 +1,7 @@ +{ + "primaryNodeType": "nt:unstructured", + "resource-types" : { + "primaryNodeType": "nt:unstructured", + "sling:resourceType" : "resource-editor/resource-type-list" + } +} \ No newline at end of file -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
