From 4e7d2f668bfdd594dc8a56293eee2a42744cb7d4 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 13 Jan 2017 10:26:43 +0100 Subject: [PATCH 01/24] add node preview component --- .../components/umbnodepreview.directive.js | 35 +++++++++ src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../src/less/components/umb-node-preview.less | 71 +++++++++++++++++++ .../views/components/umb-node-preview.html | 13 ++++ 4 files changed, 120 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js new file mode 100644 index 0000000000..51e9b265f5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js @@ -0,0 +1,35 @@ +(function () { + 'use strict'; + + function NodePreviewDirective() { + + function link(scope, el, attr, ctrl) { + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-node-preview.html', + scope: { + icon: "=?", + name: "=", + description: "=?", + sortable: "=?", + allowEdit: "=?", + allowOpen: "=?", + allowRemove: "=?", + onEdit: "&?", + onOpen: "&?", + onRemove: "&?" + }, + link: link + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbNodePreview', NodePreviewDirective); + +})(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 2b53fbddfc..49a98ee219 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -122,6 +122,7 @@ @import "components/notifications/umb-notifications.less"; @import "components/umb-file-dropzone.less"; +@import "components/umb-node-preview.less"; // Utilities @import "utilities/_flexbox.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less new file mode 100644 index 0000000000..952a49f692 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -0,0 +1,71 @@ +.umb-node-preview { + padding: 5px 15px; + margin-bottom: 5px; + border: 2px solid #f8f8f8; + border-radius: 3px; + display: flex; + align-items: center; + background: @white; +} + +.umb-node-preview--sortable { + cursor: move; +} + +.umb-node-preview--sortable:hover { + border-color: #d9d9d9; +} + +.umb-node-preview__icon { + display: flex; + width: 25px; + height: 25px; + justify-content: center; + align-items: center; + font-size: 20px; + margin-right: 10px; + flex: 0 0 auto; +} + +.umb-node-preview__content { + flex: 1 1 auto; +} + +.umb-node-preview__name { + font-size: 13px; + font-weight: bold; + color: @black; +} + +.umb-node-preview__description { + font-size: 12px; +} + +.umb-node-preview__actions { + flex: 0 0 auto; +} + +.umb-node-preview__action { + margin-left: 5px; + margin-right: 5px; + font-size: 16px; +} + +.umb-node-preview__action:hover { + color: @blue; + text-decoration: none; +} + +.umb-node-preview-add { + display: flex; + align-items: center; + justify-content: center; + border: 1px dashed #d9d9d9; + color: @blue; + font-weight: bold; + padding: 5px 15px; +} + +.umb-node-preview-add:hover { + color: @blue; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html new file mode 100644 index 0000000000..4bcd1f39e2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html @@ -0,0 +1,13 @@ +
+ +
+
{{ name }}
+
{{ description }}
+
+
+ + + +
+ +
\ No newline at end of file From 0c709d144b3d805db1b67d66fc637f0eb0882148 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 13 Jan 2017 10:40:03 +0100 Subject: [PATCH 02/24] move mini editor launch logic to helper service so we can open it without the directive - it is needed for the content picker --- .../umblaunchminieditor.directive.js | 65 +-------------- .../services/minieditorhelper.service.js | 79 +++++++++++++++++++ 2 files changed, 81 insertions(+), 63 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umblaunchminieditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umblaunchminieditor.directive.js index 91212a82f8..11b934ce96 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umblaunchminieditor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umblaunchminieditor.directive.js @@ -7,7 +7,7 @@ * Used on a button to launch a mini content editor editor dialog **/ angular.module("umbraco.directives") - .directive('umbLaunchMiniEditor', function (dialogService, editorState, fileManager, contentEditingHelper) { + .directive('umbLaunchMiniEditor', function (miniEditorHelper) { return { restrict: 'A', replace: false, @@ -16,69 +16,8 @@ angular.module("umbraco.directives") }, link: function(scope, element, attrs) { - var launched = false; - element.click(function() { - - if (launched === true) { - return; - } - - launched = true; - - //We need to store the current files selected in the file manager locally because the fileManager - // is a singleton and is shared globally. The mini dialog will also be referencing the fileManager - // and we don't want it to be sharing the same files as the main editor. So we'll store the current files locally here, - // clear them out and then launch the dialog. When the dialog closes, we'll reset the fileManager to it's previous state. - var currFiles = _.groupBy(fileManager.getFiles(), "alias"); - fileManager.clearFiles(); - - //We need to store the original editorState entity because it will need to change when the mini editor is loaded so that - // any property editors that are working with editorState get given the correct entity, otherwise strange things will - // start happening. - var currEditorState = editorState.getCurrent(); - - dialogService.open({ - template: "views/common/dialogs/content/edit.html", - id: scope.node.id, - closeOnSave: true, - tabFilter: ["Generic properties"], - callback: function (data) { - - //set the node name back - scope.node.name = data.name; - - //reset the fileManager to what it was - fileManager.clearFiles(); - _.each(currFiles, function (val, key) { - fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; })); - }); - - //reset the editor state - editorState.set(currEditorState); - - //Now we need to check if the content item that was edited was actually the same content item - // as the main content editor and if so, update all property data - if (data.id === currEditorState.id) { - var changed = contentEditingHelper.reBindChangedProperties(currEditorState, data); - } - - launched = false; - }, - closeCallback: function () { - //reset the fileManager to what it was - fileManager.clearFiles(); - _.each(currFiles, function (val, key) { - fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; })); - }); - - //reset the editor state - editorState.set(currEditorState); - - launched = false; - } - }); - + miniEditorHelper.launchMiniEditor(scope.node); }); } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js new file mode 100644 index 0000000000..9c974f9ca9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js @@ -0,0 +1,79 @@ +(function () { + 'use strict'; + + function miniEditorHelper(dialogService, editorState, fileManager, contentEditingHelper) { + + var launched = false; + + function launchMiniEditor(node) { + + launched = true; + + //We need to store the current files selected in the file manager locally because the fileManager + // is a singleton and is shared globally. The mini dialog will also be referencing the fileManager + // and we don't want it to be sharing the same files as the main editor. So we'll store the current files locally here, + // clear them out and then launch the dialog. When the dialog closes, we'll reset the fileManager to it's previous state. + var currFiles = _.groupBy(fileManager.getFiles(), "alias"); + fileManager.clearFiles(); + + //We need to store the original editorState entity because it will need to change when the mini editor is loaded so that + // any property editors that are working with editorState get given the correct entity, otherwise strange things will + // start happening. + var currEditorState = editorState.getCurrent(); + + dialogService.open({ + template: "views/common/dialogs/content/edit.html", + id: node.id, + closeOnSave: true, + tabFilter: ["Generic properties"], + callback: function (data) { + + //set the node name back + node.name = data.name; + + //reset the fileManager to what it was + fileManager.clearFiles(); + _.each(currFiles, function (val, key) { + fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; })); + }); + + //reset the editor state + editorState.set(currEditorState); + + //Now we need to check if the content item that was edited was actually the same content item + // as the main content editor and if so, update all property data + if (data.id === currEditorState.id) { + var changed = contentEditingHelper.reBindChangedProperties(currEditorState, data); + } + + launched = false; + }, + closeCallback: function () { + //reset the fileManager to what it was + fileManager.clearFiles(); + _.each(currFiles, function (val, key) { + fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; })); + }); + + //reset the editor state + editorState.set(currEditorState); + + launched = false; + } + }); + + } + + var service = { + launchMiniEditor: launchMiniEditor + }; + + return service; + + } + + + angular.module('umbraco.services').factory('miniEditorHelper', miniEditorHelper); + + +})(); From 8e79e15c71c69ddb7c882ee0f0afde897006dd73 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 13 Jan 2017 11:16:15 +0100 Subject: [PATCH 03/24] use new umb node preview component in content picker --- .../contentpicker/contentpicker.controller.js | 8 ++- .../contentpicker/contentpicker.html | 51 +++++++++---------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 667dfd0f22..c02ab7a588 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -1,7 +1,7 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it -function contentPickerController($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location) { +function contentPickerController($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location, $timeout, miniEditorHelper) { function trim(str, chr) { var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g'); @@ -77,6 +77,8 @@ function contentPickerController($scope, dialogService, entityResource, editorSt : "Document"; $scope.allowOpenButton = entityType === "Document" || entityType === "Media"; $scope.allowEditButton = entityType === "Document"; + $scope.allowRemoveButton = true; + $scope.sortable = true; //the dialog options for the picker var dialogOptions = { @@ -200,6 +202,10 @@ function contentPickerController($scope, dialogService, entityResource, editorSt $scope.clear = function () { $scope.renderModel = []; }; + + $scope.openMiniEditor = function(node) { + miniEditorHelper.launchMiniEditor(node); + }; var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { var currIds = _.map($scope.renderModel, function (i) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index 9f5f3fb60f..760063b94b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -1,33 +1,30 @@
- - - - + +
+ + +
+ + + Add + From 21714f0eacdbd14822212c91faa758c0ed230fe4 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 13 Jan 2017 11:16:59 +0100 Subject: [PATCH 04/24] use new umb node preview component in member group picker --- .../membergrouppicker.controller.js | 1 + .../membergrouppicker/membergrouppicker.html | 37 ++++++++----------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js index 5ebbd37217..aa20a9c43b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js @@ -8,6 +8,7 @@ function memberGroupPicker($scope, dialogService){ } $scope.renderModel = []; + $scope.allowRemove = true; if ($scope.model.value) { var modelIds = $scope.model.value.split(','); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html index 5258968c00..60ae9cb202 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html @@ -1,27 +1,22 @@
+
+ + +
- - - + + Add + Date: Fri, 13 Jan 2017 11:17:28 +0100 Subject: [PATCH 05/24] use new umb node preview component in member picker --- .../memberpicker/memberpicker.controller.js | 1 + .../memberpicker/memberpicker.html | 37 ++++++++----------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js index 9f20e121d9..0d4b2817d0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js @@ -8,6 +8,7 @@ function memberPickerController($scope, dialogService, entityResource, $log, ico } $scope.renderModel = []; + $scope.allowRemove = true; var dialogOptions = { multiPicker: false, diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html index b537a80113..a7a4127b18 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html @@ -1,27 +1,22 @@
- - - + + Add + Date: Fri, 13 Jan 2017 13:16:47 +0100 Subject: [PATCH 06/24] fine tuning --- .../components/umbnodepreview.directive.js | 2 -- .../src/less/components/umb-node-preview.less | 15 ++++++--- .../views/components/umb-node-preview.html | 5 ++- .../contentpicker/contentpicker.controller.js | 32 +++++++++++-------- .../contentpicker/contentpicker.html | 9 +++--- 5 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js index 51e9b265f5..62a8bc766e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js @@ -16,10 +16,8 @@ name: "=", description: "=?", sortable: "=?", - allowEdit: "=?", allowOpen: "=?", allowRemove: "=?", - onEdit: "&?", onOpen: "&?", onRemove: "&?" }, diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less index 952a49f692..b69d4b4b22 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -1,11 +1,11 @@ .umb-node-preview { padding: 5px 15px; margin-bottom: 5px; - border: 2px solid #f8f8f8; + background: @grayLighter; border-radius: 3px; display: flex; align-items: center; - background: @white; + max-width: 66.6%; } .umb-node-preview--sortable { @@ -38,22 +38,28 @@ } .umb-node-preview__description { - font-size: 12px; + font-size: 11px; + line-height: 1.5em; } .umb-node-preview__actions { flex: 0 0 auto; + display: flex; + align-items: center; } .umb-node-preview__action { margin-left: 5px; margin-right: 5px; - font-size: 16px; + font-size: 13px; + font-weight: bold; + opacity: 0.5; } .umb-node-preview__action:hover { color: @blue; text-decoration: none; + opacity: 1; } .umb-node-preview-add { @@ -64,6 +70,7 @@ color: @blue; font-weight: bold; padding: 5px 15px; + max-width: 66.6%; } .umb-node-preview-add:hover { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html index 4bcd1f39e2..b811ae8eec 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html @@ -5,9 +5,8 @@
{{ description }}
- - - + Open + Remove
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index c02ab7a588..ccf9d865bc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -1,7 +1,7 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it -function contentPickerController($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location, $timeout, miniEditorHelper) { +function contentPickerController($scope, dialogService, entityResource, contentResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location, $timeout, miniEditorHelper) { function trim(str, chr) { var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g'); @@ -221,26 +221,32 @@ function contentPickerController($scope, dialogService, entityResource, editorSt //load current data var modelIds = $scope.model.value ? $scope.model.value.split(',') : []; - entityResource.getByIds(modelIds, entityType).then(function (data) { + var nodePromise = (entityType === "Document") ? contentResource.getByIds(modelIds) : entityResource.getByIds(modelIds, entityType); - //Ensure we populate the render model in the same order that the ids were stored! - _.each(modelIds, function (id, i) { - var entity = _.find(data, function (d) { - return d.id == id; - }); - - if (entity) { - entity.icon = iconHelper.convertFromLegacyIcon(entity.icon); - $scope.renderModel.push({ name: entity.name, id: entity.id, icon: entity.icon, path: entity.path }); - } + nodePromise.then(function (data) { + + _.each(modelIds, function (id, i) { + var entity = _.find(data, function (d) { + return d.id == id; + }); + + if (entity) { + entity.icon = iconHelper.convertFromLegacyIcon(entity.icon); + + var url = (entity.urls && entity.urls.length > 0) ? entity.urls[0] : ""; + var path = ($scope.model.config.showPathOnHover) ? entity.path : ""; + + $scope.renderModel.push({ name: entity.name, id: entity.id, icon: entity.icon, path: path, url: url }); + } - }); + }); //everything is loaded, start the watch on the model startWatch(); }); + } angular.module('umbraco').controller("Umbraco.PropertyEditors.ContentPickerController", contentPickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index 760063b94b..abcb7f20c0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -1,20 +1,19 @@
- +
+ on-open="openMiniEditor(node)">
From 361f5093c7ea1d0a855ff1ccd7d2218e2bcc79e2 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 13 Jan 2017 14:42:36 +0100 Subject: [PATCH 07/24] align width with input fields --- .../src/less/components/umb-node-preview.less | 2 ++ src/Umbraco.Web.UI.Client/src/less/forms.less | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less index b69d4b4b22..3f51b09154 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -6,6 +6,7 @@ display: flex; align-items: center; max-width: 66.6%; + box-sizing: border-box; } .umb-node-preview--sortable { @@ -71,6 +72,7 @@ font-weight: bold; padding: 5px 15px; max-width: 66.6%; + box-sizing: border-box; } .umb-node-preview-add:hover { diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index d046ac7104..5b6e97ad71 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -190,7 +190,7 @@ input[type="tel"], input[type="color"], .uneditable-input { display: inline-block; - height: @baseLineHeight; + height: 30px; padding: 4px 6px; margin-bottom: @baseLineHeight / 2; font-size: @baseFontSize; @@ -198,6 +198,7 @@ input[type="color"], color: @gray; .border-radius(@inputBorderRadius); vertical-align: middle; + box-sizing: border-box; } input.-full-width-input { From 198edec4f4f9f72bf3ebea1dbdbd4ebfc37ee282 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 10:09:53 +0100 Subject: [PATCH 08/24] add fallback icons --- .../contentpicker/contentpicker.controller.js | 21 ++++++++++++++++++- .../memberpicker/memberpicker.controller.js | 3 ++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index ccf9d865bc..e77e743f2b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -231,7 +231,26 @@ function contentPickerController($scope, dialogService, entityResource, contentR }); if (entity) { - entity.icon = iconHelper.convertFromLegacyIcon(entity.icon); + + // set icon + if(entity.icon) { + entity.icon = iconHelper.convertFromLegacyIcon(entity.icon); + } + + // set default icon + if (!entity.icon) { + switch (entityType) { + case "Document": + entity.icon = "icon-document"; + break; + case "Media": + entity.icon = "icon-picture"; + break; + case "Member": + entity.icon = "icon-user"; + break; + } + } var url = (entity.urls && entity.urls.length > 0) ? entity.urls[0] : ""; var path = ($scope.model.config.showPathOnHover) ? entity.path : ""; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js index 0d4b2817d0..0029332f64 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js @@ -97,7 +97,8 @@ function memberPickerController($scope, dialogService, entityResource, $log, ico var modelIds = $scope.model.value ? $scope.model.value.split(',') : []; entityResource.getByIds(modelIds, "Member").then(function (data) { _.each(data, function (item, i) { - item.icon = iconHelper.convertFromLegacyIcon(item.icon); + // set default icon if it's missing + item.icon = (item.icon) ? iconHelper.convertFromLegacyIcon(item.icon) : "icon-user"; $scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon }); }); }); From 3bdc2e63abda90cb03f1d62d0bc935a78119a0a3 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 14:45:50 +0100 Subject: [PATCH 09/24] get url for items + set published/unpublished state --- .../components/umbnodepreview.directive.js | 1 + .../src/common/resources/entity.resource.js | 35 +++++- .../src/less/components/umb-node-preview.less | 8 ++ .../views/components/umb-node-preview.html | 2 +- .../contentpicker/contentpicker.controller.js | 100 ++++++++++++------ .../contentpicker/contentpicker.html | 1 + src/Umbraco.Web/Editors/EntityController.cs | 31 ++++++ 7 files changed, 140 insertions(+), 38 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js index 62a8bc766e..8d63623efb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js @@ -15,6 +15,7 @@ icon: "=?", name: "=", description: "=?", + published: "=?", sortable: "=?", allowOpen: "=?", allowRemove: "=?", diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js index 914b601249..5e0f5deada 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js @@ -56,7 +56,7 @@ function entityResource($q, $http, umbRequestHelper) { * * ##usage *
-         * entityResource.getPath(id)
+         * entityResource.getPath(id, type)
          *    .then(function(pathArray) {
          *        alert('its here!');
          *    });
@@ -77,6 +77,37 @@ function entityResource($q, $http, umbRequestHelper) {
                'Failed to retrieve path for id:' + id);
         },
 
+        /**
+         * @ngdoc method
+         * @name umbraco.resources.entityResource#getUrl
+         * @methodOf umbraco.resources.entityResource
+         *
+         * @description
+         * Returns a url, given a node ID and type
+         *
+         * ##usage
+         * 
+         * entityResource.getUrl(id, type)
+         *    .then(function(url) {
+         *        alert('its here!');
+         *    });
+         * 
+ * + * @param {Int} id Id of node to return the public url to + * @param {string} type Object type name + * @returns {Promise} resourcePromise object containing the url. + * + */ + getUrl: function(id, type) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "entityApiBaseUrl", + "GetUrl", + [{ id: id }, {type: type }])), + 'Failed to retrieve url for id:' + id); + }, + /** * @ngdoc method * @name umbraco.resources.entityResource#getById @@ -140,7 +171,7 @@ function entityResource($q, $http, umbRequestHelper) { query += "ids=" + item + "&"; }); - // if ids array is empty we need a empty variable in the querystring otherwise the service returns a error + // if ids array is empty we need a empty variable in the querystring otherwise the service returns a error if (ids.length === 0) { query += "ids=&"; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less index 3f51b09154..5ef9cc7e74 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -17,6 +17,14 @@ border-color: #d9d9d9; } +.umb-node-preview--unpublished { + .umb-node-preview__icon, + .umb-node-preview__name, + .umb-node-preview__description { + opacity: 0.6; + } +} + .umb-node-preview__icon { display: flex; width: 25px; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html index b811ae8eec..f31f595a88 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html @@ -1,4 +1,4 @@ -
+
{{ name }}
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index e77e743f2b..f97aa2bf62 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -194,8 +194,19 @@ function contentPickerController($scope, dialogService, entityResource, contentR }); if (currIds.indexOf(item.id) < 0) { - item.icon = iconHelper.convertFromLegacyIcon(item.icon); - $scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon, path: item.path }); + + // get url for content and media items + if(entityType !== "Member") { + entityResource.getUrl(item.id, entityType).then(function(data){ + // update url + item.url = data.url; + // push item to render model + addSelectedItem(item); + }); + } else { + addSelectedItem(item); + } + } }; @@ -221,43 +232,28 @@ function contentPickerController($scope, dialogService, entityResource, contentR //load current data var modelIds = $scope.model.value ? $scope.model.value.split(',') : []; - var nodePromise = (entityType === "Document") ? contentResource.getByIds(modelIds) : entityResource.getByIds(modelIds, entityType); + var nodePromise = entityResource.getByIds(modelIds, entityType); nodePromise.then(function (data) { - _.each(modelIds, function (id, i) { - var entity = _.find(data, function (d) { - return d.id == id; - }); - - if (entity) { - - // set icon - if(entity.icon) { - entity.icon = iconHelper.convertFromLegacyIcon(entity.icon); - } - - // set default icon - if (!entity.icon) { - switch (entityType) { - case "Document": - entity.icon = "icon-document"; - break; - case "Media": - entity.icon = "icon-picture"; - break; - case "Member": - entity.icon = "icon-user"; - break; - } - } - - var url = (entity.urls && entity.urls.length > 0) ? entity.urls[0] : ""; - var path = ($scope.model.config.showPathOnHover) ? entity.path : ""; - - $scope.renderModel.push({ name: entity.name, id: entity.id, icon: entity.icon, path: path, url: url }); + _.each(modelIds, function (id, i) { + var entity = _.find(data, function (d) { + return d.id == id; + }); + + if (entity) { + // get url for content and media items + if(entityType !== "Member") { + entityResource.getUrl(entity.id, entityType).then(function(data){ + // update url + entity.url = data.url; + // push item to render model + addSelectedItem(entity); + }); + } else { + addSelectedItem(entity); } - + } }); @@ -266,6 +262,40 @@ function contentPickerController($scope, dialogService, entityResource, contentR }); + function addSelectedItem(item) { + + // set icon + if(item.icon) { + item.icon = iconHelper.convertFromLegacyIcon(item.icon); + } + + // set default icon + if (!item.icon) { + switch (entityType) { + case "Document": + item.icon = "icon-document"; + break; + case "Media": + item.icon = "icon-picture"; + break; + case "Member": + item.icon = "icon-user"; + break; + } + } + + $scope.renderModel.push({ + "name": item.name, + "id": item.id, + "icon": item.icon, + "path": item.path, + "url": item.url, + "published": (item.metaData.IsPublished === false && entityType === "Document") ? false : true + // only content supports published/unpublished content so we set everything else to published so the UI looks correct + }); + + } + } angular.module('umbraco').controller("Umbraco.PropertyEditors.ContentPickerController", contentPickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index abcb7f20c0..a8076ff987 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -8,6 +8,7 @@ ng-repeat="node in renderModel" icon="node.icon" name="node.name" + published="node.published" description="node.url" sortable="sortable" allow-remove="allowRemoveButton" diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 596e27e3a5..cfc04a7f20 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -144,6 +144,37 @@ namespace Umbraco.Web.Editors return foundContent.Path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse); } + + + + public dynamic GetUrl(int id, UmbracoEntityTypes type) + { + dynamic result = new System.Dynamic.ExpandoObject(); + + + if(type == UmbracoEntityTypes.Document) + { + var foundUrl = Umbraco.Url(id); + if (string.IsNullOrEmpty(foundUrl) == false && foundUrl != "#") + { + result.url = foundUrl; + return result; + } + } + + var ancestors = GetAncestors(id, type); + + //if content, skip the first node for replicating NiceUrl defaults + if(type == UmbracoEntityTypes.Document) { + ancestors = ancestors.Skip(1); + } + + result.url = "/" + string.Join("/", ancestors.Select(x => x.Name) ); + + + return result; + } + /// /// Gets an entity by it's unique id if the entity supports that /// From bbe68cc22436917df70de0549045e45051888cfc Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 20:13:17 +0100 Subject: [PATCH 10/24] better styling of mini editor --- .../components/tabs/umbtabsnav.directive.js | 3 +- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../src/less/components/umb-mini-editor.less | 33 ++++++++++++++++ .../src/less/components/umb-node-preview.less | 10 +++++ .../views/common/dialogs/content/edit.html | 39 ++++++++++++++----- .../views/components/tabs/umb-tabs-nav.html | 2 +- 6 files changed, 76 insertions(+), 12 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtabsnav.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtabsnav.directive.js index 890acf3d6f..50c4775ef9 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtabsnav.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtabsnav.directive.js @@ -43,7 +43,8 @@ templateUrl: "views/components/tabs/umb-tabs-nav.html", scope: { model: "=", - tabdrop: "=" + tabdrop: "=", + idSuffix: "@" }, link: link }; diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 49a98ee219..79a3ecdf8f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -123,6 +123,7 @@ @import "components/notifications/umb-notifications.less"; @import "components/umb-file-dropzone.less"; @import "components/umb-node-preview.less"; +@import "components/umb-mini-editor.less"; // Utilities @import "utilities/_flexbox.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less new file mode 100644 index 0000000000..de10805aef --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less @@ -0,0 +1,33 @@ +.umb-modal .umb-mini-editor { + + .umb-panel-header { + padding: 20px 10px; + background: @grayLighter; + border-bottom: 1px solid @grayLight; + height: 59px; + + .umb-headline { + margin-left: 0; + margin-right: 0; + margin-bottom: 0; + margin-top: 3px; + } + } + + .umb-panel-body { + padding-left: 0; + padding-right: 0; + } + + .umb-panel-body.with-footer { + bottom: 52px; + } + + .umb-panel-footer { + background: @grayLighter; + border-top: 1px solid @grayLight; + height: 51px; + padding: 0 20px; + } + +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less index 5ef9cc7e74..5b96492b48 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -85,4 +85,14 @@ .umb-node-preview-add:hover { color: @blue; +} + +.umb-overlay, +.umb-modal { + .umb-node-preview { + max-width: none; + } + .umb-node-preview-add { + max-width: none; + } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.html index 55e0cad2e7..eef28aefa1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.html @@ -2,7 +2,8 @@ ng-controller="Umbraco.Dialogs.Content.EditController" ng-show="loaded" ng-submit="save()" - val-form-manager> + val-form-manager + class="umb-mini-editor">
@@ -12,15 +13,33 @@
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/tabs/umb-tabs-nav.html b/src/Umbraco.Web.UI.Client/src/views/components/tabs/umb-tabs-nav.html index 8522bc3b6a..808822e138 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/tabs/umb-tabs-nav.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/tabs/umb-tabs-nav.html @@ -1,5 +1,5 @@ From cca0f4c293fdf95420e9b74cf024468531ca5c74 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 20:13:43 +0100 Subject: [PATCH 11/24] clean up --- .../propertyeditors/contentpicker/contentpicker.controller.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index f97aa2bf62..b587231220 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -232,9 +232,8 @@ function contentPickerController($scope, dialogService, entityResource, contentR //load current data var modelIds = $scope.model.value ? $scope.model.value.split(',') : []; - var nodePromise = entityResource.getByIds(modelIds, entityType); - nodePromise.then(function (data) { + entityResource.getByIds(modelIds, entityType).then(function (data) { _.each(modelIds, function (id, i) { var entity = _.find(data, function (d) { From 7b616cf719ce8eedfc78cc182de5e78174052b82 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 20:50:56 +0100 Subject: [PATCH 12/24] add a bit padding on the sides --- .../src/less/components/umb-mini-editor.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less index de10805aef..53e0dcd6b3 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less @@ -1,7 +1,7 @@ .umb-modal .umb-mini-editor { .umb-panel-header { - padding: 20px 10px; + padding: 20px; background: @grayLighter; border-bottom: 1px solid @grayLight; height: 59px; From c58957dc7b1a7e8e885562a5b134f333e7b32de2 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 20:52:07 +0100 Subject: [PATCH 13/24] fix unit tests --- .../common/mocks/resources/entity.mocks.js | 17 ++++++++ .../contentpicker/contentpicker.controller.js | 2 +- .../content-picker-controller.spec.js | 40 +++++++++++++------ 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js index fc60781375..a98240111e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js @@ -33,6 +33,19 @@ angular.module('umbraco.mocks'). return [200, nodes, null]; } + function returnEntityUrl() { + + if (!mocksUtils.checkAuth()) { + return [401, null, null]; + } + + var urlOrbject = { + "url": "url" + }; + + return [200, urlOrbject, null]; + + } return { register: function () { @@ -48,6 +61,10 @@ angular.module('umbraco.mocks'). $httpBackend .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetById?')) .respond(returnEntitybyId); + + $httpBackend + .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetUrl?')) + .respond(returnEntityUrl); } }; }]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index b587231220..e5852c29fd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -289,7 +289,7 @@ function contentPickerController($scope, dialogService, entityResource, contentR "icon": item.icon, "path": item.path, "url": item.url, - "published": (item.metaData.IsPublished === false && entityType === "Document") ? false : true + "published": (item.metaData && item.metaData.IsPublished === false && entityType === "Document") ? false : true // only content supports published/unpublished content so we set everything else to published so the UI looks correct }); diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js index e6d1312109..f34f8088df 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js @@ -17,7 +17,11 @@ describe('Content picker controller tests', function () { value:"1233,1231,23121", label: "My content picker", description: "desc", - config: {} + config: { + startNode: { + type: "content" + } + } }; //this controller requires an angular form controller applied to it @@ -47,6 +51,12 @@ describe('Content picker controller tests', function () { })); describe('content edit controller save and publish', function () { + + var item = { + name: "meh", + id: 666, + icon: "woop" + }; it('should define the default properties on construction', function () { expect(scope.model.value).toNotBe(undefined); @@ -65,7 +75,6 @@ describe('Content picker controller tests', function () { }); it("Removing an item should update renderModel, ids and model.value", function(){ - scope.remove(1); scope.$apply(); expect(scope.renderModel.length).toBe(2); @@ -73,24 +82,29 @@ describe('Content picker controller tests', function () { }); it("Adding an item should update renderModel, ids and model.value", function(){ - - scope.add({name: "meh", id: 666, icon: "woop"}); + scope.add(item); scope.$apply(); - expect(scope.renderModel.length).toBe(4); - expect(scope.model.value).toBe("1233,1231,23121,666"); + setTimeout(function(){ + expect(scope.renderModel.length).toBe(4); + expect(scope.model.value).toBe("1233,1231,23121,666"); + }, 1000); }); it("Adding a dublicate item should note update renderModel, ids and model.value", function(){ - - scope.add({ name: "meh", id: 666, icon: "woop" }); + scope.add(item); scope.$apply(); - expect(scope.renderModel.length).toBe(4); - expect(scope.model.value).toBe("1233,1231,23121,666"); + setTimeout(function(){ + expect(scope.renderModel.length).toBe(4); + expect(scope.model.value).toBe("1233,1231,23121,666"); + }, 1000); - scope.add({ name: "meh 2", id: 666, icon: "woop 2" }); + scope.add(item); scope.$apply(); - expect(scope.renderModel.length).toBe(4); - expect(scope.model.value).toBe("1233,1231,23121,666"); + setTimeout(function(){ + expect(scope.renderModel.length).toBe(4); + expect(scope.model.value).toBe("1233,1231,23121,666"); + }, 1000); + }); }); }); \ No newline at end of file From bf4ad25cadaa97b5783ef178187e07b8987d4b91 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 21:16:31 +0100 Subject: [PATCH 14/24] remove ng-title --- .../src/views/propertyeditors/contentpicker/contentpicker.html | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index a8076ff987..ca6f9ac826 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -4,7 +4,6 @@
Date: Tue, 17 Jan 2017 11:11:20 +0100 Subject: [PATCH 15/24] combine "open" end "edit" prevalues into one setting (open in dialog) + remove "show path on hover" setting because it is not used anymore --- .../PropertyEditors/ContentPickerPropertyEditor.cs | 7 +------ .../PropertyEditors/MultiNodeTreePickerPropertyEditor.cs | 9 +-------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs index 4d58060164..be10cd83e2 100644 --- a/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs @@ -33,17 +33,12 @@ namespace Umbraco.Web.PropertyEditors internal class ContentPickerPreValueEditor : PreValueEditor { - [PreValueField("showOpenButton", "Show open button", "boolean")] + [PreValueField("showOpenButton", "Show open button (this feature is in preview!)", "boolean", Description = " Opens the node in a dialog")] public string ShowOpenButton { get; set; } - - [PreValueField("showEditButton", "Show edit button (this feature is in preview!)", "boolean")] - public string ShowEditButton { get; set; } [PreValueField("startNodeId", "Start node", "treepicker")] public int StartNodeId { get; set; } - [PreValueField("showPathOnHover", "Show path when hovering items", "boolean")] - public bool ShowPathOnHover { get; set; } } } } \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs index b3d3f02448..82daf80e34 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs @@ -45,16 +45,9 @@ namespace Umbraco.Web.PropertyEditors [PreValueField("maxNumber", "Maximum number of items", "number")] public string MaxNumber { get; set; } - - [PreValueField("showOpenButton", "Show open button", "boolean")] + [PreValueField("showOpenButton", "Show open button (this feature is in preview!)", "boolean", Description = " Opens the node in a dialog")] public string ShowOpenButton { get; set; } - [PreValueField("showEditButton", "Show edit button (this feature is in preview!)", "boolean")] - public string ShowEditButton { get; set; } - - [PreValueField("showPathOnHover", "Show path when hovering items", "boolean")] - public bool ShowPathOnHover { get; set; } - /// /// This ensures the multiPicker pre-val is set based on the maxNumber of nodes set /// From a54f4aeffdf139533f356f541ab53ea1bb8427cd Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 17 Jan 2017 19:10:22 +0100 Subject: [PATCH 16/24] added documentation for umb-node-preview component --- .../components/umbnodepreview.directive.js | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js index 8d63623efb..77f6f06a30 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js @@ -1,3 +1,91 @@ +/** +@ngdoc directive +@name umbraco.directives.directive:umbNodePreview +@restrict E +@scope + +@description +Added in Umbraco v. 7.6: Use this directive to render a node preview. + +

Markup example

+
+    
+ +
+ + +
+ +
+
+ +

Controller example

+
+    (function () {
+        "use strict";
+    
+        function Controller() {
+    
+            var vm = this;
+    
+            vm.allowRemove = true;
+            vm.allowOpen = true;
+            vm.sortable = true;
+    
+            vm.nodes = [
+                {
+                    "icon": "icon-document",
+                    "name": "My node 1",
+                    "published": true,
+                    "description": "A short description of my node"
+                },
+                {
+                    "icon": "icon-document",
+                    "name": "My node 2",
+                    "published": true,
+                    "description": "A short description of my node"
+                }
+            ];
+    
+            vm.remove = remove;
+            vm.open = open;
+    
+            function remove(index, nodes) {
+                alert("remove node");
+            }
+    
+            function open(node) {
+                alert("open node");
+            }
+    
+        }
+    
+        angular.module("umbraco").controller("My.NodePreviewController", Controller);
+    
+    })();
+
+ +@param {string} icon (binding): The node icon. +@param {string} name (binding): The node name. +@param {boolean} published (binding): The node pusblished state. +@param {string} description (binding): A short description. +@param {boolean} sortable (binding): Will add a move cursor on the node preview. Can used in combination with ui-sortable. +@param {boolean} allowRemove (binding): Show/Hide the remove button. +@param {boolean} allowOpen (binding): Show/Hide the open button. +@param {function} onRemove (expression): Callback function when the remove button is clicked. +@param {function} onOpen (expression): Callback function when the open button is clicked. +**/ + (function () { 'use strict'; From 4fe12d893044478a4b2f275b50ad15542e7fa16c Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 17 Jan 2017 19:26:00 +0100 Subject: [PATCH 17/24] clean up dependencies in content picker controller --- .../propertyeditors/contentpicker/contentpicker.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index e5852c29fd..1ebfe81905 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -1,7 +1,7 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it -function contentPickerController($scope, dialogService, entityResource, contentResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location, $timeout, miniEditorHelper) { +function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, miniEditorHelper) { function trim(str, chr) { var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g'); From 4612183b13f8a15c6154e8c470137306e1a6f44f Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 17 Jan 2017 19:27:39 +0100 Subject: [PATCH 18/24] fix unit test typo --- .../unit/app/propertyeditors/content-picker-controller.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js index f34f8088df..d5e3a7a496 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js @@ -90,7 +90,7 @@ describe('Content picker controller tests', function () { }, 1000); }); - it("Adding a dublicate item should note update renderModel, ids and model.value", function(){ + it("Adding a duplicate item should note update renderModel, ids and model.value", function(){ scope.add(item); scope.$apply(); setTimeout(function(){ From 2964f9a1c7596ecc4796d715dd0ef2257018e67a Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 17 Jan 2017 20:48:00 +0100 Subject: [PATCH 19/24] clean up redundant code --- .../contentpicker/contentpicker.controller.js | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 1ebfe81905..c191ee5518 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -194,19 +194,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper }); if (currIds.indexOf(item.id) < 0) { - - // get url for content and media items - if(entityType !== "Member") { - entityResource.getUrl(item.id, entityType).then(function(data){ - // update url - item.url = data.url; - // push item to render model - addSelectedItem(item); - }); - } else { - addSelectedItem(item); - } - + setEntityUrl(item); } }; @@ -241,17 +229,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper }); if (entity) { - // get url for content and media items - if(entityType !== "Member") { - entityResource.getUrl(entity.id, entityType).then(function(data){ - // update url - entity.url = data.url; - // push item to render model - addSelectedItem(entity); - }); - } else { - addSelectedItem(entity); - } + setEntityUrl(entity); } }); @@ -261,6 +239,20 @@ function contentPickerController($scope, entityResource, editorState, iconHelper }); + function setEntityUrl(entity) { + // get url for content and media items + if(entityType !== "Member") { + entityResource.getUrl(entity.id, entityType).then(function(data){ + // update url + entity.url = data.url; + // push item to render model + addSelectedItem(entity); + }); + } else { + addSelectedItem(entity); + } + } + function addSelectedItem(item) { // set icon From c6ed4eaff695a3ad20a198e4229a7e07c78c993d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 17 Jan 2017 21:27:24 +0100 Subject: [PATCH 20/24] only sort lists with more than one item --- .../contentpicker/contentpicker.controller.js | 21 ++++++++++++++++++- .../contentpicker/contentpicker.html | 4 ++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index c191ee5518..3a238e141f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -39,6 +39,9 @@ function contentPickerController($scope, entityResource, editorState, iconHelper else { $scope.contentPickerForm.maxCount.$setValidity("maxCount", true); } + + setSortingState($scope.renderModel); + }); } @@ -59,6 +62,14 @@ function contentPickerController($scope, entityResource, editorState, iconHelper } }; + // sortable options + $scope.sortableOptions = { + distance: 10, + tolerance: "pointer", + scroll: true, + zIndex: 6000 + }; + if ($scope.model.config) { //merge the server config on top of the default config, then set the server config to use the result $scope.model.config = angular.extend(defaultConfig, $scope.model.config); @@ -78,7 +89,6 @@ function contentPickerController($scope, entityResource, editorState, iconHelper $scope.allowOpenButton = entityType === "Document" || entityType === "Media"; $scope.allowEditButton = entityType === "Document"; $scope.allowRemoveButton = true; - $scope.sortable = true; //the dialog options for the picker var dialogOptions = { @@ -287,6 +297,15 @@ function contentPickerController($scope, entityResource, editorState, iconHelper } + function setSortingState(items) { + // disable sorting if the list only consist of one item + if(items.length > 1) { + $scope.sortableOptions.disabled = false; + } else { + $scope.sortableOptions.disabled = true; + } + } + } angular.module('umbraco').controller("Umbraco.PropertyEditors.ContentPickerController", contentPickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index ca6f9ac826..900cf6b416 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -2,14 +2,14 @@ -
+
Date: Tue, 17 Jan 2017 23:01:40 +0100 Subject: [PATCH 21/24] remove option to open media items in mini editor --- .../propertyeditors/contentpicker/contentpicker.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 3a238e141f..22312511ce 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -86,7 +86,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper : $scope.model.config.startNode.type === "media" ? "Media" : "Document"; - $scope.allowOpenButton = entityType === "Document" || entityType === "Media"; + $scope.allowOpenButton = entityType === "Document"; $scope.allowEditButton = entityType === "Document"; $scope.allowRemoveButton = true; From fc5bf81991fa79f10fd8753da22de8bec7f3abf6 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 18 Jan 2017 12:33:43 +0000 Subject: [PATCH 22/24] Returns a string as opposed to a Dynamic object - however cannot return a string object from this method as the result is wrapped in quotes hence we use a HttpResponseMessage & StringContent to return a raw string from the API endpoint --- src/Umbraco.Web/Editors/EntityController.cs | 31 ++++++++++++++------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index cfc04a7f20..9f8b15449c 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using System.Linq; +using System.Net.Http; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models; using Umbraco.Web.WebApi.Filters; @@ -146,19 +147,27 @@ namespace Umbraco.Web.Editors - - public dynamic GetUrl(int id, UmbracoEntityTypes type) + /// + /// Gets the url of an entity + /// + /// Int id of the entity to fetch URL for + /// The tpye of entity such as Document, Media, Member + /// The URL or path to the item + public HttpResponseMessage GetUrl(int id, UmbracoEntityTypes type) { - dynamic result = new System.Dynamic.ExpandoObject(); - + var returnUrl = string.Empty; - if(type == UmbracoEntityTypes.Document) + if (type == UmbracoEntityTypes.Document) { var foundUrl = Umbraco.Url(id); if (string.IsNullOrEmpty(foundUrl) == false && foundUrl != "#") { - result.url = foundUrl; - return result; + returnUrl = foundUrl; + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(returnUrl) + }; } } @@ -169,10 +178,12 @@ namespace Umbraco.Web.Editors ancestors = ancestors.Skip(1); } - result.url = "/" + string.Join("/", ancestors.Select(x => x.Name) ); + returnUrl = "/" + string.Join("/", ancestors.Select(x => x.Name)); - - return result; + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(returnUrl) + }; } /// From 40ea54007fab4e08e540cbf3444064da703a22d1 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 18 Jan 2017 12:34:58 +0000 Subject: [PATCH 23/24] Update the usage of GetUrl - to just use the raw string value as opposed to a JSON object. Updated related JS object mock as well --- .../src/common/mocks/resources/entity.mocks.js | 8 ++------ .../contentpicker/contentpicker.controller.js | 3 ++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js index a98240111e..8c03679183 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js @@ -38,12 +38,8 @@ angular.module('umbraco.mocks'). if (!mocksUtils.checkAuth()) { return [401, null, null]; } - - var urlOrbject = { - "url": "url" - }; - return [200, urlOrbject, null]; + return [200, "url", null]; } @@ -61,7 +57,7 @@ angular.module('umbraco.mocks'). $httpBackend .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetById?')) .respond(returnEntitybyId); - + $httpBackend .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetUrl?')) .respond(returnEntityUrl); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 22312511ce..d39027e014 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -254,7 +254,8 @@ function contentPickerController($scope, entityResource, editorState, iconHelper if(entityType !== "Member") { entityResource.getUrl(entity.id, entityType).then(function(data){ // update url - entity.url = data.url; + entity.url = data; + // push item to render model addSelectedItem(entity); }); From 1fd98dba30aff6183ec6c82613a7a2ac8b2f72ea Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 18 Jan 2017 14:50:51 +0100 Subject: [PATCH 24/24] make launch mini editor service return a promise so we can update a node when it is saved --- .../src/common/services/minieditorhelper.service.js | 12 +++++++++++- .../contentpicker/contentpicker.controller.js | 11 ++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js index 9c974f9ca9..693457c7e8 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js @@ -1,12 +1,14 @@ (function () { 'use strict'; - function miniEditorHelper(dialogService, editorState, fileManager, contentEditingHelper) { + function miniEditorHelper(dialogService, editorState, fileManager, contentEditingHelper, $q) { var launched = false; function launchMiniEditor(node) { + var deferred = $q.defer(); + launched = true; //We need to store the current files selected in the file manager locally because the fileManager @@ -47,6 +49,9 @@ } launched = false; + + deferred.resolve(data); + }, closeCallback: function () { //reset the fileManager to what it was @@ -59,9 +64,14 @@ editorState.set(currEditorState); launched = false; + + deferred.reject(); + } }); + return deferred.promise; + } var service = { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index d39027e014..ba29b92477 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -213,7 +213,16 @@ function contentPickerController($scope, entityResource, editorState, iconHelper }; $scope.openMiniEditor = function(node) { - miniEditorHelper.launchMiniEditor(node); + miniEditorHelper.launchMiniEditor(node).then(function(updatedNode){ + // update the node + node.name = updatedNode.name; + node.published = updatedNode.hasPublishedVersion; + if(entityType !== "Member") { + entityResource.getUrl(updatedNode.id, entityType).then(function(data){ + node.url = data; + }); + } + }); }; var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {