From 21114cbbd774114fa90be616311e3ca280c49b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 14 Jan 2020 10:54:11 +0100 Subject: [PATCH 001/508] gulp support for less files in views folder --- src/Umbraco.Web.UI.Client/gulp/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/gulp/config.js b/src/Umbraco.Web.UI.Client/gulp/config.js index 59e8bf6c05..a807d63f5f 100755 --- a/src/Umbraco.Web.UI.Client/gulp/config.js +++ b/src/Umbraco.Web.UI.Client/gulp/config.js @@ -17,7 +17,7 @@ module.exports = { installer: { files: "./src/less/installer.less", watch: "./src/less/**/*.less", out: "installer.css" }, nonodes: { files: "./src/less/pages/nonodes.less", watch: "./src/less/**/*.less", out: "nonodes.style.min.css"}, preview: { files: "./src/less/canvas-designer.less", watch: "./src/less/**/*.less", out: "canvasdesigner.css" }, - umbraco: { files: "./src/less/belle.less", watch: "./src/less/**/*.less", out: "umbraco.css" }, + umbraco: { files: "./src/less/belle.less", watch: "./src/**/*.less", out: "umbraco.css" }, rteContent: { files: "./src/less/rte-content.less", watch: "./src/less/**/*.less", out: "rte-content.css" } }, From 678e31834d966adc59b6f423d403e1dfc88a3744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 09:06:27 +0100 Subject: [PATCH 002/508] registrer Block List Editor --- src/Umbraco.Core/Constants-PropertyEditors.cs | 5 ++++ .../BlockListPropertyEditor.cs | 30 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs diff --git a/src/Umbraco.Core/Constants-PropertyEditors.cs b/src/Umbraco.Core/Constants-PropertyEditors.cs index eb2b3525a7..dcd7eb9d05 100644 --- a/src/Umbraco.Core/Constants-PropertyEditors.cs +++ b/src/Umbraco.Core/Constants-PropertyEditors.cs @@ -36,6 +36,11 @@ namespace Umbraco.Core /// public static class Aliases { + /// + /// Block List. + /// + public const string BlockList = "Umbraco.BlockList"; + /// /// CheckBox List. /// diff --git a/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs new file mode 100644 index 0000000000..da44151ec9 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs @@ -0,0 +1,30 @@ +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// Represents a block list property editor. + /// + [DataEditor( + Constants.PropertyEditors.Aliases.BlockList, + "Block List", + "blocklist", + ValueType = ValueTypes.Json, + Group = Constants.PropertyEditors.Groups.Lists, + Icon = "icon-thumbnail-list")] + public class BlockListPropertyEditor : DataEditor + { + public BlockListPropertyEditor(ILogger logger) + : base(logger) + { } + + #region Pre Value Editor + //protected override IConfigurationEditor CreateConfigurationEditor() => new BlockEditorListConfigurationEditor(); + + #endregion + + } +} From 897517d4ec0675cf10c375dd96cf3ad29df5f32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 09:07:13 +0100 Subject: [PATCH 003/508] do not set focus if already set + clear timeout if running --- .../components/forms/focuswhen.directive.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/focuswhen.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/focuswhen.directive.js index d8dbcc1012..dda5c51175 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/focuswhen.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/focuswhen.directive.js @@ -2,13 +2,20 @@ angular.module("umbraco.directives").directive('focusWhen', function ($timeout) return { restrict: 'A', link: function (scope, elm, attrs, ctrl) { + + var delayTimer; + attrs.$observe("focusWhen", function (newValue) { - if (newValue === "true") { - $timeout(function () { - elm.trigger("focus"); - }); + if (newValue === "true" && document.activeelement !== elm[0]) { + delayTimer = $timeout(function () { + elm[0].focus(); + }); } }); + + scope.$on('$destroy', function() { + $timeout.cancel(delayTimer); + }); } }; }); From 85d4cd9be3995f10fc4699c259267decbaa49a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 09:07:41 +0100 Subject: [PATCH 004/508] compile JS after HTML, cause HTML is embeded in JS. --- src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js index 24a6e65540..42f25ccb23 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js @@ -12,6 +12,7 @@ var processLess = require('../util/processLess'); //const { less } = require('./less'); //const { views } = require('./views'); +var {js} = require('./js'); function watchTask(cb) { @@ -39,6 +40,7 @@ function watchTask(cb) { viewWatcher.on('change', function(path, stats) { console.log("copying " + group.files + " to " + config.root + config.targets.views + group.folder); src(group.files).pipe( dest(config.root + config.targets.views + group.folder) ); + js(); }); } }); From 4caad6e42953c15d5124f6c7a70c9bcba464f055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 09:09:14 +0100 Subject: [PATCH 005/508] ability to turn on/off focus outlines --- .../src/main.controller.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/main.controller.js b/src/Umbraco.Web.UI.Client/src/main.controller.js index 93870f8a56..3601cb9652 100644 --- a/src/Umbraco.Web.UI.Client/src/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/main.controller.js @@ -32,13 +32,17 @@ function MainController($scope, $location, appState, treeService, notificationsS // For more information about this approach, see https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2 function handleFirstTab(evt) { if (evt.keyCode === 9) { - $scope.tabbingActive = true; - $scope.$digest(); - window.removeEventListener('keydown', handleFirstTab); - window.addEventListener('mousedown', disableTabbingActive); + enableTabbingActive(); } } + function enableTabbingActive() { + $scope.tabbingActive = true; + $scope.$digest(); + window.addEventListener('mousedown', disableTabbingActive); + window.removeEventListener("keydown", handleFirstTab); + } + function disableTabbingActive(evt) { $scope.tabbingActive = false; $scope.$digest(); @@ -48,6 +52,12 @@ function MainController($scope, $location, appState, treeService, notificationsS window.addEventListener("keydown", handleFirstTab); + $scope.$on("showFocusOutline", function() { + $scope.tabbingActive = true; + window.addEventListener('mousedown', disableTabbingActive); + window.removeEventListener("keydown", handleFirstTab); + }); + $scope.removeNotification = function (index) { notificationsService.remove(index); From 761b5ecc01323f8393f77c6d63996177eab1e9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 09:09:54 +0100 Subject: [PATCH 006/508] Add BlockListPropertyEditor to csproj --- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 5d29e53d4a..997b13ce8d 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -888,6 +888,7 @@ + From 034567bb0ee3e918f715b47ca3a2605ef059b7d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 11:11:05 +0100 Subject: [PATCH 007/508] reverted gulp change --- src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js index 42f25ccb23..3c90003e30 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js @@ -9,11 +9,6 @@ var MergeStream = require('merge-stream'); var processJs = require('../util/processJs'); var processLess = require('../util/processLess'); -//const { less } = require('./less'); -//const { views } = require('./views'); - -var {js} = require('./js'); - function watchTask(cb) { var watchInterval = 500; @@ -40,7 +35,6 @@ function watchTask(cb) { viewWatcher.on('change', function(path, stats) { console.log("copying " + group.files + " to " + config.root + config.targets.views + group.folder); src(group.files).pipe( dest(config.root + config.targets.views + group.folder) ); - js(); }); } }); From 382ead27f20ee688a223405177fddf39850ad993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 13:30:50 +0100 Subject: [PATCH 008/508] Run JS when Views has been updated, cause we have embeded templates. --- src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js index 3c90003e30..a94314abd6 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js @@ -9,6 +9,8 @@ var MergeStream = require('merge-stream'); var processJs = require('../util/processJs'); var processLess = require('../util/processLess'); +var {js} = require('./js'); + function watchTask(cb) { var watchInterval = 500; @@ -33,8 +35,15 @@ function watchTask(cb) { if(group.watch !== false) { viewWatcher = watch(group.files, { ignoreInitial: true, interval: watchInterval }); viewWatcher.on('change', function(path, stats) { + console.log("copying " + group.files + " to " + config.root + config.targets.views + group.folder); - src(group.files).pipe( dest(config.root + config.targets.views + group.folder) ); + + return MergeStream( + src(group.files) + .pipe( dest(config.root + config.targets.views + group.folder) ) + , js() + ); + }); } }); From 2583d0c7d03727eaab5bb8ca3b4b5fe9b0e4a6f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 17:34:49 +0100 Subject: [PATCH 009/508] BlockList PropertyEditor --- src/Umbraco.Web.UI.Client/src/less/belle.less | 9 + .../imageblock/imageblock.editor.html | 3 + .../imageblock/imageblock.editor.less | 12 + .../labelblock/labelblock.editor.html | 4 + .../labelblock/labelblock.editor.less | 34 ++ .../textareablock.editor.controller.js | 29 + .../textareablock/textareablock.editor.html | 15 + .../textareablock/textareablock.editor.less | 27 + .../elementeditor/elementeditor.controller.js | 23 + .../elementeditor/elementeditor.html | 84 +++ .../blocklist/blocklist.component.html | 104 ++++ .../blocklist/blocklist.component.js | 553 ++++++++++++++++++ .../blocklist/blocklist.component.less | 217 +++++++ .../propertyeditors/blocklist/blocklist.html | 1 + 14 files changed, 1115 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.html diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index b5e032f9fb..78a10da78d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -195,6 +195,10 @@ @import "components/contextdialogs/umb-dialog-datatype-delete.less"; +// Property Editors +@import "../views/propertyeditors/blocklist/blocklist.component.less"; + + // Utilities @import "utilities/layout/_display.less"; @import "utilities/theme/_opacity.less"; @@ -218,6 +222,11 @@ // Used for prevalue editors @import "components/prevalues/multivalues.less"; +// Block Elements +@import "../views/blockelements/labelblock/labelblock.editor.less"; +@import "../views/blockelements/textareablock/textareablock.editor.less"; +@import "../views/blockelements/imageblock/imageblock.editor.less"; + // Dashboards @import "dashboards/getstarted.less"; @import "dashboards/umbraco-forms.less"; diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html new file mode 100644 index 0000000000..c77f19ba67 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html @@ -0,0 +1,3 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less new file mode 100644 index 0000000000..99b1bb53f2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less @@ -0,0 +1,12 @@ +.blockelement-imageblock-editor { + + padding-top: 2px; + padding-bottom: 2px; + + img { + width: 100%; + border: none; + resize: none; + border-radius: @baseBorderRadius; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html new file mode 100644 index 0000000000..f0678c76e9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html @@ -0,0 +1,4 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less new file mode 100644 index 0000000000..610773528b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less @@ -0,0 +1,34 @@ +.blockelement-labelblock-editor { + + width: 100%; + min-height: 48px; + border: 1px solid @gray-9; + border-radius: @baseBorderRadius; + + color: @ui-action-discreet-type; + + text-align: left; + padding-left: 20px; + padding-bottom: 2px; + margin-bottom: 2px; + margin-top: 2px; + + user-select: none; + + transition: border-color 120ms; + + i { + font-size: 22px; + display: inline-block; + vertical-align: middle; + } + span { + display: inline-block; + vertical-align: middle; + } + + &:hover { + color: @ui-action-discreet-type-hover; + border-color: @gray-8; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js new file mode 100644 index 0000000000..761fc57e03 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js @@ -0,0 +1,29 @@ +//used for the media picker dialog +angular.module("umbraco") +.controller("Umbraco.Editors.TextAreaBlockElementEditorController", + function ($scope) { + + var vm = this; + + vm.firstProperty = $scope.block.content.tabs[0].properties[0]; +/* + vm.submitOnEnter = function($event) { + if($event && $event.keyCode === 13 && !$event.shiftKey && !$event.ctrlKey) { + var target = $event.target; + if(target.selectionStart === target.selectionEnd && target.selectionEnd === target.textLength) { + //&& (target.textLength === 0 || /\r|\n/.test(target.value.charAt(target.textLength - 1))) + $scope.$emit("showFocusOutline"); + $scope.blockApi.showCreateOptionsFor($scope.block, $event); + } + } + } +*/ + vm.onBlur = function() { + if (vm.firstProperty.value === null || vm.firstProperty.value === "") { + $scope.blockApi.deleteBlock($scope.block); + } + } + + } + +); diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html new file mode 100644 index 0000000000..405c8634b4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html @@ -0,0 +1,15 @@ + +
+ + + + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less new file mode 100644 index 0000000000..492025274a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less @@ -0,0 +1,27 @@ +.blockelement-textareablock-editor { + + width: 100%; + padding-bottom: 24px; + padding-top: 24px; + + padding-left: 24px; + padding-right: 24px; + + min-height: 64px; + box-sizing: border-box; + + textarea { + display: block; + width: 100%; + max-width: 640px; + margin-left: auto; + margin-right: auto; + border: none; + resize: none; + overflow: auto; + + font-size: 18px; + font-family: Georgia,Cambria,"Times New Roman",Times,serif; + line-height: 1.25; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js new file mode 100644 index 0000000000..d421418297 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js @@ -0,0 +1,23 @@ +//used for the media picker dialog +angular.module("umbraco") +.controller("Umbraco.Editors.ElementEditorController", + function ($scope) { + + var vm = this; + + vm.content = $scope.model.block.content; + + vm.saveAndClose = function() { + if ($scope.model && $scope.model.submit) { + $scope.model.submit($scope.model); + } + } + + vm.close = function() { + if ($scope.model && $scope.model.close) { + $scope.model.close($scope.model); + } + } + + } +); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html new file mode 100644 index 0000000000..71aab124e6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html @@ -0,0 +1,84 @@ +
+ + + + + + +
+ + + +
+ +
+
+ +
+ +
+
{{ group.label }}
+
+ +
+ + +
+ + +
+ +
+
+ +
+ + + + + +
+
+
+
+ +
+ + + + + + + + + + + + + + + + +
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html new file mode 100644 index 0000000000..e38858c8b7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -0,0 +1,104 @@ +
+ + +
+ +
+ +
+ + + + +
+ +
+
+
+ +

No editor

+
+ +
+ + +
+ +
+ +
+
+ + + + + +
+ + + + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js new file mode 100644 index 0000000000..6283bd1026 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -0,0 +1,553 @@ +(function () { + 'use strict'; + + angular + .module('umbraco') + .component('blockListPropertyEditor', { + templateUrl: 'views/propertyeditors/blocklist/blocklist.component.html', + controller: BlockListController, + controllerAs: 'vm', + bindings: { + + }, + require: { + umbProperty: '?^umbProperty', + propertyForm: '?^propertyForm' + } + }); + + function BlockListController($scope, $interpolate, editorService, clipboardService, localizationService, overlayService) { + + var vm = this; + var model = $scope.$parent.$parent.model; + + $scope.moveFocusToBlock = null; + + vm.quickMenuVisible = false; + vm.quickMenuIndex = 0; + + vm.quickMenuAddNewBlock = function(type) { + addNewBlock(vm.quickMenuIndex, type); + vm.quickMenuVisible = false; + } + + vm.availableBlockTypes = [ + { + alias: "pageModule", + name: "Module", + icon: "icon-document", + prototype_paste_data: { + + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "{{pageTitle | truncate:true:36}}", + labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), + editor: "views/blockelements/labelblock/labelblock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ] + } + } + }, + { + alias: "contentTypeAlias", + name: "contentTypeName", + icon: "icon-text", + prototype_paste_data: { + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "Label", + editor: "views/blockelements/textareablock/textareablock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ] + } + } + }, + { + alias: "contentTypeAlias", + name: "contentTypeName", + icon: "icon-picture", + prototype_paste_data: { + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "Label", + editor: "views/blockelements/imageblock/imageblock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Let's have a chat", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ], + temp_image: "/umbraco/assets/img/login.jpg" + } + } + } + ]; + + // var defaultBlockType... + + // TODO: get icon, properties etc. from available types? + vm.blocks = [ + { + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "{{pageTitle | truncate:true:36}}", + labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), + key: 1, + editor: "views/blockelements/labelblock/labelblock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn't distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ] + } + }, + { + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "{{pageTitle | truncate:true:36}}", + labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), + key: 2, + editor: "views/blockelements/labelblock/labelblock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ] + } + }, + { + + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "{{pageTitle | truncate:true:36}}", + labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), + key: 3, + editor: "views/blockelements/labelblock/labelblock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ] + } + } + ]; + + function setDirty() { + if (vm.propertyForm) { + vm.propertyForm.$setDirty(); + } + }; + + function addNewBlock(index, type) { + + var block = angular.copy(type.prototype_paste_data); + + vm.blocks.splice(index, 0, block); + $scope.moveFocusToBlock = block; + + } + /* + function moveFocusToNextBlock(blockModel, $event) { + var index = vm.blocks.indexOf(blockModel); + if(index < vm.blocks.length) { + var nextBlock = vm.blocks[index+1]; + $scope.moveFocusToBlock = nextBlock; + } else { + showCreateOptions(blockModel, $event); + } + } + */ + + vm.showCreateOptionsFor = function(blockModel, $event) { + var index = vm.blocks.indexOf(blockModel); + $event.preventDefault(); + showCreateOptionsAt(index); + } + function showCreateOptionsAt(index) { + vm.quickMenuIndex = index; + vm.quickMenuVisible = true; + window.addEventListener("keydown", handleTypingInCreateOptions); + } + + function handleTypingInCreateOptions(event) { + if (event.ctrlKey || event.metaKey || event.altKey) + return; + + if ( + (event.keyCode === 13) // enter + || + (event.keyCode >= 48 && event.keyCode <= 90)// 0 to z + || + (event.keyCode >= 96 && event.keyCode <= 111)// numpads + || + (event.keyCode >= 186 && event.keyCode <= 222)// semi-colon and a lot of other special characters + ) { + // Continue writting... needs to know default text-element. if we have one. + } + } + + function hideCreateOptions() { + vm.quickMenuVisible = false; + window.removeEventListener("keydown", handleTypingInCreateOptions); + } + + vm.onCreateOptionsBlur = function($event) { + + if(!$($event.relatedTarget).is(".umb-block-list__block--create-bar > button")) { + hideCreateOptions(); + } + + } + + vm.getBlockLabel = function(block) { + + var name = ""; + + var props = new Object(); + + var tab = block.content.tabs[0]; + // TODO: need to look up all tabs... + for(const property of tab.properties) { + props[property.alias] = property.value; + } + + if(block.labelInterpolate) { + return block.labelInterpolate(props); + } + + return "block.label"; + } + + vm.deleteBlock = function(block) { + var index = vm.blocks.indexOf(block); + if(index !== -1) { + vm.blocks.splice(index, 1); + } + if(vm.quickMenuIndex > index) { + vm.quickMenuIndex--; + } + } + + vm.editBlock = function(blockModel) { + + var elementEditor = { + block: blockModel, + view: "views/common/infiniteeditors/elementeditor/elementeditor.html", + size: "large", + submit: function(model) { + blockModel.content = model.block.content; + editorService.close(); + }, + close: function() { + editorService.close(); + } + }; + + // open property settings editor + editorService.open(elementEditor); + } + + vm.showCreateDialog = function (createIndex, $event) { + + if (vm.blockTypePicker) { + return; + } + + if (vm.availableBlockTypes.length === 0) { + return; + } + + vm.blockTypePicker = { + show: true, + size: vm.availableBlockTypes.length > 6 ? "medium" : "small", + filter: vm.availableBlockTypes.length > 12 ? true : false, + orderBy: "$index", + view: "itempicker", + event: $event, + availableItems: vm.availableBlockTypes, + submit: function (model) { + if (model && model.selectedItem) { + addNewBlock(createIndex, model.selectedItem); + } + vm.blockTypePicker.close(); + }, + close: function () { + vm.blockTypePicker.show = false; + vm.blockTypePicker = null; + } + }; + + }; + + vm.requestCopyBlock = function(block) { + console.log("copy") + } + vm.requestDeleteBlock = function(block) { + localizationService.localizeMany(["content_nestedContentDeleteItem", "general_delete", "general_cancel", "contentTypeEditor_yesDelete"]).then(function (data) { + const overlay = { + title: data[1], + content: data[0], + closeButtonLabel: data[2], + submitButtonLabel: data[3], + submitButtonStyle: "danger", + close: function () { + overlayService.close(); + }, + submit: function () { + vm.deleteBlock(block); + overlayService.close(); + } + }; + + overlayService.open(overlay); + }); + } + + vm.showCopy = clipboardService.isSupported(); + + + + vm.sorting = false; + vm.sortableOptions = { + axis: "y", + cursor: "grabbing", + handle: '.umb-block-list__block', + cancel: 'input,textarea,select,option', + classes: '.blockelement--dragging', + distance: 5, + tolerance: "pointer", + scroll: true, + start: function (ev, ui) { + $scope.$apply(function () { + vm.sorting = true; + }); + }, + update: function (ev, ui) { + setDirty(); + }, + stop: function (ev, ui) { + $scope.$apply(function () { + vm.sorting = false; + }); + } + }; + + $scope.blockApi = { + showCreateOptionsFor: vm.showCreateOptionsFor, + removeBlock: vm.removeBlock + } + + + var copyAllEntriesAction = { + labelKey: 'clipboard_labelForCopyAllEntries', + labelTokens: [model.label], + icon: 'documents', + method: function () {}, + isDisabled: true + } + + var propertyActions = [ + copyAllEntriesAction + ]; + + this.$onInit = function () { + if (this.umbProperty) { + this.umbProperty.setPropertyActions(propertyActions); + } + }; + + + } + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less new file mode 100644 index 0000000000..a26dda4202 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less @@ -0,0 +1,217 @@ +@umb-block-list__item_minimum_height: 48px; + +.umb-block-list { + padding-bottom:10px; +} + +.umb-block-list__wrapper { + position: relative; + max-width: 1024px; + > .ui-sortable > .ui-sortable-helper > .umb-block-list__block > .umb-block-list__block--content > * { + box-shadow: 0px 5px 10px 0 rgba(0,0,0,.2); + } +} + +.umb-block-list__block { + position: relative; + width: 100%; + cursor: grab; + + .umb-block-list__block--head { + opacity: 0; + transition: opacity 120ms; + } + .umb-block-list__block--actions { + opacity: 0; + transition: opacity 120ms; + } + + &:hover, &:focus, &:focus-within { + .umb-block-list__block--head { + opacity: 1; + } + + .umb-block-list__block--actions { + opacity: 1; + } + } + + &:focus, &:focus-within { + .umb-block-list__block--head { + &::before { + background-color: @blueMid; + } + } + } +} + +.umb-block-list__block--head { + position: absolute; + top: 0; + left: -180px;// 160px from control-header + 20px from spacing. + bottom: 0; + width: 180px;// 160px from control-header + 20px from spacing. + user-select: none; + padding-top: 6px; + padding-right: 14px; + box-sizing: border-box; + color: @gray-5; + background-color: rgba(255, 255, 255, .96); + box-shadow: 0 0 6px 6px rgba(255, 255, 255, .96); + text-align: right; + &::before { + content: ''; + position: absolute; + top: 6px; + bottom: 6px; + right: 4px; + width: 1px; + background-color: @gray-10; + } + + small { + text-align: left; + margin-left: 4px; + margin-bottom: 4px; + } +} +label.umb-block-list__block--head { + cursor: grab; +} + +.umb-block-list__block--actions { + position: absolute; + top: 10px; + right: 10px; + font-size: 0; + background-color: rgba(255, 255, 255, .96); + border-radius: 14px; + padding-left: 5px; + padding-right: 5px; + .action { + display: inline-block; + color: @ui-action-discreet-type; + font-size: 18px; + padding: 5px; + &:hover { + color: @ui-action-discreet-type-hover; + } + } +} + +.umb-block-list__block--content { + position: relative; + width: 100%; + min-height: @umb-block-list__item_minimum_height; + background-color: @white; + border-radius: @baseBorderRadius; +} + + +.umb-block-list__block--create-button { + position: absolute; + width: 100%; + z-index:1; + opacity: 0; + outline: none; + height: 20px; + margin-top: -10px; + padding-top: 10px; + margin-bottom: -10px; + transition: opacity 240ms; + + &::before { + content: ''; + position: absolute; + background-color: @ui-outline; + border-radius: 2px; + top:9px; + right: 0; + left: 0; + height: 2px; + animation: umb-block-list__block--create-button 800ms ease-in-out infinite; + @keyframes umb-block-list__block--create-button { + 0% { opacity: 0.5; } + 50% { opacity: 1; } + 100% { opacity: 0.5; } + } + } + &::after { + content: "+"; + margin-left: auto; + margin-right: auto; + margin-top: -16px; + width: 28px; + height: 25px; + padding-bottom: 3px; + border-radius: 3em; + border: 2px solid @ui-outline; + display: flex; + justify-content: center; + align-items: center; + color: @ui-outline; + font-size: 20px; + font-weight: 800; + background-color: rgba(255, 255, 255, .96); + box-shadow: 0 0 0 2px rgba(255, 255, 255, .96); + transform: scale(0); + transition: transform 240ms ease-in; + } + &:focus { + &::after { + border: 2px solid @ui-outline; + } + } + &:hover, &:focus { + opacity: 1; + transition-duration: 120ms; + &::after { + transform: scale(1); + transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275); + } + } +} +/* +.umb-block-list__block--create-bar { + button { + display: inline-block; + width: 120px; + height: 120px; + border-radius: @baseBorderRadius; + text-align: center; + font-size: 12px; + i { + font-size: 30px; + line-height: 20px; + margin-bottom: 10px; + display: block; + } + } +} +*/ +.umb-block-list__create-button { + display: flex; + width: 100%; + align-items: center; + justify-content: center; + border: 1px dashed @ui-action-discreet-border; + color: @ui-action-discreet-type; + font-weight: bold; + margin: 2px 0; + padding: 5px 15px; + box-sizing: border-box; + border-radius: @baseBorderRadius; +} + +.umb-block-list__create-button:hover { + color: @ui-action-discreet-type-hover; + border-color: @ui-action-discreet-border-hover; + text-decoration: none; +} + +.umb-block-list__create-button.--disabled, +.umb-block-list__create-button.--disabled:hover { + color: @gray-7; + border-color: @gray-7; + cursor: default; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.html new file mode 100644 index 0000000000..198dab4f5f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.html @@ -0,0 +1 @@ + From f988a6a31b1a81459f0afb4c77e860cbd45a5bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 27 Jan 2020 07:37:29 +0100 Subject: [PATCH 010/508] more demo content --- .../blocklist/blocklist.component.js | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 6283bd1026..5fc17fcafd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -84,8 +84,8 @@ }, { alias: "contentTypeAlias", - name: "contentTypeName", - icon: "icon-text", + name: "Text", + icon: "icon-info", prototype_paste_data: { elementType: { alias: 'contentTypeAlias', @@ -132,7 +132,7 @@ }, { alias: "contentTypeAlias", - name: "contentTypeName", + name: "Image", icon: "icon-picture", prototype_paste_data: { elementType: { @@ -178,6 +178,55 @@ temp_image: "/umbraco/assets/img/login.jpg" } } + }, + { + alias: "contentTypeAlias", + name: "Inline editing", + icon: "icon-picture", + prototype_paste_data: { + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "Label", + editor: "views/blockelements/imageblock/imageblock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Let's have a chat", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ], + temp_image: "/umbraco/assets/img/demo.png" + } + } } ]; From 772e46b93a4323e59120cf73181fdb629baac6fd Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 30 Jan 2020 17:36:40 +1100 Subject: [PATCH 011/508] init commit of strongly typed models for implementations of a block editor and the upcoming block list editor --- .../Models/Blocks/BlockEditorModel.cs | 16 +++++++++++++++ .../Models/Blocks/BlockListLayoutReference.cs | 20 +++++++++++++++++++ .../Models/Blocks/BlockListModel.cs | 17 ++++++++++++++++ .../Models/Blocks/IBlockElement.cs | 18 +++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 4 ++++ 5 files changed, 75 insertions(+) create mode 100644 src/Umbraco.Core/Models/Blocks/BlockEditorModel.cs create mode 100644 src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs create mode 100644 src/Umbraco.Core/Models/Blocks/BlockListModel.cs create mode 100644 src/Umbraco.Core/Models/Blocks/IBlockElement.cs diff --git a/src/Umbraco.Core/Models/Blocks/BlockEditorModel.cs b/src/Umbraco.Core/Models/Blocks/BlockEditorModel.cs new file mode 100644 index 0000000000..7d00623ccf --- /dev/null +++ b/src/Umbraco.Core/Models/Blocks/BlockEditorModel.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.Models.Blocks +{ + /// + /// The base class for any strongly typed model for a Block editor implementation + /// + public abstract class BlockEditorModel + { + /// + /// The data items of the Block List editor + /// + public IEnumerable Data { get; } + } +} diff --git a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs new file mode 100644 index 0000000000..444bb7249f --- /dev/null +++ b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs @@ -0,0 +1,20 @@ +using System; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.Models.Blocks +{ + /// + /// Represents a layout item for the Block List editor + /// + public class BlockListLayoutReference : IBlockElement + { + public BlockListLayoutReference(Udi udi, IPublishedElement settings) + { + Udi = udi ?? throw new ArgumentNullException(nameof(udi)); + Settings = settings ?? throw new ArgumentNullException(nameof(settings)); + } + + public Udi Udi { get; } + public IPublishedElement Settings { get; } + } +} diff --git a/src/Umbraco.Core/Models/Blocks/BlockListModel.cs b/src/Umbraco.Core/Models/Blocks/BlockListModel.cs new file mode 100644 index 0000000000..5331ca3891 --- /dev/null +++ b/src/Umbraco.Core/Models/Blocks/BlockListModel.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Models.Blocks +{ + /// + /// The strongly typed model for the Block List editor + /// + public class BlockListModel : BlockEditorModel + { + /// + /// The layout items of the Block List editor + /// + public IEnumerable Layout { get; } + + + } +} diff --git a/src/Umbraco.Core/Models/Blocks/IBlockElement.cs b/src/Umbraco.Core/Models/Blocks/IBlockElement.cs new file mode 100644 index 0000000000..eeb5a73e2c --- /dev/null +++ b/src/Umbraco.Core/Models/Blocks/IBlockElement.cs @@ -0,0 +1,18 @@ +namespace Umbraco.Core.Models.Blocks +{ + // TODO: IBlockElement doesn't make sense, this is a reference to an actual element with some settings + // and always has to do with the "Layout", should possibly be called IBlockReference or IBlockLayout or IBlockLayoutReference + + /// + /// Represents a data item for a Block editor implementation + /// + /// + /// + /// see: https://github.com/umbraco/rfcs/blob/907f3758cf59a7b6781296a60d57d537b3b60b8c/cms/0011-block-data-structure.md#strongly-typed + /// + public interface IBlockElement + { + Udi Udi { get; } + TSettings Settings { get; } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index a8e3fc2988..8bf0b9105f 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -129,6 +129,10 @@ --> + + + + From 292c76df0b6f48335171373e604355774822cb7b Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 31 Jan 2020 15:59:27 +1100 Subject: [PATCH 012/508] Getting models and value converter setup along with tests --- src/Umbraco.Core/Constants-PropertyEditors.cs | 5 + .../Models/Blocks/BlockEditorModel.cs | 17 ++- .../Models/Blocks/BlockListLayoutReference.cs | 9 +- .../Models/Blocks/BlockListModel.cs | 16 ++- .../PropertyEditors/BlockListEditor.cs | 12 ++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../BlockListPropertyValueConverterTests.cs | 91 +++++++++++++++ .../Published/NestedContentTests.cs | 6 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../BlockEditorPropertyEditor.cs | 17 +++ .../PropertyEditors/BlockListConfiguration.cs | 24 ++++ .../BlockListPropertyEditor.cs | 21 ++++ .../NestedContentConfiguration.cs | 1 + .../ValueConverters/BlockEditorConverter.cs | 45 ++++++++ .../BlockListPropertyValueConverter.cs | 107 ++++++++++++++++++ .../NestedContentManyValueConverter.cs | 6 +- .../NestedContentSingleValueConverter.cs | 6 +- .../NestedContentValueConverterBase.cs | 34 +----- src/Umbraco.Web/Umbraco.Web.csproj | 5 + 19 files changed, 382 insertions(+), 42 deletions(-) create mode 100644 src/Umbraco.Core/PropertyEditors/BlockListEditor.cs create mode 100644 src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs create mode 100644 src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs create mode 100644 src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs create mode 100644 src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs create mode 100644 src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs create mode 100644 src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs diff --git a/src/Umbraco.Core/Constants-PropertyEditors.cs b/src/Umbraco.Core/Constants-PropertyEditors.cs index eb2b3525a7..753cd72116 100644 --- a/src/Umbraco.Core/Constants-PropertyEditors.cs +++ b/src/Umbraco.Core/Constants-PropertyEditors.cs @@ -36,6 +36,11 @@ namespace Umbraco.Core /// public static class Aliases { + /// + /// CheckBox List. + /// + public const string BlockList = "Umbraco.BlockList"; + /// /// CheckBox List. /// diff --git a/src/Umbraco.Core/Models/Blocks/BlockEditorModel.cs b/src/Umbraco.Core/Models/Blocks/BlockEditorModel.cs index 7d00623ccf..307b69f6e0 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockEditorModel.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockEditorModel.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Core.Models.Blocks @@ -8,9 +10,20 @@ namespace Umbraco.Core.Models.Blocks /// public abstract class BlockEditorModel { + protected BlockEditorModel(IEnumerable data) + { + Data = data ?? throw new ArgumentNullException(nameof(data)); + } + + public BlockEditorModel() + { + } + + /// /// The data items of the Block List editor /// - public IEnumerable Data { get; } + [DataMember(Name = "data")] + public IEnumerable Data { get; set; } } } diff --git a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs index 444bb7249f..163b564c9f 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.Serialization; using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Core.Models.Blocks @@ -6,6 +7,7 @@ namespace Umbraco.Core.Models.Blocks /// /// Represents a layout item for the Block List editor /// + [DataContract(Name = "blockListLayout", Namespace = "")] public class BlockListLayoutReference : IBlockElement { public BlockListLayoutReference(Udi udi, IPublishedElement settings) @@ -14,7 +16,10 @@ namespace Umbraco.Core.Models.Blocks Settings = settings ?? throw new ArgumentNullException(nameof(settings)); } - public Udi Udi { get; } - public IPublishedElement Settings { get; } + [DataMember(Name = "udi")] + public Udi Udi { get; set; } + + [DataMember(Name = "settings")] + public IPublishedElement Settings { get; set; } } } diff --git a/src/Umbraco.Core/Models/Blocks/BlockListModel.cs b/src/Umbraco.Core/Models/Blocks/BlockListModel.cs index 5331ca3891..36dd8af7c1 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockListModel.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockListModel.cs @@ -1,16 +1,30 @@ using System.Collections.Generic; +using System.Runtime.Serialization; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Core.Models.Blocks { /// /// The strongly typed model for the Block List editor /// + [DataContract(Name = "blockList", Namespace = "")] public class BlockListModel : BlockEditorModel { + public BlockListModel(IEnumerable data, IEnumerable layout) + : base(data) + { + Layout = layout; + } + + public BlockListModel() + { + } + /// /// The layout items of the Block List editor /// - public IEnumerable Layout { get; } + [DataMember(Name = "layout")] + public IEnumerable Layout { get; set; } } diff --git a/src/Umbraco.Core/PropertyEditors/BlockListEditor.cs b/src/Umbraco.Core/PropertyEditors/BlockListEditor.cs new file mode 100644 index 0000000000..16c6176624 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/BlockListEditor.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core.PropertyEditors +{ + public class BlockListEditor + { + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 8bf0b9105f..33796a93bf 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -133,6 +133,7 @@ + diff --git a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs new file mode 100644 index 0000000000..f345bf136c --- /dev/null +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -0,0 +1,91 @@ +using Moq; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.PropertyEditors; +using Umbraco.Web.PropertyEditors.ValueConverters; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Tests.PropertyEditors +{ + [TestFixture] + public class BlockListPropertyValueConverterTests + { + private BlockListPropertyValueConverter Create() + { + var publishedSnapshotAccessor = Mock.Of(); + var publishedModelFactory = Mock.Of(); + var editor = new BlockListPropertyValueConverter( + Mock.Of(), + publishedModelFactory, + new BlockEditorConverter(publishedSnapshotAccessor, publishedModelFactory)); + return editor; + } + + [Test] + public void Is_Converter_For() + { + var editor = Create(); + Assert.IsTrue(editor.IsConverter(Mock.Of(x => x.EditorAlias == Constants.PropertyEditors.Aliases.BlockList))); + Assert.IsFalse(editor.IsConverter(Mock.Of(x => x.EditorAlias == Constants.PropertyEditors.Aliases.NestedContent))); + } + + [Test] + public void Get_Value_Type_Multiple() + { + var editor = Create(); + var config = new BlockListConfiguration + { + ElementTypes = new[] { + new BlockListConfiguration.ElementType + { + Alias = "Test1" + }, + new BlockListConfiguration.ElementType + { + Alias = "Test2" + } + } + }; + + var dataType = new PublishedDataType(1, "test", new Lazy(() => config)); + var propType = Mock.Of(x => x.DataType == dataType); + + var valueType = editor.GetPropertyValueType(propType); + + Assert.AreEqual(typeof(IEnumerable), valueType); + } + + [Test] + public void Get_Value_Type_Single() + { + var editor = Create(); + var config = new BlockListConfiguration + { + ElementTypes = new[] { + new BlockListConfiguration.ElementType + { + Alias = "Test1" + } + } + }; + + var dataType = new PublishedDataType(1, "test", new Lazy(() => config)); + var propType = Mock.Of(x => x.DataType == dataType); + + var valueType = editor.GetPropertyValueType(propType); + + var modelType = typeof(IEnumerable<>).MakeGenericType(ModelType.For(config.ElementTypes[0].Alias)); + + // we can't compare the exact match of types because ModelType.For generates a new/different type even if the same alias is used + Assert.AreEqual(modelType.FullName, valueType.FullName); + } + + + } +} diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index adfb9d3b6b..a102b9f93e 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -119,10 +119,12 @@ namespace Umbraco.Tests.Published .Setup(x => x.PublishedSnapshot) .Returns(publishedSnapshot.Object); + var blockEditorConverter = new BlockEditorConverter(publishedSnapshotAccessor.Object, publishedModelFactory.Object); + var converters = new PropertyValueConverterCollection(new IPropertyValueConverter[] { - new NestedContentSingleValueConverter(publishedSnapshotAccessor.Object, publishedModelFactory.Object, proflog), - new NestedContentManyValueConverter(publishedSnapshotAccessor.Object, publishedModelFactory.Object, proflog), + new NestedContentSingleValueConverter(blockEditorConverter, publishedModelFactory.Object, proflog), + new NestedContentManyValueConverter(blockEditorConverter, publishedModelFactory.Object, proflog), }); var factory = new PublishedContentTypeFactory(publishedModelFactory.Object, converters, dataTypeService); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index ff923bb04b..2d980461d2 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -145,6 +145,7 @@ + diff --git a/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs new file mode 100644 index 0000000000..b9ee1b84fb --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs @@ -0,0 +1,17 @@ +using Umbraco.Core.Logging; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// Abstract class for block editor based editors + /// + public abstract class BlockEditorPropertyEditor : DataEditor + { + public const string ContentTypeAliasPropertyKey = "contentTypeAlias"; + + public BlockEditorPropertyEditor(ILogger logger) : base(logger) + { + } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs new file mode 100644 index 0000000000..95366db486 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + + /// + /// The configuration object for the Block List editor + /// + public class BlockListConfiguration + { + [ConfigurationField("elementTypes", "Element Types", "views/propertyeditors/blocklist/blocklist.elementtypepicker.html", Description = "Select the Element Types to use as models for the items.")] + public ElementType[] ElementTypes { get; set; } + + // TODO: Fill me in + + public class ElementType + { + [JsonProperty("elementTypeAlias")] + public string Alias { get; set; } + } + + } +} diff --git a/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs new file mode 100644 index 0000000000..f0148f7a9a --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs @@ -0,0 +1,21 @@ +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; + +namespace Umbraco.Web.PropertyEditors +{ + + [DataEditor( + Constants.PropertyEditors.Aliases.BlockList, + "Block List", + "blocklist", + Icon = "icon-list", + Group = Constants.PropertyEditors.Groups.Lists)] + public class BlockListPropertyEditor : BlockEditorPropertyEditor + { + public BlockListPropertyEditor(ILogger logger) : base(logger) + { + } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentConfiguration.cs b/src/Umbraco.Web/PropertyEditors/NestedContentConfiguration.cs index 0f53207462..89190883c8 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/NestedContentConfiguration.cs @@ -3,6 +3,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { + /// /// Represents the configuration for the nested content value editor. /// diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs new file mode 100644 index 0000000000..0ab9b86572 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs @@ -0,0 +1,45 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Web.PropertyEditors.ValueConverters +{ + public sealed class BlockEditorConverter + { + private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; + private readonly IPublishedModelFactory _publishedModelFactory; + + public BlockEditorConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory) + { + _publishedSnapshotAccessor = publishedSnapshotAccessor; + _publishedModelFactory = publishedModelFactory; + } + + public IPublishedElement ConvertToElement( + JObject sourceObject, string contentTypeAliasPropertyKey, + PropertyCacheLevel referenceCacheLevel, bool preview) + { + var elementTypeAlias = sourceObject[contentTypeAliasPropertyKey]?.ToObject(); + if (string.IsNullOrEmpty(elementTypeAlias)) + return null; + + // only convert element types - content types will cause an exception when PublishedModelFactory creates the model + var publishedContentType = _publishedSnapshotAccessor.PublishedSnapshot.Content.GetContentType(elementTypeAlias); + if (publishedContentType == null || publishedContentType.IsElement == false) + return null; + + var propertyValues = sourceObject.ToObject>(); + + if (!propertyValues.TryGetValue("key", out var keyo) + || !Guid.TryParse(keyo.ToString(), out var key)) + key = Guid.Empty; + + IPublishedElement element = new PublishedElement(publishedContentType, key, propertyValues, preview, referenceCacheLevel, _publishedSnapshotAccessor); + element = _publishedModelFactory.CreateModel(element); + return element; + } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs new file mode 100644 index 0000000000..dceaa9995e --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -0,0 +1,107 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.Blocks; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors.ValueConverters; + +namespace Umbraco.Web.PropertyEditors.ValueConverters +{ + + [DefaultPropertyValueConverter(typeof(JsonValueConverter))] + public class BlockListPropertyValueConverter : PropertyValueConverterBase + { + private readonly IProfilingLogger _proflog; + private readonly IPublishedModelFactory _publishedModelFactory; + private readonly BlockEditorConverter _blockConverter; + + public BlockListPropertyValueConverter(IProfilingLogger proflog, IPublishedModelFactory publishedModelFactory, BlockEditorConverter blockConverter) + { + _proflog = proflog; + _publishedModelFactory = publishedModelFactory; + _blockConverter = blockConverter; + } + + /// + public override bool IsConverter(IPublishedPropertyType propertyType) + => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.BlockList); + + /// + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) + { + var contentTypes = propertyType.DataType.ConfigurationAs().ElementTypes; + return contentTypes.Length == 1 + ? typeof(IEnumerable<>).MakeGenericType(ModelType.For(contentTypes[0].Alias)) + : typeof(IEnumerable); + } + + /// + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) + => PropertyCacheLevel.Element; + + /// + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) + { + return source?.ToString(); + } + + /// + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + { + using (_proflog.DebugDuration($"ConvertPropertyToBlockList ({propertyType.DataType.Id})")) + { + var configuration = propertyType.DataType.ConfigurationAs(); + var contentTypes = configuration.ElementTypes; + var elements = contentTypes.Length == 1 + ? (IList)_publishedModelFactory.CreateModelList(contentTypes[0].Alias) + : new List(); + + var layout = new List(); + var model = new BlockListModel(elements, layout); + + var value = (string)inter; + if (string.IsNullOrWhiteSpace(value)) return model; + + var objects = JsonConvert.DeserializeObject(value); + if (objects.Count == 0) return model; + + var jsonLayout = objects["layout"] as JObject; + if (jsonLayout == null) return model; + + var jsonData = objects["data"] as JArray; + if (jsonData == null) return model; + + var blockListLayouts = jsonLayout[Constants.PropertyEditors.Aliases.BlockList] as JArray; + if (blockListLayouts == null) return model; + + foreach(var blockListLayout in blockListLayouts) + { + var settingsJson = blockListLayout["settings"] as JObject; + if (settingsJson == null) continue; + + var element = _blockConverter.ConvertToElement(settingsJson, BlockEditorPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview); + if (element == null) continue; + + var layoutRef = new BlockListLayoutReference(blockListLayout.Value("udi"), element); + layout.Add(layoutRef); + } + + foreach (var data in jsonData.Cast()) + { + var element = _blockConverter.ConvertToElement(data, BlockEditorPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview); + if (element == null) continue; + elements.Add(element); + } + + return model; + } + } + + + } +} diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs index 4a25049695..b961048851 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs @@ -23,8 +23,8 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters /// /// Initializes a new instance of the class. /// - public NestedContentManyValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory, IProfilingLogger proflog) - : base(publishedSnapshotAccessor, publishedModelFactory) + public NestedContentManyValueConverter(BlockEditorConverter blockEditorConverter, IPublishedModelFactory publishedModelFactory, IProfilingLogger proflog) + : base(blockEditorConverter, publishedModelFactory) { _proflog = proflog; } @@ -71,7 +71,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters foreach (var sourceObject in objects) { - var element = ConvertToElement(sourceObject, referenceCacheLevel, preview); + var element = BlockEditorConverter.ConvertToElement(sourceObject, NestedContentPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview); if (element != null) elements.Add(element); } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs index c9c99615f6..b3a2a9294d 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs @@ -22,8 +22,8 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters /// /// Initializes a new instance of the class. /// - public NestedContentSingleValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory, IProfilingLogger proflog) - : base(publishedSnapshotAccessor, publishedModelFactory) + public NestedContentSingleValueConverter(BlockEditorConverter blockEditorConverter, IPublishedModelFactory publishedModelFactory, IProfilingLogger proflog) + : base(blockEditorConverter, publishedModelFactory) { _proflog = proflog; } @@ -65,7 +65,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters if (objects.Count > 1) throw new InvalidOperationException(); - return ConvertToElement(objects[0], referenceCacheLevel, preview); + return BlockEditorConverter.ConvertToElement(objects[0], NestedContentPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview); } } } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs index 7c18d8ebca..4295daf5fe 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs @@ -1,23 +1,19 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json.Linq; -using Umbraco.Core; +using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; -using Umbraco.Web.PublishedCache; namespace Umbraco.Web.PropertyEditors.ValueConverters { public abstract class NestedContentValueConverterBase : PropertyValueConverterBase { - private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; - protected NestedContentValueConverterBase(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory) + protected NestedContentValueConverterBase(BlockEditorConverter blockEditorConverter, IPublishedModelFactory publishedModelFactory) { - _publishedSnapshotAccessor = publishedSnapshotAccessor; + BlockEditorConverter = blockEditorConverter; PublishedModelFactory = publishedModelFactory; } + protected BlockEditorConverter BlockEditorConverter { get; } protected IPublishedModelFactory PublishedModelFactory { get; } public static bool IsNested(IPublishedPropertyType publishedProperty) @@ -39,26 +35,6 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return IsNested(publishedProperty) && !IsNestedSingle(publishedProperty); } - protected IPublishedElement ConvertToElement(JObject sourceObject, PropertyCacheLevel referenceCacheLevel, bool preview) - { - var elementTypeAlias = sourceObject[NestedContentPropertyEditor.ContentTypeAliasPropertyKey]?.ToObject(); - if (string.IsNullOrEmpty(elementTypeAlias)) - return null; - - // only convert element types - content types will cause an exception when PublishedModelFactory creates the model - var publishedContentType = _publishedSnapshotAccessor.PublishedSnapshot.Content.GetContentType(elementTypeAlias); - if (publishedContentType == null || publishedContentType.IsElement == false) - return null; - - var propertyValues = sourceObject.ToObject>(); - - if (!propertyValues.TryGetValue("key", out var keyo) - || !Guid.TryParse(keyo.ToString(), out var key)) - key = Guid.Empty; - - IPublishedElement element = new PublishedElement(publishedContentType, key, propertyValues, preview, referenceCacheLevel, _publishedSnapshotAccessor); - element = PublishedModelFactory.CreateModel(element); - return element; - } + } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 0111a36993..e660108cad 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -233,8 +233,13 @@ + + + + + From a23f93c557ee5826b9227da58c770b9d3bc775d0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 3 Feb 2020 16:45:54 +1100 Subject: [PATCH 013/508] Gets the block list property value converter tests running and written --- src/Umbraco.Core/Contants-UdiEntityType.cs | 3 + .../Models/Blocks/BlockListLayoutReference.cs | 2 +- .../PropertyValueConverterBase.cs | 5 + .../BlockListPropertyValueConverterTests.cs | 216 +++++++++++++++--- .../BlockListPropertyValueConverter.cs | 35 ++- 5 files changed, 220 insertions(+), 41 deletions(-) diff --git a/src/Umbraco.Core/Contants-UdiEntityType.cs b/src/Umbraco.Core/Contants-UdiEntityType.cs index 75a137bd2e..1ed862f328 100644 --- a/src/Umbraco.Core/Contants-UdiEntityType.cs +++ b/src/Umbraco.Core/Contants-UdiEntityType.cs @@ -25,6 +25,7 @@ namespace Umbraco.Core { Unknown, UdiType.Unknown }, { AnyGuid, UdiType.GuidUdi }, + { Element, UdiType.GuidUdi }, { Document, UdiType.GuidUdi }, { DocumentBlueprint, UdiType.GuidUdi }, { Media, UdiType.GuidUdi }, @@ -64,6 +65,8 @@ namespace Umbraco.Core public const string AnyGuid = "any-guid"; // that one is for tests + public const string Element = "element"; + public const string Document = "document"; public const string DocumentBlueprint = "document-blueprint"; diff --git a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs index 163b564c9f..a2fd6c9df9 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Models.Blocks public BlockListLayoutReference(Udi udi, IPublishedElement settings) { Udi = udi ?? throw new ArgumentNullException(nameof(udi)); - Settings = settings ?? throw new ArgumentNullException(nameof(settings)); + Settings = settings; // can be null } [DataMember(Name = "udi")] diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs index 3b6ebc610c..a584ea2a9c 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs @@ -30,18 +30,23 @@ namespace Umbraco.Core.PropertyEditors return value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false); } + /// public virtual Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (object); + /// public virtual PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; + /// public virtual object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) => source; + /// public virtual object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) => inter; + /// public virtual object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) => inter?.ToString() ?? string.Empty; } diff --git a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs index f345bf136c..114642f89d 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -6,7 +6,9 @@ using System.Linq; using System.Text; using Umbraco.Core; using Umbraco.Core.Logging; +using Umbraco.Core.Models.Blocks; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; using Umbraco.Web.PropertyEditors; using Umbraco.Web.PropertyEditors.ValueConverters; using Umbraco.Web.PublishedCache; @@ -16,10 +18,26 @@ namespace Umbraco.Tests.PropertyEditors [TestFixture] public class BlockListPropertyValueConverterTests { - private BlockListPropertyValueConverter Create() + /// + /// Setup mocks for IPublishedSnapshotAccessor + /// + /// + private IPublishedSnapshotAccessor GetPublishedSnapshotAccessor() { - var publishedSnapshotAccessor = Mock.Of(); - var publishedModelFactory = Mock.Of(); + var homeContentType = Mock.Of(x => + x.IsElement == true + && x.Alias == "home"); + var contentCache = new Mock(); + contentCache.Setup(x => x.GetContentType("home")).Returns(homeContentType); + var publishedSnapshot = Mock.Of(x => x.Content == contentCache.Object); + var publishedSnapshotAccessor = Mock.Of(x => x.PublishedSnapshot == publishedSnapshot); + return publishedSnapshotAccessor; + } + + private BlockListPropertyValueConverter CreateConverter() + { + var publishedSnapshotAccessor = GetPublishedSnapshotAccessor(); + var publishedModelFactory = new NoopPublishedModelFactory(); var editor = new BlockListPropertyValueConverter( Mock.Of(), publishedModelFactory, @@ -27,21 +45,9 @@ namespace Umbraco.Tests.PropertyEditors return editor; } - [Test] - public void Is_Converter_For() + private BlockListConfiguration ConfigForMany() => new BlockListConfiguration { - var editor = Create(); - Assert.IsTrue(editor.IsConverter(Mock.Of(x => x.EditorAlias == Constants.PropertyEditors.Aliases.BlockList))); - Assert.IsFalse(editor.IsConverter(Mock.Of(x => x.EditorAlias == Constants.PropertyEditors.Aliases.NestedContent))); - } - - [Test] - public void Get_Value_Type_Multiple() - { - var editor = Create(); - var config = new BlockListConfiguration - { - ElementTypes = new[] { + ElementTypes = new[] { new BlockListConfiguration.ElementType { Alias = "Test1" @@ -51,7 +57,40 @@ namespace Umbraco.Tests.PropertyEditors Alias = "Test2" } } - }; + }; + + private BlockListConfiguration ConfigForSingle() => new BlockListConfiguration + { + ElementTypes = new[] { + new BlockListConfiguration.ElementType + { + Alias = "Test1" + } + } + }; + + private IPublishedPropertyType GetPropertyType(BlockListConfiguration config) + { + var dataType = new PublishedDataType(1, "test", new Lazy(() => config)); + var propertyType = Mock.Of(x => + x.EditorAlias == Constants.PropertyEditors.Aliases.BlockList + && x.DataType == dataType); + return propertyType; + } + + [Test] + public void Is_Converter_For() + { + var editor = CreateConverter(); + Assert.IsTrue(editor.IsConverter(Mock.Of(x => x.EditorAlias == Constants.PropertyEditors.Aliases.BlockList))); + Assert.IsFalse(editor.IsConverter(Mock.Of(x => x.EditorAlias == Constants.PropertyEditors.Aliases.NestedContent))); + } + + [Test] + public void Get_Value_Type_Multiple() + { + var editor = CreateConverter(); + var config = ConfigForMany(); var dataType = new PublishedDataType(1, "test", new Lazy(() => config)); var propType = Mock.Of(x => x.DataType == dataType); @@ -64,16 +103,8 @@ namespace Umbraco.Tests.PropertyEditors [Test] public void Get_Value_Type_Single() { - var editor = Create(); - var config = new BlockListConfiguration - { - ElementTypes = new[] { - new BlockListConfiguration.ElementType - { - Alias = "Test1" - } - } - }; + var editor = CreateConverter(); + var config = ConfigForSingle(); var dataType = new PublishedDataType(1, "test", new Lazy(() => config)); var propType = Mock.Of(x => x.DataType == dataType); @@ -86,6 +117,135 @@ namespace Umbraco.Tests.PropertyEditors Assert.AreEqual(modelType.FullName, valueType.FullName); } + [Test] + public void Convert_Null_Empty() + { + var editor = CreateConverter(); + var config = ConfigForMany(); + var propertyType = GetPropertyType(config); + var publishedElement = Mock.Of(); + + string json = null; + var converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel; + + Assert.IsNotNull(converted); + Assert.AreEqual(0, converted.Data.Count()); + Assert.AreEqual(0, converted.Layout.Count()); + + json = string.Empty; + converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel; + + Assert.IsNotNull(converted); + Assert.AreEqual(0, converted.Data.Count()); + Assert.AreEqual(0, converted.Layout.Count()); + } + + [Test] + public void Convert_Valid_Empty_Json() + { + var editor = CreateConverter(); + var config = ConfigForMany(); + var propertyType = GetPropertyType(config); + var publishedElement = Mock.Of(); + + var json = "{}"; + var converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel; + + Assert.IsNotNull(converted); + Assert.AreEqual(0, converted.Data.Count()); + Assert.AreEqual(0, converted.Layout.Count()); + + json = @"{ +layout: [], +data: []}"; + converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel; + + Assert.IsNotNull(converted); + Assert.AreEqual(0, converted.Data.Count()); + Assert.AreEqual(0, converted.Layout.Count()); + + // Even though there is a layout, there is no data, so the conversion will result in zero elements in total + json = @" +{ + layout: { + '" + Constants.PropertyEditors.Aliases.BlockList + @"': [ + { + 'udi': 'umb://element/e7dba547615b4e9ab4ab2a7674845bc9', + 'settings': {} + } + ] + }, + data: [] +}"; + + converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel; + + Assert.IsNotNull(converted); + Assert.AreEqual(0, converted.Data.Count()); + Assert.AreEqual(0, converted.Layout.Count()); + + // Even though there is a layout and data, the data is invalid (missing required keys) so the conversion will result in zero elements in total + json = @" +{ + layout: { + '" + Constants.PropertyEditors.Aliases.BlockList + @"': [ + { + 'udi': 'umb://element/e7dba547615b4e9ab4ab2a7674845bc9', + 'settings': {} + } + ] + }, + data: [ + { + 'udi': 'umb://element/e7dba547615b4e9ab4ab2a7674845bc9' + } + ] +}"; + + converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel; + + Assert.IsNotNull(converted); + Assert.AreEqual(0, converted.Data.Count()); + Assert.AreEqual(0, converted.Layout.Count()); + } + + [Test] + public void Convert_Valid_Json() + { + var editor = CreateConverter(); + var config = ConfigForMany(); + var propertyType = GetPropertyType(config); + var publishedElement = Mock.Of(); + + var json = @" +{ + layout: { + '" + Constants.PropertyEditors.Aliases.BlockList + @"': [ + { + 'udi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D', + 'settings': {} + } + ] + }, + data: [ + { + 'contentTypeAlias': 'home', + 'key': '1304E1DD-AC87-4396-84FE-8A399231CB3D' + } + ] +}"; + var converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel; + + Assert.IsNotNull(converted); + Assert.AreEqual(1, converted.Data.Count()); + var item0 = converted.Data.ElementAt(0); + Assert.AreEqual(Guid.Parse("1304E1DD-AC87-4396-84FE-8A399231CB3D"), item0.Key); + Assert.AreEqual("home", item0.ContentType.Alias); + Assert.AreEqual(1, converted.Layout.Count()); + var layout0 = converted.Layout.ElementAt(0); + Assert.IsNull(layout0.Settings); + Assert.AreEqual(Udi.Parse("umb://element/1304E1DDAC87439684FE8A399231CB3D"), layout0.Udi); + } } } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs index dceaa9995e..ab289f048c 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -53,6 +53,8 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters /// public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { + // NOTE: The intermediate object is just a json string, we don't actually convert from source -> intermediate since source is always just a json string + using (_proflog.DebugDuration($"ConvertPropertyToBlockList ({propertyType.DataType.Id})")) { var configuration = propertyType.DataType.ConfigurationAs(); @@ -79,18 +81,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters var blockListLayouts = jsonLayout[Constants.PropertyEditors.Aliases.BlockList] as JArray; if (blockListLayouts == null) return model; - foreach(var blockListLayout in blockListLayouts) - { - var settingsJson = blockListLayout["settings"] as JObject; - if (settingsJson == null) continue; - - var element = _blockConverter.ConvertToElement(settingsJson, BlockEditorPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview); - if (element == null) continue; - - var layoutRef = new BlockListLayoutReference(blockListLayout.Value("udi"), element); - layout.Add(layoutRef); - } - + // parse the data elements foreach (var data in jsonData.Cast()) { var element = _blockConverter.ConvertToElement(data, BlockEditorPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview); @@ -98,6 +89,26 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters elements.Add(element); } + // if there's no elements just return since if there's no data it doesn't matter what is stored in layout + if (elements.Count == 0) return model; + + foreach (var blockListLayout in blockListLayouts) + { + var settingsJson = blockListLayout["settings"] as JObject; + if (settingsJson == null) continue; + + // the result of this can be null, that's ok + var element = _blockConverter.ConvertToElement(settingsJson, BlockEditorPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview); + + if (!Udi.TryParse(blockListLayout.Value("udi"), out var udi)) + continue; + + var layoutRef = new BlockListLayoutReference(udi, element); + layout.Add(layoutRef); + } + + + return model; } } From 9f0becaf522eeb09ceeee271554aff5ce1a948e1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 3 Feb 2020 18:31:18 +1100 Subject: [PATCH 014/508] Adds another test to show how to access the data block from a layout element --- .../Models/Blocks/BlockListLayoutReference.cs | 18 ++++- .../BlockListPropertyValueConverterTests.cs | 79 +++++++++++++++++++ .../BlockListPropertyValueConverter.cs | 19 +++-- src/Umbraco.Web/Runtime/WebInitialComposer.cs | 1 + 4 files changed, 108 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs index a2fd6c9df9..09c6c76478 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs @@ -10,16 +10,32 @@ namespace Umbraco.Core.Models.Blocks [DataContract(Name = "blockListLayout", Namespace = "")] public class BlockListLayoutReference : IBlockElement { - public BlockListLayoutReference(Udi udi, IPublishedElement settings) + public BlockListLayoutReference(Udi udi, IPublishedElement data, IPublishedElement settings) { Udi = udi ?? throw new ArgumentNullException(nameof(udi)); + Data = data ?? throw new ArgumentNullException(nameof(data)); Settings = settings; // can be null } + /// + /// The Id of the data item + /// [DataMember(Name = "udi")] public Udi Udi { get; set; } + /// + /// The settings for the layout item + /// [DataMember(Name = "settings")] public IPublishedElement Settings { get; set; } + + /// + /// The data item referenced + /// + /// + /// This is ignored from serialization since it is just a reference to the actual data element + /// + [IgnoreDataMember] + public IPublishedElement Data { get; set; } } } diff --git a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs index 114642f89d..ca687f94a6 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -207,6 +207,31 @@ data: []}"; Assert.IsNotNull(converted); Assert.AreEqual(0, converted.Data.Count()); Assert.AreEqual(0, converted.Layout.Count()); + + // Everthing is ok except the udi reference in the layout doesn't match the data so it will be empty + json = @" +{ + layout: { + '" + Constants.PropertyEditors.Aliases.BlockList + @"': [ + { + 'udi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D', + 'settings': {} + } + ] + }, + data: [ + { + 'contentTypeAlias': 'home', + 'key': '1304E1DD-0000-4396-84FE-8A399231CB3D' + } + ] +}"; + + converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel; + + Assert.IsNotNull(converted); + Assert.AreEqual(1, converted.Data.Count()); + Assert.AreEqual(0, converted.Layout.Count()); } [Test] @@ -247,5 +272,59 @@ data: []}"; Assert.AreEqual(Udi.Parse("umb://element/1304E1DDAC87439684FE8A399231CB3D"), layout0.Udi); } + [Test] + public void Get_Data_From_Layout_Item() + { + var editor = CreateConverter(); + var config = ConfigForMany(); + var propertyType = GetPropertyType(config); + var publishedElement = Mock.Of(); + + var json = @" +{ + layout: { + '" + Constants.PropertyEditors.Aliases.BlockList + @"': [ + { + 'udi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D', + 'settings': {} + }, + { + 'udi': 'umb://element/0A4A416E547D464FABCC6F345C17809A', + 'settings': {} + } + ] + }, + data: [ + { + 'contentTypeAlias': 'home', + 'key': '1304E1DD-AC87-4396-84FE-8A399231CB3D' + }, + { + 'contentTypeAlias': 'home', + 'key': 'E05A0347-0442-4AB3-A520-E048E6197E79' + }, + { + 'contentTypeAlias': 'home', + 'key': '0A4A416E-547D-464F-ABCC-6F345C17809A' + } + ] +}"; + + var converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel; + + Assert.IsNotNull(converted); + Assert.AreEqual(3, converted.Data.Count()); + Assert.AreEqual(2, converted.Layout.Count()); + + var item0 = converted.Layout.ElementAt(0); + Assert.AreEqual(Guid.Parse("1304E1DD-AC87-4396-84FE-8A399231CB3D"), item0.Data.Key); + Assert.AreEqual("home", item0.Data.ContentType.Alias); + + var item1 = converted.Layout.ElementAt(1); + Assert.AreEqual(Guid.Parse("0A4A416E-547D-464F-ABCC-6F345C17809A"), item1.Data.Key); + Assert.AreEqual("home", item1.Data.ContentType.Alias); + + } + } } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs index ab289f048c..3878b8d605 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -59,12 +59,13 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { var configuration = propertyType.DataType.ConfigurationAs(); var contentTypes = configuration.ElementTypes; - var elements = contentTypes.Length == 1 + var elements = (contentTypes.Length == 1 ? (IList)_publishedModelFactory.CreateModelList(contentTypes[0].Alias) - : new List(); + : new List()) + .ToDictionary(x => x.Key, x => x); var layout = new List(); - var model = new BlockListModel(elements, layout); + var model = new BlockListModel(elements.Values, layout); var value = (string)inter; if (string.IsNullOrWhiteSpace(value)) return model; @@ -86,7 +87,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { var element = _blockConverter.ConvertToElement(data, BlockEditorPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview); if (element == null) continue; - elements.Add(element); + elements[element.Key] = element; } // if there's no elements just return since if there's no data it doesn't matter what is stored in layout @@ -100,15 +101,17 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters // the result of this can be null, that's ok var element = _blockConverter.ConvertToElement(settingsJson, BlockEditorPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview); - if (!Udi.TryParse(blockListLayout.Value("udi"), out var udi)) + if (!Udi.TryParse(blockListLayout.Value("udi"), out var udi) || !(udi is GuidUdi guidUdi)) continue; - var layoutRef = new BlockListLayoutReference(udi, element); + // get the data reference + if (!elements.TryGetValue(guidUdi.Guid, out var data)) + continue; + + var layoutRef = new BlockListLayoutReference(udi, data, element); layout.Add(layoutRef); } - - return model; } } diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 1c4121da0c..79b24175ea 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -108,6 +108,7 @@ namespace Umbraco.Web.Runtime composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(); + composition.RegisterUnique(); // register the umbraco helper - this is Transient! very important! // also, if not level.Run, we cannot really use the helper (during upgrade...) From 199ea404ed072ddbec500f56f975f64d764824c6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 3 Feb 2020 18:37:07 +1100 Subject: [PATCH 015/508] Makes model props readonly/immutable --- src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs | 6 +++--- src/Umbraco.Core/Models/Blocks/BlockListModel.cs | 6 +----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs index 09c6c76478..19b30e6ea6 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs @@ -21,13 +21,13 @@ namespace Umbraco.Core.Models.Blocks /// The Id of the data item /// [DataMember(Name = "udi")] - public Udi Udi { get; set; } + public Udi Udi { get; } /// /// The settings for the layout item /// [DataMember(Name = "settings")] - public IPublishedElement Settings { get; set; } + public IPublishedElement Settings { get; } /// /// The data item referenced @@ -36,6 +36,6 @@ namespace Umbraco.Core.Models.Blocks /// This is ignored from serialization since it is just a reference to the actual data element /// [IgnoreDataMember] - public IPublishedElement Data { get; set; } + public IPublishedElement Data { get; } } } diff --git a/src/Umbraco.Core/Models/Blocks/BlockListModel.cs b/src/Umbraco.Core/Models/Blocks/BlockListModel.cs index 36dd8af7c1..089ca7e6a3 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockListModel.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockListModel.cs @@ -16,15 +16,11 @@ namespace Umbraco.Core.Models.Blocks Layout = layout; } - public BlockListModel() - { - } - /// /// The layout items of the Block List editor /// [DataMember(Name = "layout")] - public IEnumerable Layout { get; set; } + public IEnumerable Layout { get; } } From 89c489cfe55fada40d1d4f98f816eeecbfa8373b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 3 Feb 2020 14:53:45 +0100 Subject: [PATCH 016/508] inline-editor + interpolation for label + element editor as a component --- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../inlineblock.editor.controller.js | 20 +++++ .../inlineblock/inlineblock.editor.html | 10 +++ .../inlineblock/inlineblock.editor.less | 67 ++++++++++++++++ .../labelblock/labelblock.editor.html | 2 +- .../labelblock/labelblock.editor.less | 1 + .../elementeditor.component.html | 38 ++++++++++ .../elementeditor/elementeditor.component.js | 23 ++++++ .../elementeditor/elementeditor.controller.js | 2 + .../elementeditor/elementeditor.html | 49 +++--------- .../blocklist/blocklist.component.js | 76 +++++++++++++++++-- .../blocklist/blocklist.component.less | 16 ++-- 12 files changed, 252 insertions(+), 53 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.component.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.component.js diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 695b456fd6..f264038e93 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -226,6 +226,7 @@ // Block Elements @import "../views/blockelements/labelblock/labelblock.editor.less"; +@import "../views/blockelements/inlineblock/inlineblock.editor.less"; @import "../views/blockelements/textareablock/textareablock.editor.less"; @import "../views/blockelements/imageblock/imageblock.editor.less"; diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.controller.js new file mode 100644 index 0000000000..c7cac47d85 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.controller.js @@ -0,0 +1,20 @@ +(function () { + 'use strict'; + + function InlineBlockEditor($scope) { + + const bc = this; + + bc.isOpen = false; + bc.caretIconType = "icon-navigation-right"; + + bc.openBlock = function() { + bc.isOpen = !bc.isOpen; + bc.caretIconType = bc.isOpen ? "icon-navigation-down" : "icon-navigation-right"; + } + + } + + angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditor.InlineBlockEditor", InlineBlockEditor); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html new file mode 100644 index 0000000000..78e23b17f3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html @@ -0,0 +1,10 @@ +
+ +
+ +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less new file mode 100644 index 0000000000..dc014c5daf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less @@ -0,0 +1,67 @@ +.blockelement-inlineblock-editor { + + margin-bottom: 2px; + margin-top: 2px; + border: 1px solid @gray-9; + border-radius: @baseBorderRadius; + transition: border-color 120ms; + + &:not(.--open):hover { + border-color: @gray-8; + } + + > button { + width: 100%; + min-height: 48px; + + cursor: pointer; + color: @ui-action-discreet-type; + + text-align: left; + padding-left: 10px; + padding-bottom: 2px; + + user-select: none; + + .caret { + transform: rotate(-90deg); + transition: transform 80ms ease-out; + } + i { + font-size: 22px; + display: inline-block; + vertical-align: middle; + } + span { + display: inline-block; + vertical-align: middle; + } + + &:hover { + color: @ui-action-discreet-type-hover; + border-color: @gray-8; + } + } + + &.--open { + border-color: @gray-8; + box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.05); + > button { + > .caret { + transform: rotate(0deg); + } + } + } +} +.blockelement-inlineblock-editor__inner { + border-top: 1px solid @gray-8; + + .umb-group-panel { + background-color: transparent; + box-shadow: none; + margin-bottom: 0; + } + .umb-group-panel__header { + display:none; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html index f0678c76e9..bd8f9558fd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html @@ -1,4 +1,4 @@ diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less index 610773528b..5ce53aece4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less @@ -5,6 +5,7 @@ border: 1px solid @gray-9; border-radius: @baseBorderRadius; + cursor: pointer; color: @ui-action-discreet-type; text-align: left; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.component.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.component.html new file mode 100644 index 0000000000..09724f6619 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.component.html @@ -0,0 +1,38 @@ +
+ +
+ +
+
{{ group.label }}
+
+ +
+ + +
+ + +
+ +
+
+ +
+ + + + + +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.component.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.component.js new file mode 100644 index 0000000000..6ba59f0703 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.component.js @@ -0,0 +1,23 @@ +(function () { + 'use strict'; + + angular + .module('umbraco.directives') + .component('umbElementEditor', { + templateUrl: 'views/common/infiniteeditors/elementeditor/elementeditor.component.html', + controller: ElementEditorComponentController, + controllerAs: 'vm', + bindings: { + content: '=' + } + }); + + function ElementEditorComponentController() { + + const vm = this; + + // TODO: we might not need this.. + + } + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js index d421418297..ebabe86617 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js @@ -7,6 +7,8 @@ angular.module("umbraco") vm.content = $scope.model.block.content; + vm.title = $scope.model.block.label; + vm.saveAndClose = function() { if ($scope.model && $scope.model.submit) { $scope.model.submit($scope.model); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html index 71aab124e6..2df5dcd923 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html @@ -4,55 +4,24 @@ + +
-
-
- -
- -
-
{{ group.label }}
-
- -
- - -
- - -
- -
-
- -
- - - - - -
-
+
-
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 5fc17fcafd..f3f8c2e5fe 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -43,9 +43,10 @@ icon: "icon-document", label: "Text" }, - label: "{{pageTitle | truncate:true:36}}", + labelTemplate: "{{pageTitle | truncate:true:36}}", labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), editor: "views/blockelements/labelblock/labelblock.editor.html", + overlaySize: 'medium', content: { variants: [ { @@ -94,6 +95,7 @@ }, label: "Label", editor: "views/blockelements/textareablock/textareablock.editor.html", + overlaySize: 'medium', content: { variants: [ { @@ -142,6 +144,7 @@ }, label: "Label", editor: "views/blockelements/imageblock/imageblock.editor.html", + overlaySize: 'medium', content: { variants: [ { @@ -191,6 +194,7 @@ }, label: "Label", editor: "views/blockelements/imageblock/imageblock.editor.html", + overlaySize: 'medium', content: { variants: [ { @@ -234,6 +238,52 @@ // TODO: get icon, properties etc. from available types? vm.blocks = [ + { + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "{{pageTitle | truncate:true:36}}", + labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), + key: 1, + editor: "views/blockelements/inlineblock/inlineblock.editor.html", + overlaySize: 'medium', + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn't distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ] + } + }, { elementType: { alias: 'contentTypeAlias', @@ -244,6 +294,7 @@ labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), key: 1, editor: "views/blockelements/labelblock/labelblock.editor.html", + overlaySize: 'medium', content: { variants: [ { @@ -289,6 +340,7 @@ labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), key: 2, editor: "views/blockelements/labelblock/labelblock.editor.html", + overlaySize: 'medium', content: { variants: [ { @@ -335,6 +387,7 @@ labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), key: 3, editor: "views/blockelements/labelblock/labelblock.editor.html", + overlaySize: 'medium', content: { variants: [ { @@ -438,10 +491,12 @@ } } - - vm.getBlockLabel = function(block) { - var name = ""; + function getBlockLabel(block) { + + console.log("getBlockLabel", block) + + // TODO: we should do something about this for performance. var props = new Object(); @@ -473,7 +528,7 @@ var elementEditor = { block: blockModel, view: "views/common/infiniteeditors/elementeditor/elementeditor.html", - size: "large", + size: blockModel.overlaySize, submit: function(model) { blockModel.content = model.block.content; editorService.close(); @@ -551,7 +606,7 @@ vm.sortableOptions = { axis: "y", cursor: "grabbing", - handle: '.umb-block-list__block', + handle: '.blockelement__draggable-element', cancel: 'input,textarea,select,option', classes: '.blockelement--dragging', distance: 5, @@ -596,6 +651,15 @@ } }; + // TODO: We need to investigate if we can do a specific watch on each block, so we dont re-render all blocks. + $scope.$watch('vm.blocks', onBlocksUpdated, true); + function onBlocksUpdated(newVal, oldVal){ + console.log("onBlocksUpdated"); + for(const block of vm.blocks) { + block.label = getBlockLabel(block); + } + } + } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less index a26dda4202..78f4138897 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less @@ -15,7 +15,6 @@ .umb-block-list__block { position: relative; width: 100%; - cursor: grab; .umb-block-list__block--head { opacity: 0; @@ -107,6 +106,10 @@ label.umb-block-list__block--head { border-radius: @baseBorderRadius; } +.blockelement__draggable-element { + cursor: grab; +} + .umb-block-list__block--create-button { position: absolute; @@ -114,10 +117,10 @@ label.umb-block-list__block--head { z-index:1; opacity: 0; outline: none; - height: 20px; - margin-top: -10px; - padding-top: 10px; - margin-bottom: -10px; + height: 12px; + margin-top: -6px; + padding-top: 6px; + margin-bottom: -6px; transition: opacity 240ms; &::before { @@ -125,7 +128,7 @@ label.umb-block-list__block--head { position: absolute; background-color: @ui-outline; border-radius: 2px; - top:9px; + top:5px; right: 0; left: 0; height: 2px; @@ -141,6 +144,7 @@ label.umb-block-list__block--head { margin-left: auto; margin-right: auto; margin-top: -16px; + margin-bottom: -16px; width: 28px; height: 25px; padding-bottom: 3px; From 898594cea3135511e2f9ad77fb530ca807dae425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 3 Feb 2020 15:08:43 +0100 Subject: [PATCH 017/508] adjustment of border-radius --- .../views/propertyeditors/blocklist/blocklist.component.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less index 78f4138897..8708fdf000 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less @@ -84,7 +84,7 @@ label.umb-block-list__block--head { right: 10px; font-size: 0; background-color: rgba(255, 255, 255, .96); - border-radius: 14px; + border-radius: 16px; padding-left: 5px; padding-right: 5px; .action { From fcad2c4863903ed0fcd5059afa60e2be847020d1 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 4 Feb 2020 10:00:39 +0000 Subject: [PATCH 018/508] Fix up unit test as a new Data Editor was added for Block Editor --- src/Umbraco.Tests/Composing/TypeLoaderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 7459ae848b..9cd4f39c17 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -268,7 +268,7 @@ AnotherContentFinder public void GetDataEditors() { var types = _typeLoader.GetDataEditors(); - Assert.AreEqual(38, types.Count()); + Assert.AreEqual(39, types.Count()); } /// From 03fe861b9407877067b6c25d770fbf4a50db5859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 4 Feb 2020 14:40:20 +0100 Subject: [PATCH 019/508] dragable --- .../src/views/blockelements/imageblock/imageblock.editor.html | 2 +- .../views/blockelements/textareablock/textareablock.editor.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html index c77f19ba67..0b82d2202d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html @@ -1,3 +1,3 @@ - diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html index 405c8634b4..c64d42b6ac 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html @@ -1,5 +1,5 @@ -
+
From 8c02c33143403cfe6fe5de26cc1e27fd593bfc5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 5 Feb 2020 11:16:31 +0100 Subject: [PATCH 020/508] style create-bar --- .../blocklist/blocklist.component.less | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less index 8708fdf000..02670e88b0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less @@ -132,8 +132,8 @@ label.umb-block-list__block--head { right: 0; left: 0; height: 2px; - animation: umb-block-list__block--create-button 800ms ease-in-out infinite; - @keyframes umb-block-list__block--create-button { + animation: umb-block-list__block--create-button_before 800ms ease-in-out infinite; + @keyframes umb-block-list__block--create-button_before { 0% { opacity: 0.5; } 50% { opacity: 1; } 100% { opacity: 0.5; } @@ -143,8 +143,8 @@ label.umb-block-list__block--head { content: "+"; margin-left: auto; margin-right: auto; - margin-top: -16px; - margin-bottom: -16px; + margin-top: -18px; + margin-bottom: -18px; width: 28px; height: 25px; padding-bottom: 3px; @@ -160,6 +160,12 @@ label.umb-block-list__block--head { box-shadow: 0 0 0 2px rgba(255, 255, 255, .96); transform: scale(0); transition: transform 240ms ease-in; + animation: umb-block-list__block--create-button_after 800ms ease-in-out infinite; + @keyframes umb-block-list__block--create-button_after { + 0% { color: rgba(@ui-outline, 0.8); } + 50% { color: rgba(@ui-outline, 1); } + 100% { color: rgba(@ui-outline, 0.8); } + } } &:focus { &::after { From 41e866c43aba4200a2ce6d2df601f8322a81fdbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 5 Feb 2020 11:16:40 +0100 Subject: [PATCH 021/508] more demo content --- .../blocklist/blocklist.component.js | 363 +++++++++++++----- 1 file changed, 264 insertions(+), 99 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index f3f8c2e5fe..35259eaf9f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -58,7 +58,7 @@ tabs: [ { id: 1234, - label: "Group 1", + label: "Content", properties: [ { label: "Page Title", @@ -76,6 +76,120 @@ isSensitive: false, culture: null, segment: null + }, + { + label: "Image", + description: "", + view: "mediapicker", + config: {multiPicker: false, + onlyImages: true, + disableFolderSelect: true, + startNodeId: "umb://media/1fd2ecaff3714c009306867fa4585e7a", + ignoreUserStartNodes: false, + idType: "udi" + }, + hideLabel: false, + validation: {mandatory: false, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 495, + dataTypeKey: "e26a8d91-a9d7-475b-bc3b-2a09f4743754", + value: "umb://media/fa763e0d0ceb408c8720365d57e06e32", + alias: "photo", + editor: "Umbraco.MediaPicker", + isSensitive: false, + culture: null, + segment: null + }, + { + label: "Image Description", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 442, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Let's have a chat", + alias: "imageDesc", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ] + } + } + }, + { + alias: "pageModule", + name: "Inline module", + icon: "icon-document", + prototype_paste_data: { + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "{{pageTitle | truncate:true:36}}", + labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), + key: 1, + editor: "views/blockelements/inlineblock/inlineblock.editor.html", + overlaySize: 'medium', + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Image Title", + description: "The title on top of image", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn't distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", + alias: "imageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + }, + { + label: "Image", + description: "", + view: "mediapicker", + config: {multiPicker: false, + onlyImages: true, + disableFolderSelect: true, + startNodeId: "umb://media/1fd2ecaff3714c009306867fa4585e7a", + ignoreUserStartNodes: false, + idType: "udi" + }, + hideLabel: false, + validation: {mandatory: false, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 495, + dataTypeKey: "e26a8d91-a9d7-475b-bc3b-2a09f4743754", + value: "umb://media/fa763e0d0ceb408c8720365d57e06e32", + alias: "photo", + editor: "Umbraco.MediaPicker", + isSensitive: false, + culture: null, + segment: null } ] } @@ -159,7 +273,30 @@ label: "Group 1", properties: [ { - label: "Page Title", + label: "Image", + description: "", + view: "mediapicker", + config: {multiPicker: false, + onlyImages: true, + disableFolderSelect: true, + startNodeId: "umb://media/1fd2ecaff3714c009306867fa4585e7a", + ignoreUserStartNodes: false, + idType: "udi" + }, + hideLabel: false, + validation: {mandatory: false, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 495, + dataTypeKey: "e26a8d91-a9d7-475b-bc3b-2a09f4743754", + value: "umb://media/fa763e0d0ceb408c8720365d57e06e32", + alias: "photo", + editor: "Umbraco.MediaPicker", + isSensitive: false, + culture: null, + segment: null + }, + { + label: "Image Description", description: "The title of the page", view: "textbox", config: {maxChars: 500}, @@ -169,7 +306,7 @@ id: 441, dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", value: "Let's have a chat", - alias: "pageTitle", + alias: "imageDesc", editor: "Umbraco.TextBox", isSensitive: false, culture: null, @@ -181,56 +318,6 @@ temp_image: "/umbraco/assets/img/login.jpg" } } - }, - { - alias: "contentTypeAlias", - name: "Inline editing", - icon: "icon-picture", - prototype_paste_data: { - elementType: { - alias: 'contentTypeAlias', - icon: "icon-document", - label: "Text" - }, - label: "Label", - editor: "views/blockelements/imageblock/imageblock.editor.html", - overlaySize: 'medium', - content: { - variants: [ - { - language: { - isDefault: true - } - } - ], - tabs: [ - { - id: 1234, - label: "Group 1", - properties: [ - { - label: "Page Title", - description: "The title of the page", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 441, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "Let's have a chat", - alias: "pageTitle", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - } - ] - } - ], - temp_image: "/umbraco/assets/img/demo.png" - } - } } ]; @@ -238,52 +325,6 @@ // TODO: get icon, properties etc. from available types? vm.blocks = [ - { - elementType: { - alias: 'contentTypeAlias', - icon: "icon-document", - label: "Text" - }, - label: "{{pageTitle | truncate:true:36}}", - labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), - key: 1, - editor: "views/blockelements/inlineblock/inlineblock.editor.html", - overlaySize: 'medium', - content: { - variants: [ - { - language: { - isDefault: true - } - } - ], - tabs: [ - { - id: 1234, - label: "Group 1", - properties: [ - { - label: "Page Title", - description: "The title of the page", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 441, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn't distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", - alias: "pageTitle", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - } - ] - } - ] - } - }, { elementType: { alias: 'contentTypeAlias', @@ -324,6 +365,46 @@ isSensitive: false, culture: null, segment: null + }, + { + label: "Image", + description: "", + view: "mediapicker", + config: {multiPicker: false, + onlyImages: true, + disableFolderSelect: true, + startNodeId: "umb://media/1fd2ecaff3714c009306867fa4585e7a", + ignoreUserStartNodes: false, + idType: "udi" + }, + hideLabel: false, + validation: {mandatory: false, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 495, + dataTypeKey: "e26a8d91-a9d7-475b-bc3b-2a09f4743754", + value: "umb://media/fa763e0d0ceb408c8720365d57e06e32", + alias: "photo", + editor: "Umbraco.MediaPicker", + isSensitive: false, + culture: null, + segment: null + }, + { + label: "Image Description", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 442, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Let's have a chat", + alias: "imageDesc", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null } ] } @@ -370,6 +451,46 @@ isSensitive: false, culture: null, segment: null + }, + { + label: "Image", + description: "", + view: "mediapicker", + config: {multiPicker: false, + onlyImages: true, + disableFolderSelect: true, + startNodeId: "umb://media/1fd2ecaff3714c009306867fa4585e7a", + ignoreUserStartNodes: false, + idType: "udi" + }, + hideLabel: false, + validation: {mandatory: false, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 495, + dataTypeKey: "e26a8d91-a9d7-475b-bc3b-2a09f4743754", + value: "umb://media/fa763e0d0ceb408c8720365d57e06e32", + alias: "photo", + editor: "Umbraco.MediaPicker", + isSensitive: false, + culture: null, + segment: null + }, + { + label: "Image Description", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 442, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Let's have a chat", + alias: "imageDesc", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null } ] } @@ -417,6 +538,46 @@ isSensitive: false, culture: null, segment: null + }, + { + label: "Image", + description: "", + view: "mediapicker", + config: {multiPicker: false, + onlyImages: true, + disableFolderSelect: true, + startNodeId: "umb://media/1fd2ecaff3714c009306867fa4585e7a", + ignoreUserStartNodes: false, + idType: "udi" + }, + hideLabel: false, + validation: {mandatory: false, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 495, + dataTypeKey: "e26a8d91-a9d7-475b-bc3b-2a09f4743754", + value: "umb://media/fa763e0d0ceb408c8720365d57e06e32", + alias: "photo", + editor: "Umbraco.MediaPicker", + isSensitive: false, + culture: null, + segment: null + }, + { + label: "Image Description", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 442, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Let's have a chat", + alias: "imageDesc", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null } ] } @@ -425,6 +586,10 @@ } ]; + + + + function setDirty() { if (vm.propertyForm) { vm.propertyForm.$setDirty(); From 6fb9afc3d8e8306d8b123590aaa79be1a86d45a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 5 Feb 2020 11:20:53 +0100 Subject: [PATCH 022/508] labelTemplate rename --- .../blocklist/blocklist.component.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 35259eaf9f..56a936982b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -133,8 +133,8 @@ icon: "icon-document", label: "Text" }, - label: "{{pageTitle | truncate:true:36}}", - labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), + labelTemplate: "{{imageTitle | truncate:true:36}}", + labelInterpolate: $interpolate("{{imageTitle | truncate:true:36}}"), key: 1, editor: "views/blockelements/inlineblock/inlineblock.editor.html", overlaySize: 'medium', @@ -207,7 +207,8 @@ icon: "icon-document", label: "Text" }, - label: "Label", + labelTemplate: "Label", + labelInterpolate: $interpolate("Label"), editor: "views/blockelements/textareablock/textareablock.editor.html", overlaySize: 'medium', content: { @@ -256,7 +257,8 @@ icon: "icon-document", label: "Text" }, - label: "Label", + labelTemplate: "Label", + labelInterpolate: $interpolate("Label"), editor: "views/blockelements/imageblock/imageblock.editor.html", overlaySize: 'medium', content: { @@ -331,7 +333,7 @@ icon: "icon-document", label: "Text" }, - label: "{{pageTitle | truncate:true:36}}", + labelTemplate: "{{pageTitle | truncate:true:36}}", labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), key: 1, editor: "views/blockelements/labelblock/labelblock.editor.html", @@ -417,7 +419,7 @@ icon: "icon-document", label: "Text" }, - label: "{{pageTitle | truncate:true:36}}", + labelTemplate: "{{pageTitle | truncate:true:36}}", labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), key: 2, editor: "views/blockelements/labelblock/labelblock.editor.html", @@ -504,7 +506,7 @@ icon: "icon-document", label: "Text" }, - label: "{{pageTitle | truncate:true:36}}", + labelTemplate: "{{pageTitle | truncate:true:36}}", labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), key: 3, editor: "views/blockelements/labelblock/labelblock.editor.html", From 3b12d25dddd92a1ec989ebcc44486c4e43514003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 5 Feb 2020 11:24:38 +0100 Subject: [PATCH 023/508] which around actions --- .../blocklist/blocklist.component.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index e38858c8b7..8aa40e26d9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -52,18 +52,18 @@
- - +
From a2e2b63aabaa506d2f646506fbe99a7bd6d74581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 5 Feb 2020 14:05:56 +0100 Subject: [PATCH 024/508] added content-apps --- .../blocklist/blocklist.component.js | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 56a936982b..1c78435586 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -48,6 +48,31 @@ editor: "views/blockelements/labelblock/labelblock.editor.html", overlaySize: 'medium', content: { + apps: [ + { + name: "Content", + alias: "umbContent", + weight: -100, + icon: "icon-document", + view: "views/content/apps/content/content.html", + viewModel: 0, + active: true, + badge: null, + anchors: [], + hasError: false + }, + { + name: "Info", + alias: "umbInfo", + weight: 100, + icon: "icon-info", + view: "views/content/apps/info/info.html", + viewModel: null, + active: false, + badge: null, + hasError: false + } + ], variants: [ { language: { @@ -139,6 +164,31 @@ editor: "views/blockelements/inlineblock/inlineblock.editor.html", overlaySize: 'medium', content: { + apps: [ + { + name: "Content", + alias: "umbContent", + weight: -100, + icon: "icon-document", + view: "views/content/apps/content/content.html", + viewModel: 0, + active: true, + badge: null, + anchors: [], + hasError: false + }, + { + name: "Info", + alias: "umbInfo", + weight: 100, + icon: "icon-info", + view: "views/content/apps/info/info.html", + viewModel: null, + active: false, + badge: null, + hasError: false + } + ], variants: [ { language: { @@ -212,6 +262,31 @@ editor: "views/blockelements/textareablock/textareablock.editor.html", overlaySize: 'medium', content: { + apps: [ + { + name: "Content", + alias: "umbContent", + weight: -100, + icon: "icon-document", + view: "views/content/apps/content/content.html", + viewModel: 0, + active: true, + badge: null, + anchors: [], + hasError: false + }, + { + name: "Info", + alias: "umbInfo", + weight: 100, + icon: "icon-info", + view: "views/content/apps/info/info.html", + viewModel: null, + active: false, + badge: null, + hasError: false + } + ], variants: [ { language: { @@ -262,6 +337,31 @@ editor: "views/blockelements/imageblock/imageblock.editor.html", overlaySize: 'medium', content: { + apps: [ + { + name: "Content", + alias: "umbContent", + weight: -100, + icon: "icon-document", + view: "views/content/apps/content/content.html", + viewModel: 0, + active: true, + badge: null, + anchors: [], + hasError: false + }, + { + name: "Info", + alias: "umbInfo", + weight: 100, + icon: "icon-info", + view: "views/content/apps/info/info.html", + viewModel: null, + active: false, + badge: null, + hasError: false + } + ], variants: [ { language: { @@ -339,6 +439,31 @@ editor: "views/blockelements/labelblock/labelblock.editor.html", overlaySize: 'medium', content: { + apps: [ + { + name: "Content", + alias: "umbContent", + weight: -100, + icon: "icon-document", + view: "views/content/apps/content/content.html", + viewModel: 0, + active: true, + badge: null, + anchors: [], + hasError: false + }, + { + name: "Info", + alias: "umbInfo", + weight: 100, + icon: "icon-info", + view: "views/content/apps/info/info.html", + viewModel: null, + active: false, + badge: null, + hasError: false + } + ], variants: [ { language: { @@ -425,6 +550,31 @@ editor: "views/blockelements/labelblock/labelblock.editor.html", overlaySize: 'medium', content: { + apps: [ + { + name: "Content", + alias: "umbContent", + weight: -100, + icon: "icon-document", + view: "views/content/apps/content/content.html", + viewModel: 0, + active: true, + badge: null, + anchors: [], + hasError: false + }, + { + name: "Info", + alias: "umbInfo", + weight: 100, + icon: "icon-info", + view: "views/content/apps/info/info.html", + viewModel: null, + active: false, + badge: null, + hasError: false + } + ], variants: [ { language: { @@ -512,6 +662,31 @@ editor: "views/blockelements/labelblock/labelblock.editor.html", overlaySize: 'medium', content: { + apps: [ + { + name: "Content", + alias: "umbContent", + weight: -100, + icon: "icon-document", + view: "views/content/apps/content/content.html", + viewModel: 0, + active: true, + badge: null, + anchors: [], + hasError: false + }, + { + name: "Info", + alias: "umbInfo", + weight: 100, + icon: "icon-info", + view: "views/content/apps/info/info.html", + viewModel: null, + active: false, + badge: null, + hasError: false + } + ], variants: [ { language: { From 39c0e686ff6de6cab4ba26d070d4e9c2715b14ec Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 6 Feb 2020 16:40:16 +1100 Subject: [PATCH 025/508] Remove empty BlockListEditor --- src/Umbraco.Core/PropertyEditors/BlockListEditor.cs | 12 ------------ src/Umbraco.Core/Umbraco.Core.csproj | 1 - 2 files changed, 13 deletions(-) delete mode 100644 src/Umbraco.Core/PropertyEditors/BlockListEditor.cs diff --git a/src/Umbraco.Core/PropertyEditors/BlockListEditor.cs b/src/Umbraco.Core/PropertyEditors/BlockListEditor.cs deleted file mode 100644 index 16c6176624..0000000000 --- a/src/Umbraco.Core/PropertyEditors/BlockListEditor.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Umbraco.Core.PropertyEditors -{ - public class BlockListEditor - { - } -} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 33796a93bf..8bf0b9105f 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -133,7 +133,6 @@ - From 45e892f3505059674779c6e1a43084a367c2862f Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 6 Feb 2020 16:52:34 +1100 Subject: [PATCH 026/508] Changes api to GetData --- .../Models/Blocks/BlockListLayoutReference.cs | 11 +---------- src/Umbraco.Core/Models/Blocks/BlockListModel.cs | 14 +++++++++++++- .../BlockListPropertyValueConverterTests.cs | 11 +++++++---- .../BlockListPropertyValueConverter.cs | 2 +- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs index 19b30e6ea6..85d17fad24 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs @@ -10,10 +10,9 @@ namespace Umbraco.Core.Models.Blocks [DataContract(Name = "blockListLayout", Namespace = "")] public class BlockListLayoutReference : IBlockElement { - public BlockListLayoutReference(Udi udi, IPublishedElement data, IPublishedElement settings) + public BlockListLayoutReference(Udi udi, IPublishedElement settings) { Udi = udi ?? throw new ArgumentNullException(nameof(udi)); - Data = data ?? throw new ArgumentNullException(nameof(data)); Settings = settings; // can be null } @@ -29,13 +28,5 @@ namespace Umbraco.Core.Models.Blocks [DataMember(Name = "settings")] public IPublishedElement Settings { get; } - /// - /// The data item referenced - /// - /// - /// This is ignored from serialization since it is just a reference to the actual data element - /// - [IgnoreDataMember] - public IPublishedElement Data { get; } } } diff --git a/src/Umbraco.Core/Models/Blocks/BlockListModel.cs b/src/Umbraco.Core/Models/Blocks/BlockListModel.cs index 089ca7e6a3..153fe6be8a 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockListModel.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockListModel.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Runtime.Serialization; using Umbraco.Core.Models.PublishedContent; @@ -22,6 +23,17 @@ namespace Umbraco.Core.Models.Blocks [DataMember(Name = "layout")] public IEnumerable Layout { get; } - + /// + /// Returns the data item associated with the layout udi reference + /// + /// + /// + public IPublishedElement GetData(Udi udi) + { + if (!(udi is GuidUdi guidUdi)) + return null; + return Data.FirstOrDefault(x => x.Key == guidUdi.Guid); + } + } } diff --git a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs index ca687f94a6..300c19ac1e 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -317,12 +317,15 @@ data: []}"; Assert.AreEqual(2, converted.Layout.Count()); var item0 = converted.Layout.ElementAt(0); - Assert.AreEqual(Guid.Parse("1304E1DD-AC87-4396-84FE-8A399231CB3D"), item0.Data.Key); - Assert.AreEqual("home", item0.Data.ContentType.Alias); + var item0Data = converted.GetData(item0.Udi); + Assert.IsNotNull(item0Data); + Assert.AreEqual(Guid.Parse("1304E1DD-AC87-4396-84FE-8A399231CB3D"), item0Data.Key); + Assert.AreEqual("home", item0Data.ContentType.Alias); var item1 = converted.Layout.ElementAt(1); - Assert.AreEqual(Guid.Parse("0A4A416E-547D-464F-ABCC-6F345C17809A"), item1.Data.Key); - Assert.AreEqual("home", item1.Data.ContentType.Alias); + var item1Data = converted.GetData(item1.Udi); + Assert.AreEqual(Guid.Parse("0A4A416E-547D-464F-ABCC-6F345C17809A"), item1Data.Key); + Assert.AreEqual("home", item1Data.ContentType.Alias); } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs index 3878b8d605..3d8e8e2b13 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -108,7 +108,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters if (!elements.TryGetValue(guidUdi.Guid, out var data)) continue; - var layoutRef = new BlockListLayoutReference(udi, data, element); + var layoutRef = new BlockListLayoutReference(udi, element); layout.Add(layoutRef); } From 88df0fe69196cc2966562eb930c0b23048f18a9f Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 7 Feb 2020 00:17:21 +1100 Subject: [PATCH 027/508] Adds new methods to MembershipHelper for dealing with checking bulk paths for access --- src/Umbraco.Web/Security/MembershipHelper.cs | 66 +++++++++++++++++--- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index f74897d565..01044dd7f2 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -77,6 +77,17 @@ namespace Umbraco.Web.Security return _publicAccessService.IsProtected(path); } + public virtual IDictionary IsProtected(IEnumerable paths) + { + var result = new Dictionary(); + foreach (var path in paths) + { + //this is a cached call + result[path] = _publicAccessService.IsProtected(path); + } + return result; + } + /// /// Check if the current user has access to a document /// @@ -84,15 +95,33 @@ namespace Umbraco.Web.Security /// True if the current user has access or if the current document isn't protected public virtual bool MemberHasAccess(string path) { - //cache this in the request cache - return _appCaches.RequestCache.GetCacheItem($"{typeof(MembershipHelper)}.MemberHasAccess-{path}", () => + if (IsProtected(path)) { - if (IsProtected(path)) - { - return IsLoggedIn() && HasAccess(path, Roles.Provider); - } - return true; - }); + return IsLoggedIn() && HasAccess(path, Roles.Provider); + } + return true; + } + + /// + /// Checks if the current user has access to the paths + /// + /// + /// + public virtual IDictionary MemberHasAccess(IEnumerable paths) + { + var protectedPaths = IsProtected(paths); + + var pathsWithProtection = protectedPaths.Where(x => x.Value).Select(x => x.Key); + var pathsWithAccess = HasAccess(pathsWithProtection, Roles.Provider); + + var result = new Dictionary(); + foreach(var path in paths) + { + pathsWithAccess.TryGetValue(path, out var hasAccess); + // if it's not found it's false anyways + result[path] = hasAccess; + } + return result; } /// @@ -106,6 +135,25 @@ namespace Umbraco.Web.Security return _publicAccessService.HasAccess(path, CurrentUserName, roleProvider.GetRolesForUser); } + private IDictionary HasAccess(IEnumerable paths, RoleProvider roleProvider) + { + // ensure we only lookup user roles once + string[] userRoles = null; + string[] getUserRoles(string username) + { + if (userRoles != null) return userRoles; + userRoles = roleProvider.GetRolesForUser(username).ToArray(); + return userRoles; + } + + var result = new Dictionary(); + foreach (var path in paths) + { + result[path] = IsLoggedIn() && _publicAccessService.HasAccess(path, CurrentUserName, getUserRoles); + } + return result; + } + /// /// Returns true if the current membership provider is the Umbraco built-in one. /// @@ -796,7 +844,7 @@ namespace Umbraco.Web.Security private static string GetCacheKey(string key, params object[] additional) { var sb = new StringBuilder(); - sb.Append(typeof (MembershipHelper).Name); + sb.Append(typeof(MembershipHelper).Name); sb.Append("-"); sb.Append(key); foreach (var s in additional) From 4784a7b1eb74a4d2bb3c26e0334dd69c85755fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 7 Feb 2020 12:45:10 +0100 Subject: [PATCH 028/508] more data for demo --- .../blocklist/blocklist.component.js | 104 +++++++++++++++++- 1 file changed, 98 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 1c78435586..287d5aba08 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -143,6 +143,29 @@ segment: null } ] + }, + { + id: 1234, + label: "Styling", + properties: [ + { + label: "Background color", + description: "", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn't distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] } ] } @@ -199,7 +222,7 @@ tabs: [ { id: 1234, - label: "Group 1", + label: "Content", properties: [ { label: "Image Title", @@ -297,7 +320,7 @@ tabs: [ { id: 1234, - label: "Group 1", + label: "Content", properties: [ { label: "Page Title", @@ -372,7 +395,7 @@ tabs: [ { id: 1234, - label: "Group 1", + label: "Content", properties: [ { label: "Image", @@ -474,7 +497,7 @@ tabs: [ { id: 1234, - label: "Group 1", + label: "Content", properties: [ { label: "Page Title", @@ -534,6 +557,29 @@ segment: null } ] + }, + { + id: 1234, + label: "Styling", + properties: [ + { + label: "Background color", + description: "", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn't distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] } ] } @@ -585,7 +631,7 @@ tabs: [ { id: 1234, - label: "Group 1", + label: "Content", properties: [ { label: "Page Title", @@ -645,6 +691,29 @@ segment: null } ] + }, + { + id: 1234, + label: "Styling", + properties: [ + { + label: "Background color", + description: "", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn't distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] } ] } @@ -697,7 +766,7 @@ tabs: [ { id: 1234, - label: "Group 1", + label: "Content", properties: [ { label: "Page Title", @@ -757,6 +826,29 @@ segment: null } ] + }, + { + id: 1234, + label: "Styling", + properties: [ + { + label: "Background color", + description: "", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn't distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] } ] } From bc1f5f2086035f2664e93a510bc4a9e1614233cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 12 Feb 2020 17:17:04 +0100 Subject: [PATCH 029/508] Initial work on Block List Prevalue Editor --- .../common/resources/elementtype.resource.js | 32 +++ src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../overlays/itempicker/itempicker.html | 8 + .../blocklist.elementtypepicker.controller.js | 236 ++++++++++++++++++ .../prevalue/blocklist.elementtypepicker.html | 81 ++++++ .../prevalue/blocklist.elementtypepicker.less | 114 +++++++++ src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 9 + .../Umbraco/config/lang/en_us.xml | 9 + .../PropertyEditors/BlockListConfiguration.cs | 23 +- .../BlockListConfigurationEditor.cs | 20 ++ .../BlockListPropertyEditor.cs | 3 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 12 files changed, 534 insertions(+), 3 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/resources/elementtype.resource.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less create mode 100644 src/Umbraco.Web/PropertyEditors/BlockListConfigurationEditor.cs mode change 100755 => 100644 src/Umbraco.Web/Umbraco.Web.csproj diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/elementtype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/elementtype.resource.js new file mode 100644 index 0000000000..680b75ac78 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/resources/elementtype.resource.js @@ -0,0 +1,32 @@ +/** + * @ngdoc service + * @name umbraco.resources.elementTypeResource + * @description Loads in data for element types + **/ +function elementTypeResource($q, $http, umbRequestHelper) { + + return { + + getAll: function () { + + // TODO: Change this into a real api (ElementTypeApi). This is a temporary fix to get data. + var url = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + "/backoffice/UmbracoApi/NestedContent/GetContentTypes"; + return umbRequestHelper.resourcePromise( + $http.get(url), + 'Failed to retrieve content types' + ); + + /* + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "elementTypeApiBaseUrl", + "GetAll")), + "Failed to retrieve data"); + */ + } + + }; +} + +angular.module("umbraco.resources").factory("elementTypeResource", elementTypeResource); diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index f264038e93..9b9d10b965 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -199,6 +199,7 @@ // Property Editors @import "../views/propertyeditors/blocklist/blocklist.component.less"; +@import "../views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less"; // Utilities diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.html index 715dc12a07..eec87246c0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.html @@ -42,5 +42,13 @@ +
  • + +
  • diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js new file mode 100644 index 0000000000..13937a0625 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js @@ -0,0 +1,236 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.PropertySettingsController + * @function + * + * @description + * The controller for the content type editor property settings dialog + */ + +(function () { + "use strict"; + + function ElementTypePickerController($scope, elementTypeResource, overlayService, localizationService, editorService) { + + var vm = this; + + vm.enableAddEntry = true; + + function evaluateStatus() { + + if (!vm.elementTypes) return;// cancel if elementTypes isnt loaded jet. + + vm.enableAddEntry = vm.getAvailableElementTypes().length > 0; + + } + + function onInit() { + + if (!$scope.model.value) { + $scope.model.value = []; + } + + localizationService.localize("content_nestedContentSelectElementTypeModalTitle").then(function (value) { + //selectElementTypeModalTitle = value; + }); + + loadElementTypes(); + + } + + function loadElementTypes() { + return elementTypeResource.getAll().then(function (elementTypes) { + vm.elementTypes = elementTypes; + console.log("vm.elementTypes:", vm.elementTypes) + evaluateStatus(); + }); + } + + vm.removeEntryByIndex = function (index) { + $scope.model.value.splice(index, 1); + }; + + vm.sortableOptions = { + axis: "y", + cursor: "grabbing", + placeholder: 'sortable-placeholder', + forcePlaceholderSize: true + }; + + + vm.getAvailableElementTypes = function () { + return vm.elementTypes.filter(function (type) { + return !$scope.model.value.find(function (entry) { + return type.alias === entry.elementTypeAlias; + }); + }); + }; + + vm.getElementTypeByAlias = function(alias) { + return _.find(vm.elementTypes, function (type) { + return type.alias === alias; + }); + }; + + vm.openAddDialog = function ($event, entry) { + + //we have to add the alias to the objects (they are stored as elementTypeAlias) + var selectedItems = _.each($scope.model.value, function (obj) { + obj.alias = obj.elementTypeAlias; + return obj; + }); + + var availableItems = vm.getAvailableElementTypes() + + var elemTypeSelectorOverlay = { + view: "itempicker", + title: "no title jet", + availableItems: availableItems, + selectedItems: selectedItems, + createNewItem: { + action: function() { + overlayService.close(); + vm.createElementTypeAndAdd(vm.addEntryFromElementTypeAlias); + }, + icon: "icon-add", + name: "Create new" + }, + position: "target", + event: $event, + size: availableItems.length < 7 ? "small" : "medium", + submit: function (overlay) { + vm.addEntryFromElementTypeAlias(overlay.selectedItem.alias); + overlayService.close(); + }, + close: function () { + overlayService.close(); + } + }; + + overlayService.open(elemTypeSelectorOverlay); + }; + + vm.createElementTypeAndAdd = function(callback) { + const editor = { + create: true, + infiniteMode: true, + isElement: true, + submit: function (model) { + console.log(model) + loadElementTypes().then( function () { + callback(model.documentTypeAlias); + }); + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.documentTypeEditor(editor); + } + + vm.addEntryFromElementTypeAlias = function(alias) { + + var entry = { + "elementTypeAlias": alias, + "view": null, + "labelTemplate": "", + "settingsElementTypeAlias": null + }; + + $scope.model.value.push(entry); + }; + + vm.removeSettingsForEntry = function(entry) { + entry.settingsElementTypeAlias = null; + }; + + vm.openPickSettingsDialog = function ($event, entry) { + + var elemTypeSelectorOverlay = { + view: "itempicker", + title: "Pick settings (missing translation)", + availableItems: vm.elementTypes, + position: "target", + event: $event, + size: vm.elementTypes.length < 7 ? "small" : "medium", + createNewItem: { + action: function() { + overlayService.close(); + vm.createElementTypeAndAdd((alias) => { + vm.addSettingsAtEntry(entry, alias); + }); + }, + icon: "icon-add", + name: "Create new" + }, + submit: function (overlay) { + vm.addSettingsAtEntry(entry, overlay.selectedItem.alias); + overlayService.close(); + }, + close: function () { + overlayService.close(); + } + }; + + overlayService.open(elemTypeSelectorOverlay); + }; + vm.addSettingsAtEntry = function(entry, alias) { + entry.settingsElementTypeAlias = alias; + }; + + vm.openElementType = function(elementTypeAlias) { + var elementTypeId = vm.getElementTypeByAlias(elementTypeAlias).id; + const editor = { + id: elementTypeId, + submit: function (model) { + loadElementTypes(); + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.documentTypeEditor(editor); + } + + vm.removeViewForEntry = function(entry) { + entry.view = null; + }; + vm.addViewForEntry = function(entry) { + const viewPicker = { + title: "Pick view (TODO need translation)", + section: "settings", + treeAlias: "partialView", + entityType: "partialView", + onlyInitialized: false, + filter: function (i) { + if (i.name.indexOf(".cshtml") === -1 && i.name.indexOf(".vbhtml") === -1) { + return true; + } + }, + filterCssClass: "not-allowed", + select: function (node) { + console.log(node); + //entry.view = node.name; + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.treePicker(viewPicker); + } + + + onInit(); + + $scope.$watchCollection('model.value', function(newVal, oldVal) { + evaluateStatus(); + }); + + } + + angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockList.ElementTypePickerController", ElementTypePickerController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html new file mode 100644 index 0000000000..d40ec19311 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html @@ -0,0 +1,81 @@ +
    +
    +
    +
    +
    +
    + Custom view +
    +
    + Label + +
    +
    + Custom view +
    +
    + Custom view +
    +
    +
    +
    +
    +
    +
    +
    + {{ contentPreview = vm.getElementTypeByAlias(entry.elementTypeAlias); "" }} + + +
    +
    + +
    +
    +
    + + + + +
    + +
    +
    +
    + {{ settingsPreview = vm.getElementTypeByAlias(entry.settingsElementTypeAlias); "" }} + + + + +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less new file mode 100644 index 0000000000..fb22ea8b46 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less @@ -0,0 +1,114 @@ +.umb-block-list-element-type-picker { + + .block-entry { + cursor: grab; + background-color: white; + border-radius: @baseBorderRadius; + } + + .umb-table-head { + button { + margin-left: 5px; + color: @ui-action-discreet-type; + &:hover { + color: @ui-action-discreet-type-hover; + } + } + } + + .umb-table-cell { + padding-left: 10px; + padding-right: 0; + &.action-cell { + padding-right: 15px; + } + } + + .action-cell { + flex: 0 0 30px; + } + + .text-input { + width: 100%; + } + + .umb-node-preview { + flex-grow: 1; + } + + .cell-btn { + position: relative; + opacity: 0; + color: @ui-action-discreet-type; + height: 30px; + width: 26px; + margin-top: 1px; + &:hover { + color: @ui-action-discreet-type-hover; + } + &:last-of-type { + margin-right: 7px; + } + } + .umb-table-cell:hover, + .umb-table-cell:focus, + .umb-table-cell:focus-within { + .cell-btn { + opacity: 1; + } + } + + .settings-input { + position: relative; + padding: 5px 8px; + color: @ui-action-discreet-type; + border: 1px dashed @ui-action-discreet-border; + width: 100%; + font-weight: bold; + display: flex; + flex-flow: row nowrap; + + localize { + width: 100%; + } + + .umb-node-preview { + padding: 3px 0; + margin-left: 5px; + } + + &.--noValue { + text-align: center; + border-radius: @baseBorderRadius; + &:hover { + color: @ui-action-discreet-type-hover; + border-color: @ui-action-discreet-border-hover; + } + } + + &.--hasValue { + border: 1px solid @inputBorder; + padding: 0; + } + } + + .add-button { + width:100%; + color: @ui-action-discreet-type; + border: 1px dashed @ui-action-discreet-border; + border-radius: @baseBorderRadius; + display: flex; + align-items: center; + justify-content: center; + padding: 5px 15px; + box-sizing: border-box; + margin: 10px 0; + font-weight: bold; + } + + .add-button:hover { + color: @ui-action-discreet-type-hover; + border-color: @ui-action-discreet-border-hover; + } + +} diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index a85df5714b..a7abf75281 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2393,4 +2393,13 @@ To manage your website, simply open the Umbraco back office and start adding con Umbraco Forms Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it! + + Content model + Label + Custom view + Settings model + Add custom view + Add settings + Overwrite label template + diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index d14fb03727..e4fb330296 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2405,4 +2405,13 @@ To manage your website, simply open the Umbraco back office and start adding con Umbraco Forms Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it! + + Content model + Label + Custom view + Settings model + Add custom view + Add settings + Overwrite label template + diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index 95366db486..ebdc39e445 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -9,16 +9,35 @@ namespace Umbraco.Web.PropertyEditors ///
    public class BlockListConfiguration { - [ConfigurationField("elementTypes", "Element Types", "views/propertyeditors/blocklist/blocklist.elementtypepicker.html", Description = "Select the Element Types to use as models for the items.")] + + // TODO: rename this to blockDefinitions, cause its not elementTypes, its a dictionary of objects that define blocks, part of a block is the elementType used as content model. + [ConfigurationField("elementTypes", "Available Blocks", "views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html", Description = "Define the available blocks.")] public ElementType[] ElementTypes { get; set; } - // TODO: Fill me in + [ConfigurationField("minNumber", "Minimum amount", "number")] + public int MinNumber { get; set; } + + [ConfigurationField("maxNumber", "Maximum amount", "number")] + public int MaxNumber { get; set; } public class ElementType { + // TODO: rename this to contentElementTypeAlias, I would like this to be specific, since we have the settings. [JsonProperty("elementTypeAlias")] public string Alias { get; set; } + + [JsonProperty("settingsElementTypeAlias")] + public string SettingsElementTypeAlias { get; set; } + + [JsonProperty("view")] + public string View { get; set; } + + [JsonProperty("labelTemplate")] + public string Template { get; set; } } + [ConfigurationField("useAccordionsAsDefault", "Inline editing mode", "boolean", Description = "Use the inline editor as the default block view")] + public bool useInlineEditingAsDefault { get; set; } + } } diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfigurationEditor.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfigurationEditor.cs new file mode 100644 index 0000000000..3a4e3eae9b --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfigurationEditor.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Umbraco.Core; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + internal class BlockListConfigurationEditor : ConfigurationEditor + { + public BlockListConfigurationEditor() + { + + } + + } +} diff --git a/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs index 9c4e2f460f..782122bccd 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs @@ -22,7 +22,8 @@ namespace Umbraco.Web.PropertyEditors { } #region Pre Value Editor - //protected override IConfigurationEditor CreateConfigurationEditor() => new BlockEditorListConfigurationEditor(); + + protected override IConfigurationEditor CreateConfigurationEditor() => new BlockListConfigurationEditor(); #endregion diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj old mode 100755 new mode 100644 index e660108cad..c8812ad9ec --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -235,6 +235,7 @@ + From bc73c6f060d699d622b4d3dffa2ce841b76b6643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 12 Feb 2020 17:17:36 +0100 Subject: [PATCH 030/508] ability to not embed templates in dev mode --- src/Umbraco.Web.UI.Client/gulp/config.js | 6 ++++-- src/Umbraco.Web.UI.Client/gulp/util/processJs.js | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/gulp/config.js b/src/Umbraco.Web.UI.Client/gulp/config.js index a807d63f5f..92e0b6d21d 100755 --- a/src/Umbraco.Web.UI.Client/gulp/config.js +++ b/src/Umbraco.Web.UI.Client/gulp/config.js @@ -3,10 +3,12 @@ module.exports = { compile: { build: { - sourcemaps: false + sourcemaps: false, + embedtemplates: true }, dev: { - sourcemaps: true + sourcemaps: true, + embedtemplates: false } }, sources: { diff --git a/src/Umbraco.Web.UI.Client/gulp/util/processJs.js b/src/Umbraco.Web.UI.Client/gulp/util/processJs.js index e3e393b661..67dd6dd420 100644 --- a/src/Umbraco.Web.UI.Client/gulp/util/processJs.js +++ b/src/Umbraco.Web.UI.Client/gulp/util/processJs.js @@ -25,7 +25,9 @@ module.exports = function (files, out) { .pipe(sort()); //in production, embed the templates - task = task.pipe(embedTemplates({ basePath: "./src/", minimize: { loose: true } })) + if(config.compile.current.embedtemplates === true) { + task = task.pipe(embedTemplates({ basePath: "./src/", minimize: { loose: true } })); + } task = task.pipe(concat(out)) .pipe(wrap('(function(){\n%= body %\n})();')) From 7b8cd45f278c06b84f1b484ac987cd58b45dd28a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 12 Feb 2020 17:18:10 +0100 Subject: [PATCH 031/508] add style to create-option in itempicker + removing overflow hidden --- .../src/less/components/card.less | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/card.less b/src/Umbraco.Web.UI.Client/src/less/components/card.less index ed80359833..04992d591f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/card.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/card.less @@ -93,7 +93,7 @@ } .umb-card-grid li { - overflow: hidden; + font-size: 12px; text-align: center; box-sizing: border-box; @@ -135,11 +135,20 @@ } -.umb-card-grid .umb-card-grid-item:hover, -.umb-card-grid .umb-card-grid-item:focus { +.umb-card-grid .umb-card-grid-item:hover { background-color: @ui-option-hover; color: @ui-option-type-hover; } +.umb-card-grid .umb-card-grid-item:focus { + color: @ui-option-type-hover; +} + +.umb-card-grid .umb-card-grid-item.--creator { + border: 1px dashed @ui-action-discreet-border; + &:hover { + border-color: @ui-action-discreet-border-hover; + } +} .umb-card-grid a { color: @ui-option-type; From bb3bccb1cc398da761082519231c6be67a3e0613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 13 Feb 2020 12:36:48 +0100 Subject: [PATCH 032/508] style adjustment --- .../prevalue/blocklist.elementtypepicker.less | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less index fb22ea8b46..f76e9ee1fd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less @@ -6,6 +6,10 @@ border-radius: @baseBorderRadius; } + .umb-table { + border:1px solid @gray-11; + } + .umb-table-head { button { margin-left: 5px; @@ -80,7 +84,9 @@ &.--noValue { text-align: center; border-radius: @baseBorderRadius; - &:hover { + color: white; + transition: color 120ms; + &:hover, &:focus { color: @ui-action-discreet-type-hover; border-color: @ui-action-discreet-border-hover; } @@ -102,7 +108,7 @@ justify-content: center; padding: 5px 15px; box-sizing: border-box; - margin: 10px 0; + margin: 20px 0; font-weight: bold; } From 596c6b937d3924fe3396ef41829ec90287f5c8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 13 Feb 2020 12:36:57 +0100 Subject: [PATCH 033/508] clean up of html --- .../prevalue/blocklist.elementtypepicker.html | 134 +++++++++--------- 1 file changed, 65 insertions(+), 69 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html index d40ec19311..830ee4124e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html @@ -1,81 +1,77 @@
    -
    -
    -
    -
    -
    - Custom view -
    -
    - Label - -
    -
    - Custom view -
    -
    - Custom view -
    -
    -
    +
    +
    +
    +
    + Custom view +
    +
    + Label + +
    +
    + Custom view +
    +
    + Custom view +
    +
    -
    -
    -
    - {{ contentPreview = vm.getElementTypeByAlias(entry.elementTypeAlias); "" }} - -
    +
    +
    +
    + {{ contentPreview = vm.getElementTypeByAlias(entry.elementTypeAlias); "" }} + + +
    +
    + +
    +
    +
    + + + + +
    + +
    +
    +
    + {{ settingsPreview = vm.getElementTypeByAlias(entry.settingsElementTypeAlias); "" }} + + + -
    -
    - -
    -
    -
    - - - - -
    - -
    -
    -
    - {{ settingsPreview = vm.getElementTypeByAlias(entry.settingsElementTypeAlias); "" }} - - - - -
    - -
    -
    -
    + +
    +
    +
    -
    - -
    +
    From abdf8bc22cd7b7a3a43e5e9dff25278d2c0e7392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 13 Feb 2020 12:37:28 +0100 Subject: [PATCH 034/508] correct sentence to use the number 7 --- .../src/views/propertyeditors/blocklist/blocklist.component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 287d5aba08..0aa6003dca 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -988,7 +988,7 @@ vm.blockTypePicker = { show: true, - size: vm.availableBlockTypes.length > 6 ? "medium" : "small", + size: vm.availableBlockTypes.length < 7 ? "small" : "medium", filter: vm.availableBlockTypes.length > 12 ? true : false, orderBy: "$index", view: "itempicker", From 35ba840a25c8c484c2c61ff4613f4247b3cf28d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 13 Feb 2020 12:37:49 +0100 Subject: [PATCH 035/508] correct overlays, so they can use size --- src/Umbraco.Web.UI.Client/src/less/components/overlays.less | 2 +- .../src/views/components/overlays/umb-overlay.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less index eb8740b385..bbd866a5fd 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less @@ -128,7 +128,7 @@ border-radius: @baseBorderRadius; &.umb-overlay--medium { - width: 480px; + width: 520px; } } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html b/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html index a19cf40e1a..5735f8462b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html @@ -1,4 +1,4 @@ - diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index 8aa40e26d9..7fcee650fc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -90,6 +90,21 @@ + + + + +
    +
    + Minimum %0% entries, needs %1% more. +
    +
    +
    +
    + Maximum %0% entries, %1% too many. +
    +
    +
    = 48 && event.keyCode <= 90)// 0 to z - || - (event.keyCode >= 96 && event.keyCode <= 111)// numpads - || - (event.keyCode >= 186 && event.keyCode <= 222)// semi-colon and a lot of other special characters - ) { - // Continue writting... needs to know default text-element. if we have one. - } - } - - function hideCreateOptions() { - vm.quickMenuVisible = false; - window.removeEventListener("keydown", handleTypingInCreateOptions); - } - - vm.onCreateOptionsBlur = function($event) { - - if(!$($event.relatedTarget).is(".umb-block-list__block--create-bar > button")) { - hideCreateOptions(); - } - } function getBlockLabel(block) { - console.log("getBlockLabel", block) - // TODO: we should do something about this for performance. var props = new Object(); @@ -1012,13 +958,11 @@ console.log("copy") } vm.requestDeleteBlock = function(block) { - localizationService.localizeMany(["content_nestedContentDeleteItem", "general_delete", "general_cancel", "contentTypeEditor_yesDelete"]).then(function (data) { + localizationService.localizeMany(["general_delete", "blockEditor_confirmDeleteBlockMessage", "contentTypeEditor_yesDelete"]).then(function (data) { const overlay = { - title: data[1], - content: data[0], - closeButtonLabel: data[2], - submitButtonLabel: data[3], - submitButtonStyle: "danger", + title: data[0], + content: localizationService.tokenReplace(data[1], [block.label]), + submitButtonLabel: data[2], close: function () { overlayService.close(); }, @@ -1028,7 +972,7 @@ } }; - overlayService.open(overlay); + overlayService.confirmDelete(overlay); }); } @@ -1040,9 +984,9 @@ vm.sortableOptions = { axis: "y", cursor: "grabbing", - handle: '.blockelement__draggable-element', - cancel: 'input,textarea,select,option', - classes: '.blockelement--dragging', + handle: ".blockelement__draggable-element", + cancel: "input,textarea,select,option", + classes: ".blockelement--dragging", distance: 5, tolerance: "pointer", scroll: true, @@ -1062,15 +1006,14 @@ }; $scope.blockApi = { - showCreateOptionsFor: vm.showCreateOptionsFor, removeBlock: vm.removeBlock } var copyAllEntriesAction = { - labelKey: 'clipboard_labelForCopyAllEntries', + labelKey: "clipboard_labelForCopyAllEntries", labelTokens: [model.label], - icon: 'documents', + icon: "documents", method: function () {}, isDisabled: true } @@ -1085,14 +1028,40 @@ } }; + + function validateLimits() { + if (vm.validationLimit.min && vm.blocks.length < vm.validationLimit.min) { + vm.propertyForm.minCount.$setValidity("minCount", false); + } + else { + vm.propertyForm.minCount.$setValidity("minCount", true); + } + + if (vm.validationLimit.max && vm.blocks.length > vm.validationLimit.max) { + vm.propertyForm.maxCount.$setValidity("maxCount", false); + } + else { + vm.propertyForm.maxCount.$setValidity("maxCount", true); + } + } + + + + // TODO: We need to investigate if we can do a specific watch on each block, so we dont re-render all blocks. - $scope.$watch('vm.blocks', onBlocksUpdated, true); + unsubscribe.push($scope.$watch("vm.blocks", onBlocksUpdated, true)); function onBlocksUpdated(newVal, oldVal){ - console.log("onBlocksUpdated"); for(const block of vm.blocks) { block.label = getBlockLabel(block); } } + unsubscribe.push($scope.$watch(() => vm.blocks.length, validateLimits)); + + $scope.$on("$destroy", function () { + for (const subscription of unsubscribe) { + subscription(); + } + }); } diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index 5898ee4a70..344604325e 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -15,8 +15,8 @@ namespace Umbraco.Web.PropertyEditors public ElementType[] ElementTypes { get; set; } - [ConfigurationField("range", "Amount", "numberrange", Description = "Set a required range of blocks")] - public NumberRange Range { get; set; } = new NumberRange(); + [ConfigurationField("validationLimit", "Amount", "numberrange", Description = "Set a required range of blocks")] + public NumberRange ValidationLimit { get; set; } = new NumberRange(); public class NumberRange { From 82676ae6498c05b351cd814755ea65bc8eb09b41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 25 Feb 2020 12:55:27 +0100 Subject: [PATCH 047/508] rename ElementTypes to Blocks --- .../BlockListPropertyValueConverterTests.cs | 6 +++--- src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs | 7 +++---- .../ValueConverters/BlockListPropertyValueConverter.cs | 6 +++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs index 300c19ac1e..a8a500d5c0 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -47,7 +47,7 @@ namespace Umbraco.Tests.PropertyEditors private BlockListConfiguration ConfigForMany() => new BlockListConfiguration { - ElementTypes = new[] { + Blocks = new[] { new BlockListConfiguration.ElementType { Alias = "Test1" @@ -61,7 +61,7 @@ namespace Umbraco.Tests.PropertyEditors private BlockListConfiguration ConfigForSingle() => new BlockListConfiguration { - ElementTypes = new[] { + Blocks = new[] { new BlockListConfiguration.ElementType { Alias = "Test1" @@ -111,7 +111,7 @@ namespace Umbraco.Tests.PropertyEditors var valueType = editor.GetPropertyValueType(propType); - var modelType = typeof(IEnumerable<>).MakeGenericType(ModelType.For(config.ElementTypes[0].Alias)); + var modelType = typeof(IEnumerable<>).MakeGenericType(ModelType.For(config.Blocks[0].Alias)); // we can't compare the exact match of types because ModelType.For generates a new/different type even if the same alias is used Assert.AreEqual(modelType.FullName, valueType.FullName); diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index 344604325e..6485bd061a 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -10,9 +10,8 @@ namespace Umbraco.Web.PropertyEditors public class BlockListConfiguration { - // TODO: rename this to blockDefinitions, cause its not elementTypes, its a dictionary of objects that define blocks, part of a block is the elementType used as content model. - [ConfigurationField("elementTypes", "Available Blocks", "views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html", Description = "Define the available blocks.")] - public ElementType[] ElementTypes { get; set; } + [ConfigurationField("blocks", "Available Blocks", "views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html", Description = "Define the available blocks.")] + public BlockConfiguration[] Blocks { get; set; } [ConfigurationField("validationLimit", "Amount", "numberrange", Description = "Set a required range of blocks")] @@ -27,7 +26,7 @@ namespace Umbraco.Web.PropertyEditors public int? Max { get; set; } } - public class ElementType + public class BlockConfiguration { // TODO: rename this to contentElementTypeAlias, I would like this to be specific, since we have the settings. [JsonProperty("elementTypeAlias")] diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs index 3d8e8e2b13..94d64ee29d 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -34,7 +34,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters /// public override Type GetPropertyValueType(IPublishedPropertyType propertyType) { - var contentTypes = propertyType.DataType.ConfigurationAs().ElementTypes; + var contentTypes = propertyType.DataType.ConfigurationAs().Blocks; return contentTypes.Length == 1 ? typeof(IEnumerable<>).MakeGenericType(ModelType.For(contentTypes[0].Alias)) : typeof(IEnumerable); @@ -58,7 +58,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters using (_proflog.DebugDuration($"ConvertPropertyToBlockList ({propertyType.DataType.Id})")) { var configuration = propertyType.DataType.ConfigurationAs(); - var contentTypes = configuration.ElementTypes; + var contentTypes = configuration.Blocks; var elements = (contentTypes.Length == 1 ? (IList)_publishedModelFactory.CreateModelList(contentTypes[0].Alias) : new List()) @@ -116,6 +116,6 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters } } - + } } From 8ffae1ce31befedda4d13025e5f7964a12943f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 26 Feb 2020 13:02:22 +0100 Subject: [PATCH 048/508] implement block list editor --- .../common/services/blockeditor.service.js | 227 ++++++++++++++++++ .../src/common/services/udi.service.js | 32 +++ .../inlineblock/inlineblock.editor.html | 2 +- .../labelblock/labelblock.editor.html | 2 +- .../textareablock.editor.controller.js | 2 +- .../elementContentEditor.component.js | 2 +- .../blocklist/blocklist.component.js | 74 +++--- .../blocklist.elementtypepicker.controller.js | 10 +- .../prevalue/blocklist.elementtypepicker.html | 6 +- .../PropertyEditors/BlockListConfiguration.cs | 6 +- 10 files changed, 321 insertions(+), 42 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/services/udi.service.js diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js new file mode 100644 index 0000000000..8846dc7d3a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -0,0 +1,227 @@ +(function () { + 'use strict'; + + + function blockEditorService($interpolate, udiService) { + + + function applyModelToScaffold(scaffold, contentModel) { + + scaffold.key = contentModel.key; + + var variant = scaffold.variants[0]; + + for (var t = 0; t < variant.tabs.length; t++) { + var tab = variant.tabs[t]; + + for (var p = 0; p < tab.properties.length; p++) { + var prop = tab.properties[p]; + if (contentModel[prop.propertyAlias]) { + prop.value = contentModel[prop.propertyAlias]; + } + } + } + } + + + /** + * @ngdoc factory + * @name umbraco.factory.BlockEditorModelObject + * @description A model object used to handle Block Editor data. + **/ + function BlockEditorModelObject(propertyModelValue, propertyEditorAlias, blockConfigurations) { + + if (!propertyModelValue) { + throw new Error("propertyModelValue cannot be undefined, to ensure we keep the binding to the angular model we need minimum an empty object."); + } + + // ensure basic part of data-structure is in place: + this.value = propertyModelValue; + this.value.layout = this.value.layout || []; + this.value.data = this.value.data || []; + + this.propertyEditorAlias = propertyEditorAlias; + this.blockConfigurations = blockConfigurations; + + this.scaffolds = []; + + }; + + BlockEditorModelObject.prototype = { + + getBlockConfiguration: function(alias) { + return this.blockConfigurations.find(blockConfiguration => blockConfiguration.contentTypeAlias === alias); + }, + + loadScaffolds: function(contentResource) { + var tasks = []; + + var scaffoldAliases = []; + + this.blockConfigurations.forEach(blockConfiguration => { + scaffoldAliases.push(blockConfiguration.contentTypeAlias); + if (blockConfiguration.settingsElementTypeAlias != null) { + scaffoldAliases.push(elementType.settingsElementTypeAlias); + } + }); + + // remove dublicates. + scaffoldAliases = scaffoldAliases.filter((value, index, self) => self.indexOf(value) === index); + + scaffoldAliases.forEach((elementTypeAlias => { + tasks.push(contentResource.getScaffold(-20, elementTypeAlias).then(scaffold => { + console.log(scaffold); + this.scaffolds.push(scaffold); + })); + })); + + return Promise.all(tasks); + }, + + getAvailableBlocksForItemPicker: function() { + + var blocks = []; + + this.blockConfigurations.forEach(blockConfiguration => { + + var scaffold = this.getScaffoldFor(blockConfiguration.contentTypeAlias); + + blocks.push({ + alias: scaffold.contentTypeAlias, + name: scaffold.contentTypeName, + icon: scaffold.icon + }); + }); + + return blocks; + }, + + getScaffoldFor: function(contentTypeAlias, data) { + return this.scaffolds.find(o => o.contentTypeAlias === contentTypeAlias); + }, + + /** + * Retrieve editing model of a layout entry + * @return {Object} Scaffolded Block Content object. + */ + getEditingModel: function(layoutEntry) { + + var contentModel = this.getContentByUdi(layoutEntry.udi); + + var blockConfiguration = this.getBlockConfiguration(contentModel.contentTypeAlias); + + // TODO: make blockConfiguration the base for model, remeber to make a copy. + var model = { + label: "", + labelInterpolate: $interpolate(blockConfiguration.label), + editor: blockConfiguration.view, + overlaySize: "medium" + }; + + var scaffold = this.getScaffoldFor(blockConfiguration.contentTypeAlias); + if(scaffold === null) { + return null; + } + + model.content = angular.copy(scaffold); + applyModelToScaffold(model.content, contentModel); + + // TODO: settings + + return model; + + }, + + /** + * Retrieve layout data + * @return layout object. + */ + getLayout: function() { + if (!this.value.layout[this.propertyEditorAlias]) { + this.value.layout[this.propertyEditorAlias] = []; + } + return this.value.layout[this.propertyEditorAlias]; + }, + + /** + * Create layout entry + * @param {object} blockConfiguration + * @return layout entry, to be added in the layout. + */ + createLayoutEntry: function(contentTypeAlias) { + + var blockConfiguration = this.getBlockConfiguration(contentTypeAlias); + + var entry = { + udi: this.createContent(contentTypeAlias) + } + + if (blockConfiguration.settingsElementTypeAlias != null) { + // TODO: Settings. + } + + return entry; + }, + + // You make entries in your layout your self. + + getContentByUdi: function(udi) { + return this.value.data.find(entry => entry.udi === udi); + }, + + createContent: function(elementTypeAlias) { + var content = { + contentTypeAlias: elementTypeAlias, + udi: udiService.create("element") + }; + this.value.data.push(content); + return content.udi; + }, + + removeContent: function(entry) { + const index = this.value.data.indexOf(entry) + if (index > -1) { + this.value.splice(index, 1); + } + }, + + removeContentByUdi: function(udi) { + const index = this.value.data.findIndex(o => o.udi === udi); + if (index > -1) { + this.value.splice(index, 1); + } + } + } + + return { + createModelObject: function(propertyModelValue, propertyEditorAlias, blockConfigurations) { + return new BlockEditorModelObject(propertyModelValue, propertyEditorAlias, blockConfigurations); + }, + getBlockLabel: function(blockModelObject, labelIndex) { + + console.log("getBlockLabel", blockModelObject); + + // TODO: we should do something about this for performance. + + var vars = new Object(); + vars["$index"] = labelIndex; + + var variant = blockModelObject.content.variants[0]; + var tab = variant.tabs[0]; + // TODO: need to look up all tabs... + for(const property of tab.properties) { + vars[property.alias] = property.value; + } + + if(blockModelObject.labelInterpolate) { + return blockModelObject.labelInterpolate(vars); + } + + return blockModelObject.contentTypeName; + } + } + } + + angular.module('umbraco.services').service('blockEditorService', blockEditorService); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/udi.service.js b/src/Umbraco.Web.UI.Client/src/common/services/udi.service.js new file mode 100644 index 0000000000..b7d0cd05e8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/udi.service.js @@ -0,0 +1,32 @@ +(function () { + "use strict"; + + /** + * @ngdoc service + * @name umbraco.services.udiService + * @description A service for UDIs + **/ + function udiService() { + return { + + /** + * @ngdoc method + * @name umbraco.services.udiService#parse + * @methodOf umbraco.services.udiService + * @function + * + * @description + * Generates a Udi string. + * + * @param {string} entityType The entityType as a string. + * @returns {string} The generated UDI + */ + create: function(entityType) { + return "umb://" + entityType + "/" + String.CreateGuid(); + } + } + } + + angular.module("umbraco.services").factory("udiService", udiService); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html index ff555fc773..9561d28a9e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html @@ -1,7 +1,7 @@
    diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html index bd8f9558fd..419a1fd8a9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html @@ -1,4 +1,4 @@ diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js index caad1c83a7..868573fb72 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js @@ -5,7 +5,7 @@ angular.module("umbraco") var vm = this; - vm.firstProperty = $scope.block.content.tabs[0].properties[0]; + vm.firstProperty = $scope.block.content.variants[0].tabs[0].properties[0]; /* vm.onBlur = function() { if (vm.firstProperty.value === null || vm.firstProperty.value === "") { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.js index 34cfed2ad9..637df93a72 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.js @@ -4,7 +4,7 @@ angular .module('umbraco.directives') .component('umbElementContentEditor', { - templateUrl: 'views/common/infiniteeditors/elementeditor/elementeditor.component.html', + templateUrl: 'views/common/infiniteeditors/elementeditor/elementContentEditor.component.html', controller: ElementEditorComponentController, controllerAs: 'vm', bindings: { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index abfd738861..7aa03f9402 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -15,7 +15,7 @@ } }); - function BlockListController($scope, $interpolate, editorService, clipboardService, localizationService, overlayService) { + function BlockListController($scope, $interpolate, editorService, clipboardService, localizationService, overlayService, blockEditorService, contentResource) { var unsubscribe = []; var vm = this; @@ -24,13 +24,15 @@ $scope.moveFocusToBlock = null; + console.log("model JSON:", JSON.stringify(model)); + console.log("model:", model); console.log("config:", model.config); vm.validationLimit = model.config.validationLimit; console.log("value:", model.value); - + /* vm.availableBlockTypes = [ { alias: "pageModule", @@ -856,7 +858,34 @@ ]; + */ + + model.value = model.value || {}; + + var modelObject = blockEditorService.createModelObject(model.value, model.editor, model.config.blocks); + + modelObject.loadScaffolds(contentResource).then(loaded); + + vm.layout = []; + vm.blocks = []; + vm.availableBlockTypes = []; + + function loaded() { + + console.log("Loading done!!!"); + console.log(modelObject); + + + vm.layout = modelObject.getLayout(); + vm.layout.forEach(entry => { + vm.blocks.push(modelObject.getEditingModel(entry)); + }); + + vm.availableBlockTypes = modelObject.getAvailableBlocksForItemPicker(); + console.log(vm.availableBlockTypes); + + } function setDirty() { @@ -865,33 +894,23 @@ } }; - function addNewBlock(index, type) { + function addNewBlock(index, contentTypeAlias) { - var block = angular.copy(type.prototype_paste_data); + // Create layout entry. + var layoutEntry = modelObject.createLayoutEntry(contentTypeAlias); + // add layout entry a decired location in layout. + vm.layout.splice(index, 0, layoutEntry); - vm.blocks.splice(index, 0, block); - $scope.moveFocusToBlock = block; + // make editing object + var blockEditingObject = modelObject.getEditingModel(layoutEntry); + // apply editing model at decired location in editing model. + vm.blocks.splice(index, 0, blockEditingObject); + + $scope.moveFocusToBlock = blockEditingObject; } - function getBlockLabel(block) { - - // TODO: we should do something about this for performance. - - var props = new Object(); - - var tab = block.content.tabs[0]; - // TODO: need to look up all tabs... - for(const property of tab.properties) { - props[property.alias] = property.value; - } - - if(block.labelInterpolate) { - return block.labelInterpolate(props); - } - - return "block.label"; - } + vm.deleteBlock = function(block) { var index = vm.blocks.indexOf(block); @@ -942,7 +961,7 @@ availableItems: vm.availableBlockTypes, submit: function (model) { if (model && model.selectedItem) { - addNewBlock(createIndex, model.selectedItem); + addNewBlock(createIndex, model.selectedItem.alias); } vm.blockTypePicker.close(); }, @@ -1050,9 +1069,10 @@ // TODO: We need to investigate if we can do a specific watch on each block, so we dont re-render all blocks. unsubscribe.push($scope.$watch("vm.blocks", onBlocksUpdated, true)); - function onBlocksUpdated(newVal, oldVal){ + function onBlocksUpdated(newVal, oldVal) { + var labelIndex = 1; for(const block of vm.blocks) { - block.label = getBlockLabel(block); + block.label = blockEditorService.getBlockLabel(block, labelIndex++); } } unsubscribe.push($scope.$watch(() => vm.blocks.length, validateLimits)); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js index 970b42d2cb..77751579c9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js @@ -47,7 +47,7 @@ vm.requestRemoveEntryByIndex = function (index) { localizationService.localizeMany(["general_delete", "blockEditor_confirmDeleteBlockMessage", "blockEditor_confirmDeleteBlockNotice"]).then(function (data) { - var contentElementType = vm.getElementTypeByAlias($scope.model.value[index].elementTypeAlias); + var contentElementType = vm.getElementTypeByAlias($scope.model.value[index].contentTypeAlias); overlayService.confirmDelete({ title: data[0], content: localizationService.tokenReplace(data[1], [contentElementType.name]), @@ -78,7 +78,7 @@ vm.getAvailableElementTypes = function () { return vm.elementTypes.filter(function (type) { return !$scope.model.value.find(function (entry) { - return type.alias === entry.elementTypeAlias; + return type.alias === entry.contentTypeAlias; }); }); }; @@ -91,9 +91,9 @@ vm.openAddDialog = function ($event, entry) { - //we have to add the alias to the objects (they are stored as elementTypeAlias) + //we have to add the alias to the objects (they are stored as contentTypeAlias) var selectedItems = _.each($scope.model.value, function (obj) { - obj.alias = obj.elementTypeAlias; + obj.alias = obj.contentTypeAlias; return obj; }); @@ -149,7 +149,7 @@ vm.addEntryFromElementTypeAlias = function(alias) { var entry = { - "elementTypeAlias": alias, + "contentTypeAlias": alias, "view": null, "labelTemplate": "", "settingsElementTypeAlias": null diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html index 9ca35778be..8184761a1a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html @@ -24,16 +24,16 @@
    - {{ contentPreview = vm.getElementTypeByAlias(entry.elementTypeAlias); "" }} + {{ contentPreview = vm.getElementTypeByAlias(entry.contentTypeAlias); "" }}
    -
    - +
    diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index 6485bd061a..f069804348 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -29,7 +29,7 @@ namespace Umbraco.Web.PropertyEditors public class BlockConfiguration { // TODO: rename this to contentElementTypeAlias, I would like this to be specific, since we have the settings. - [JsonProperty("elementTypeAlias")] + [JsonProperty("contentTypeAlias")] public string Alias { get; set; } [JsonProperty("settingsElementTypeAlias")] @@ -38,8 +38,8 @@ namespace Umbraco.Web.PropertyEditors [JsonProperty("view")] public string View { get; set; } - [JsonProperty("labelTemplate")] - public string Template { get; set; } + [JsonProperty("label")] + public string Label { get; set; } } [ConfigurationField("useAccordionsAsDefault", "Inline editing mode", "boolean", Description = "Use the inline editor as the default block view")] From 28adc1b4f3b892e01b09cab5ee341812b5bb45ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 26 Feb 2020 14:47:52 +0100 Subject: [PATCH 049/508] renaming --- .../src/common/services/blockeditor.service.js | 4 ++-- .../views/propertyeditors/blocklist/blocklist.component.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index 8846dc7d3a..6ed5b27211 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -50,10 +50,10 @@ BlockEditorModelObject.prototype = { getBlockConfiguration: function(alias) { - return this.blockConfigurations.find(blockConfiguration => blockConfiguration.contentTypeAlias === alias); + return this.blockConfigurations.find(bc => bc.contentTypeAlias === alias); }, - loadScaffolds: function(contentResource) { + loadScaffolding: function(contentResource) { var tasks = []; var scaffoldAliases = []; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 7aa03f9402..1cb7a8d259 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -865,7 +865,7 @@ var modelObject = blockEditorService.createModelObject(model.value, model.editor, model.config.blocks); - modelObject.loadScaffolds(contentResource).then(loaded); + modelObject.loadScaffolding(contentResource).then(loaded); vm.layout = []; vm.blocks = []; From 3779ea80508ce2963f7f4f1d02ac83963abb7e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 26 Feb 2020 14:49:17 +0100 Subject: [PATCH 050/508] use Chrome Headless for unit tests --- src/Umbraco.Web.UI.Client/gulp/tasks/test.js | 16 +- src/Umbraco.Web.UI.Client/gulpfile.js | 4 +- src/Umbraco.Web.UI.Client/package-lock.json | 364 +++++++++++++----- src/Umbraco.Web.UI.Client/package.json | 5 +- .../test/config/karma.conf.js | 3 +- 5 files changed, 278 insertions(+), 114 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/test.js b/src/Umbraco.Web.UI.Client/gulp/tasks/test.js index 1e8d074f7e..b5239d35e7 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/test.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/test.js @@ -6,11 +6,23 @@ var karmaServer = require('karma').Server; * Build tests **************************/ - // Karma test +// Karma test function testUnit() { + return new karmaServer({ + configFile: __dirname + "/../../test/config/karma.conf.js" + }) + .start(); +}; + +// Run karma test server +function runUnitTestServer() { + return new karmaServer({ configFile: __dirname + "/../../test/config/karma.conf.js", + autoWatch: true, + port: 9999, + singleRun: false, keepalive: true }) .start(); @@ -24,4 +36,4 @@ function testE2e() { .start(); }; -module.exports = { testUnit: testUnit, testE2e: testE2e }; +module.exports = { testUnit: testUnit, testE2e: testE2e, runUnitTestServer: runUnitTestServer }; diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js index 705c54bf04..f6964df7c5 100644 --- a/src/Umbraco.Web.UI.Client/gulpfile.js +++ b/src/Umbraco.Web.UI.Client/gulpfile.js @@ -17,7 +17,7 @@ const { setDevelopmentMode } = require('./gulp/modes'); const { dependencies } = require('./gulp/tasks/dependencies'); const { js } = require('./gulp/tasks/js'); const { less } = require('./gulp/tasks/less'); -const { testE2e, testUnit } = require('./gulp/tasks/test'); +const { testE2e, testUnit, runUnitTestServer } = require('./gulp/tasks/test'); const { views } = require('./gulp/tasks/views'); const { watchTask } = require('./gulp/tasks/watchTask'); @@ -32,5 +32,5 @@ exports.dev = series(setDevelopmentMode, parallel(dependencies, js, less, views) exports.watch = series(watchTask); // exports.runTests = series(js, testUnit); -exports.testUnit = series(testUnit); +exports.runUnit = series(runUnitTestServer); exports.testE2e = series(testE2e); diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 42a89c5d13..be8174739a 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1806,7 +1806,8 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true + "dev": true, + "optional": true }, "base64id": { "version": "1.0.0", @@ -2053,6 +2054,7 @@ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", "dev": true, + "optional": true, "requires": { "p-finally": "^1.0.0" } @@ -2094,6 +2096,7 @@ "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "dev": true, + "optional": true, "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -2103,13 +2106,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2125,6 +2130,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -2259,6 +2265,7 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", "dev": true, + "optional": true, "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -2284,7 +2291,8 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true + "dev": true, + "optional": true }, "buffer-equal": { "version": "1.0.0", @@ -2475,6 +2483,7 @@ "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", "integrity": "sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==", "dev": true, + "optional": true, "requires": { "get-proxy": "^2.0.0", "isurl": "^1.0.0-alpha5", @@ -2908,6 +2917,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "dev": true, + "optional": true, "requires": { "graceful-readlink": ">= 1.0.0" } @@ -3002,6 +3012,7 @@ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", "dev": true, + "optional": true, "requires": { "ini": "^1.3.4", "proto-list": "~1.2.1" @@ -3057,6 +3068,7 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", "dev": true, + "optional": true, "requires": { "safe-buffer": "5.1.2" } @@ -3460,6 +3472,7 @@ "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz", "integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=", "dev": true, + "optional": true, "requires": { "decompress-tar": "^4.0.0", "decompress-tarbz2": "^4.0.0", @@ -3476,6 +3489,7 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, + "optional": true, "requires": { "pify": "^3.0.0" }, @@ -3484,7 +3498,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true + "dev": true, + "optional": true } } } @@ -3495,6 +3510,7 @@ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "dev": true, + "optional": true, "requires": { "mimic-response": "^1.0.0" } @@ -3504,6 +3520,7 @@ "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", "dev": true, + "optional": true, "requires": { "file-type": "^5.2.0", "is-stream": "^1.1.0", @@ -3514,7 +3531,8 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", - "dev": true + "dev": true, + "optional": true } } }, @@ -3523,6 +3541,7 @@ "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", "dev": true, + "optional": true, "requires": { "decompress-tar": "^4.1.0", "file-type": "^6.1.0", @@ -3535,7 +3554,8 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", - "dev": true + "dev": true, + "optional": true } } }, @@ -3544,6 +3564,7 @@ "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", "dev": true, + "optional": true, "requires": { "decompress-tar": "^4.1.1", "file-type": "^5.2.0", @@ -3554,7 +3575,8 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", - "dev": true + "dev": true, + "optional": true } } }, @@ -3563,6 +3585,7 @@ "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", "dev": true, + "optional": true, "requires": { "file-type": "^3.8.0", "get-stream": "^2.2.0", @@ -3574,13 +3597,15 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", - "dev": true + "dev": true, + "optional": true }, "get-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", "dev": true, + "optional": true, "requires": { "object-assign": "^4.0.1", "pinkie-promise": "^2.0.0" @@ -3590,7 +3615,8 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "dev": true, + "optional": true } } }, @@ -3855,7 +3881,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true + "dev": true, + "optional": true } } }, @@ -3872,7 +3899,8 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true + "dev": true, + "optional": true }, "duplexify": { "version": "3.7.1", @@ -4475,6 +4503,7 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, + "optional": true, "requires": { "cross-spawn": "^5.0.1", "get-stream": "^3.0.0", @@ -4490,6 +4519,7 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, + "optional": true, "requires": { "lru-cache": "^4.0.1", "shebang-command": "^1.2.0", @@ -4629,6 +4659,7 @@ "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", "dev": true, + "optional": true, "requires": { "mime-db": "^1.28.0" } @@ -4638,6 +4669,7 @@ "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", "dev": true, + "optional": true, "requires": { "ext-list": "^2.0.0", "sort-keys-length": "^1.0.0" @@ -4919,6 +4951,7 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, + "optional": true, "requires": { "pend": "~1.2.0" } @@ -4957,13 +4990,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", - "dev": true + "dev": true, + "optional": true }, "filenamify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz", "integrity": "sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==", "dev": true, + "optional": true, "requires": { "filename-reserved-regex": "^2.0.0", "strip-outer": "^1.0.0", @@ -5313,7 +5348,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", - "dev": true + "dev": true, + "optional": true }, "fs-extra": { "version": "1.0.0", @@ -5379,7 +5415,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5400,12 +5437,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5420,17 +5459,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5547,7 +5589,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5559,6 +5602,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5573,6 +5617,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5580,12 +5625,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5604,6 +5651,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5684,7 +5732,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5696,6 +5745,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5781,7 +5831,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -5817,6 +5868,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5836,6 +5888,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5879,12 +5932,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -5911,6 +5966,7 @@ "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", "integrity": "sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==", "dev": true, + "optional": true, "requires": { "npm-conf": "^1.1.0" } @@ -5919,13 +5975,15 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true + "dev": true, + "optional": true }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true + "dev": true, + "optional": true }, "get-value": { "version": "2.0.6", @@ -6240,7 +6298,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true + "dev": true, + "optional": true }, "growly": { "version": "1.3.0", @@ -7059,7 +7118,8 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", - "dev": true + "dev": true, + "optional": true }, "has-symbols": { "version": "1.0.0", @@ -7072,6 +7132,7 @@ "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", "dev": true, + "optional": true, "requires": { "has-symbol-support-x": "^1.4.1" } @@ -7272,7 +7333,8 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true + "dev": true, + "optional": true }, "ignore": { "version": "4.0.6", @@ -7402,6 +7464,7 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, + "optional": true, "requires": { "repeating": "^2.0.0" } @@ -7723,6 +7786,7 @@ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7775,7 +7839,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=", - "dev": true + "dev": true, + "optional": true }, "is-negated-glob": { "version": "1.0.0", @@ -7813,13 +7878,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", - "dev": true + "dev": true, + "optional": true }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true + "dev": true, + "optional": true }, "is-plain-object": { "version": "2.0.4", @@ -7883,7 +7950,8 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true + "dev": true, + "optional": true }, "is-stream": { "version": "1.1.0", @@ -7986,6 +8054,7 @@ "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", "dev": true, + "optional": true, "requires": { "has-to-string-tag-x": "^1.2.0", "is-object": "^1.0.1" @@ -8308,6 +8377,15 @@ } } }, + "karma-chrome-launcher": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", + "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", + "dev": true, + "requires": { + "which": "^1.2.1" + } + }, "karma-jasmine": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-2.0.1.tgz", @@ -8803,7 +8881,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", - "dev": true + "dev": true, + "optional": true }, "lpad-align": { "version": "1.1.2", @@ -8873,7 +8952,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true + "dev": true, + "optional": true }, "map-visit": { "version": "1.0.0", @@ -9029,7 +9109,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true + "dev": true, + "optional": true }, "minimatch": { "version": "3.0.4", @@ -9266,9 +9347,9 @@ } }, "npm": { - "version": "6.13.6", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.13.6.tgz", - "integrity": "sha512-NomC08kv7HIl1FOyLOe9Hp89kYsOsvx52huVIJ7i8hFW8Xp65lDwe/8wTIrh9q9SaQhA8hTrfXPh3BEL3TmMpw==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.0.tgz", + "integrity": "sha512-OgfdLadz7j6dikbpaimmLzMxwLKbXthQXHiJwtegorwtBVnhecfUeYkHopwd5ICaiClQnqlYQCHERXDiYK3Jcw==", "requires": { "JSONStream": "^1.3.5", "abbrev": "~1.1.1", @@ -9276,12 +9357,12 @@ "ansistyles": "~0.1.3", "aproba": "^2.0.0", "archy": "~1.0.0", - "bin-links": "^1.1.6", + "bin-links": "^1.1.7", "bluebird": "^3.5.5", "byte-size": "^5.0.1", "cacache": "^12.0.3", "call-limit": "^1.1.1", - "chownr": "^1.1.3", + "chownr": "^1.1.4", "ci-info": "^2.0.0", "cli-columns": "^3.1.2", "cli-table3": "^0.5.1", @@ -9301,7 +9382,7 @@ "glob": "^7.1.4", "graceful-fs": "^4.2.3", "has-unicode": "~2.0.1", - "hosted-git-info": "^2.8.5", + "hosted-git-info": "^2.8.6", "iferr": "^1.0.2", "imurmurhash": "*", "infer-owner": "^1.0.4", @@ -9319,7 +9400,7 @@ "libnpmorg": "^1.0.1", "libnpmsearch": "^2.0.2", "libnpmteam": "^1.0.2", - "libnpx": "^10.2.0", + "libnpx": "^10.2.2", "lock-verify": "^2.1.0", "lockfile": "^1.0.4", "lodash._baseindexof": "*", @@ -9338,7 +9419,7 @@ "mississippi": "^3.0.0", "mkdirp": "~0.5.1", "move-concurrently": "^1.0.1", - "node-gyp": "^5.0.5", + "node-gyp": "^5.0.7", "nopt": "~4.0.1", "normalize-package-data": "^2.5.0", "npm-audit-report": "^1.3.2", @@ -9346,10 +9427,10 @@ "npm-install-checks": "^3.0.2", "npm-lifecycle": "^3.1.4", "npm-package-arg": "^6.1.1", - "npm-packlist": "^1.4.7", + "npm-packlist": "^1.4.8", "npm-pick-manifest": "^3.0.2", "npm-profile": "^4.0.2", - "npm-registry-fetch": "^4.0.2", + "npm-registry-fetch": "^4.0.3", "npm-user-validate": "~1.0.0", "npmlog": "~4.1.2", "once": "~1.4.0", @@ -9366,7 +9447,7 @@ "read-installed": "~4.0.3", "read-package-json": "^2.1.1", "read-package-tree": "^5.3.1", - "readable-stream": "^3.4.0", + "readable-stream": "^3.6.0", "readdir-scoped-modules": "^1.1.0", "request": "^2.88.0", "retry": "^0.12.0", @@ -9535,7 +9616,7 @@ } }, "bin-links": { - "version": "1.1.6", + "version": "1.1.7", "bundled": true, "requires": { "bluebird": "^3.5.3", @@ -9634,7 +9715,7 @@ } }, "chownr": { - "version": "1.1.3", + "version": "1.1.4", "bundled": true }, "ci-info": { @@ -10015,7 +10096,7 @@ } }, "env-paths": { - "version": "1.0.0", + "version": "2.2.0", "bundled": true }, "err-code": { @@ -10318,7 +10399,7 @@ } }, "get-caller-file": { - "version": "1.0.2", + "version": "1.0.3", "bundled": true }, "get-stream": { @@ -10413,7 +10494,7 @@ "bundled": true }, "hosted-git-info": { - "version": "2.8.5", + "version": "2.8.6", "bundled": true }, "http-cache-semantics": { @@ -10513,7 +10594,7 @@ } }, "invert-kv": { - "version": "1.0.0", + "version": "2.0.0", "bundled": true }, "ip": { @@ -10671,10 +10752,10 @@ "bundled": true }, "lcid": { - "version": "1.0.0", + "version": "2.0.0", "bundled": true, "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "^2.0.0" } }, "libcipm": { @@ -10833,7 +10914,7 @@ } }, "libnpx": { - "version": "10.2.0", + "version": "10.2.2", "bundled": true, "requires": { "dotenv": "^5.0.1", @@ -10963,15 +11044,30 @@ "ssri": "^6.0.0" } }, + "map-age-cleaner": { + "version": "0.1.3", + "bundled": true, + "requires": { + "p-defer": "^1.0.0" + } + }, "meant": { "version": "1.0.1", "bundled": true }, "mem": { - "version": "1.1.0", + "version": "4.3.0", "bundled": true, "requires": { - "mimic-fn": "^1.0.0" + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "bundled": true + } } }, "mime-db": { @@ -10985,10 +11081,6 @@ "mime-db": "~1.35.0" } }, - "mimic-fn": { - "version": "1.2.0", - "bundled": true - }, "minimatch": { "version": "3.0.4", "bundled": true, @@ -11066,6 +11158,10 @@ "version": "0.0.7", "bundled": true }, + "nice-try": { + "version": "1.0.5", + "bundled": true + }, "node-fetch-npm": { "version": "2.0.2", "bundled": true, @@ -11076,33 +11172,20 @@ } }, "node-gyp": { - "version": "5.0.5", + "version": "5.0.7", "bundled": true, "requires": { - "env-paths": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "npmlog": "^4.1.2", + "request": "^2.88.0", + "rimraf": "^2.6.3", + "semver": "^5.7.1", "tar": "^4.4.12", - "which": "1" - }, - "dependencies": { - "nopt": { - "version": "3.0.6", - "bundled": true, - "requires": { - "abbrev": "1" - } - }, - "semver": { - "version": "5.3.0", - "bundled": true - } + "which": "^1.3.1" } }, "nopt": { @@ -11191,11 +11274,12 @@ } }, "npm-packlist": { - "version": "1.4.7", + "version": "1.4.8", "bundled": true, "requires": { "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" } }, "npm-pick-manifest": { @@ -11217,7 +11301,7 @@ } }, "npm-registry-fetch": { - "version": "4.0.2", + "version": "4.0.3", "bundled": true, "requires": { "JSONStream": "^1.3.4", @@ -11296,12 +11380,38 @@ "bundled": true }, "os-locale": { - "version": "2.1.0", + "version": "3.1.0", "bundled": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "bundled": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "bundled": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + } } }, "os-tmpdir": { @@ -11316,10 +11426,18 @@ "os-tmpdir": "^1.0.0" } }, + "p-defer": { + "version": "1.0.0", + "bundled": true + }, "p-finally": { "version": "1.0.0", "bundled": true }, + "p-is-promise": { + "version": "2.1.0", + "bundled": true + }, "p-limit": { "version": "1.2.0", "bundled": true, @@ -11625,7 +11743,7 @@ } }, "readable-stream": { - "version": "3.4.0", + "version": "3.6.0", "bundled": true, "requires": { "inherits": "^2.0.3", @@ -11960,10 +12078,16 @@ } }, "string_decoder": { - "version": "1.2.0", + "version": "1.3.0", "bundled": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "bundled": true + } } }, "stringify-package": { @@ -12294,14 +12418,14 @@ "bundled": true }, "yargs": { - "version": "11.0.0", + "version": "11.1.1", "bundled": true, "requires": { "cliui": "^4.0.0", "decamelize": "^1.1.1", "find-up": "^2.1.0", "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", + "os-locale": "^3.1.0", "require-directory": "^2.1.1", "require-main-filename": "^1.0.1", "set-blocking": "^2.0.0", @@ -12331,6 +12455,7 @@ "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", "dev": true, + "optional": true, "requires": { "config-chain": "^1.1.11", "pify": "^3.0.0" @@ -12340,7 +12465,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true + "dev": true, + "optional": true } } }, @@ -12349,6 +12475,7 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, + "optional": true, "requires": { "path-key": "^2.0.0" } @@ -12704,7 +12831,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "dev": true, + "optional": true }, "p-is-promise": { "version": "1.1.0", @@ -12741,6 +12869,7 @@ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", "dev": true, + "optional": true, "requires": { "p-finally": "^1.0.0" } @@ -13464,7 +13593,8 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", - "dev": true + "dev": true, + "optional": true }, "prr": { "version": "1.0.1", @@ -13820,6 +13950,7 @@ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, + "optional": true, "requires": { "is-finite": "^1.0.0" } @@ -14159,6 +14290,7 @@ "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", "dev": true, + "optional": true, "requires": { "commander": "~2.8.1" } @@ -14561,6 +14693,7 @@ "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", "dev": true, + "optional": true, "requires": { "is-plain-obj": "^1.0.0" } @@ -14570,6 +14703,7 @@ "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=", "dev": true, + "optional": true, "requires": { "sort-keys": "^1.0.0" } @@ -14894,6 +15028,7 @@ "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", "dev": true, + "optional": true, "requires": { "is-natural-number": "^4.0.1" } @@ -14902,7 +15037,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true + "dev": true, + "optional": true }, "strip-indent": { "version": "1.0.1", @@ -14925,6 +15061,7 @@ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", "dev": true, + "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15044,6 +15181,7 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", "dev": true, + "optional": true, "requires": { "bl": "^1.0.0", "buffer-alloc": "^1.2.0", @@ -15058,13 +15196,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -15080,6 +15220,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -15090,13 +15231,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", - "dev": true + "dev": true, + "optional": true }, "tempfile": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", "integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=", "dev": true, + "optional": true, "requires": { "temp-dir": "^1.0.0", "uuid": "^3.0.1" @@ -15197,7 +15340,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true + "dev": true, + "optional": true }, "timers-ext": { "version": "0.1.7", @@ -15254,7 +15398,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", - "dev": true + "dev": true, + "optional": true }, "to-fast-properties": { "version": "2.0.0", @@ -15349,6 +15494,7 @@ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", "dev": true, + "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15370,6 +15516,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.0.1" } @@ -15485,6 +15632,7 @@ "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", "dev": true, + "optional": true, "requires": { "buffer": "^5.2.1", "through": "^2.3.8" @@ -15685,7 +15833,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", - "dev": true + "dev": true, + "optional": true }, "use": { "version": "3.1.1", @@ -16104,6 +16253,7 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, + "optional": true, "requires": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index c150af79de..a956b5d803 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -2,7 +2,7 @@ "private": true, "scripts": { "test": "gulp runTests", - "unit": "gulp testUnit", + "unit": "gulp runUnit", "e2e": "gulp testE2e", "build": "gulp build", "dev": "gulp dev", @@ -39,7 +39,7 @@ "moment": "2.22.2", "ng-file-upload": "12.2.13", "nouislider": "14.1.1", - "npm": "6.13.6", + "npm": "^6.14.0", "signalr": "2.4.0", "spectrum-colorpicker": "1.8.0", "tinymce": "4.9.7", @@ -72,6 +72,7 @@ "gulp-wrap-js": "0.4.1", "jasmine-core": "3.5.0", "karma": "4.4.1", + "karma-chrome-launcher": "^3.1.0", "karma-jasmine": "2.0.1", "karma-junit-reporter": "2.0.1", "karma-phantomjs-launcher": "1.0.4", diff --git a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js index 4e3a78144b..8a120b3165 100644 --- a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js +++ b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js @@ -98,7 +98,7 @@ module.exports = function (config) { // - PhantomJS // - IE (only Windows) // CLI --browsers Chrome,Firefox,Safari - browsers: ['PhantomJS'], + browsers: ['ChromeHeadless'], // allow waiting a bit longer, some machines require this @@ -115,6 +115,7 @@ module.exports = function (config) { plugins: [ require('karma-jasmine'), require('karma-phantomjs-launcher'), + require('karma-chrome-launcher'), require('karma-junit-reporter'), require('karma-spec-reporter') From 4a6484b577dc91d03f232f26afb4aa9e3af2c139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 26 Feb 2020 17:03:06 +0100 Subject: [PATCH 051/508] test for blockEditorService --- src/Umbraco.Web.UI.Client/gulpfile.js | 2 +- src/Umbraco.Web.UI.Client/package-lock.json | 6 + src/Umbraco.Web.UI.Client/package.json | 1 + .../src/common/mocks/resources/_utils.js | 168 ++++++++++++++++++ .../mocks/resources/variantcontent.mocks.js | 56 ++++++ .../test/config/karma.conf.js | 3 + .../common/filters/truncate-filters.spec.js | 1 - .../services/block-editor-service.spec.js | 66 +++++++ 8 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/mocks/resources/variantcontent.mocks.js create mode 100644 src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js index f6964df7c5..ec9b7bc508 100644 --- a/src/Umbraco.Web.UI.Client/gulpfile.js +++ b/src/Umbraco.Web.UI.Client/gulpfile.js @@ -32,5 +32,5 @@ exports.dev = series(setDevelopmentMode, parallel(dependencies, js, less, views) exports.watch = series(watchTask); // exports.runTests = series(js, testUnit); -exports.runUnit = series(runUnitTestServer); +exports.runUnit = series(js, runUnitTestServer, watchTask); exports.testE2e = series(testE2e); diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index be8174739a..5bab6a2e22 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -8066,6 +8066,12 @@ "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", "dev": true }, + "jasmine-promise-matchers": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/jasmine-promise-matchers/-/jasmine-promise-matchers-2.6.0.tgz", + "integrity": "sha1-J1ASqFEeXoh9g11TWKutIMAmz2M=", + "dev": true + }, "jpegtran-bin": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jpegtran-bin/-/jpegtran-bin-4.0.0.tgz", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index a956b5d803..e7a6cce6a6 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -71,6 +71,7 @@ "gulp-wrap": "0.15.0", "gulp-wrap-js": "0.4.1", "jasmine-core": "3.5.0", + "jasmine-promise-matchers": "^2.6.0", "karma": "4.4.1", "karma-chrome-launcher": "^3.1.0", "karma-jasmine": "2.0.1", diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js index 6e6eb00da7..cf73e6a8ce 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js @@ -280,6 +280,174 @@ angular.module('umbraco.mocks'). return node; }, + + /** Creats a mock variant content object */ + getMockVariantContent: function(id) { + var node = { + name: "My content with id: " + id, + updateDate: new Date().toIsoDateTimeString(), + publishDate: new Date().toIsoDateTimeString(), + createDate: new Date().toIsoDateTimeString(), + id: id, + parentId: 1234, + icon: "icon-umb-content", + owner: { name: "Administrator", id: 0 }, + updater: { name: "Per Ploug Krogslund", id: 1 }, + path: "-1,1234,2455", + allowedActions: ["U", "H", "A"], + variants: [ + { + name: "", + language: null, + segment: null, + state: "NotCreated", + updateDate: "0001-01-01 00:00:00", + createDate: "0001-01-01 00:00:00", + publishDate: null, + releaseDate: null, + expireDate: null, + notifications: [], + tabs: [ + { + label: "Content", + id: 2, + properties: [ + { alias: "valTest", label: "Validation test", view: "validationtest", value: "asdfasdf" }, + { alias: "bodyText", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, + { alias: "textarea", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, + { alias: "media", label: "Media picker", view: "mediapicker", value: "1234,23242,23232,23231", config: {multiPicker: 1} } + ] + }, + { + label: "Sample Editor", + id: 3, + properties: [ + { alias: "datepicker", label: "Datepicker", view: "datepicker", config: { pickTime: false, format: "yyyy-MM-dd" } }, + { alias: "tags", label: "Tags", view: "tags", value: "" } + ] + }, + { + label: "This", + id: 4, + properties: [ + { alias: "valTest4", label: "Validation test", view: "validationtest", value: "asdfasdf" }, + { alias: "bodyText4", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, + { alias: "textarea4", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, + { alias: "content4", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } + ] + }, + { + label: "Is", + id: 5, + properties: [ + { alias: "valTest5", label: "Validation test", view: "validationtest", value: "asdfasdf" }, + { alias: "bodyText5", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, + { alias: "textarea5", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, + { alias: "content5", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } + ] + }, + { + label: "Overflown", + id: 6, + properties: [ + { alias: "valTest6", label: "Validation test", view: "validationtest", value: "asdfasdf" }, + { alias: "bodyText6", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, + { alias: "textarea6", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, + { alias: "content6", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } + ] + }, + { + label: "Generic Properties", + id: 0, + properties: [ + { + label: 'Id', + value: 1234, + view: "readonlyvalue", + alias: "_umb_id" + }, + { + label: 'Created by', + description: 'Original author', + value: "Administrator", + view: "readonlyvalue", + alias: "_umb_createdby" + }, + { + label: 'Created', + description: 'Date/time this document was created', + value: new Date().toIsoDateTimeString(), + view: "readonlyvalue", + alias: "_umb_createdate" + }, + { + label: 'Updated', + description: 'Date/time this document was created', + value: new Date().toIsoDateTimeString(), + view: "readonlyvalue", + alias: "_umb_updatedate" + }, + { + label: 'Document Type', + value: "Home page", + view: "readonlyvalue", + alias: "_umb_doctype" + }, + { + label: 'Publish at', + description: 'Date/time to publish this document', + value: new Date().toIsoDateTimeString(), + view: "datepicker", + alias: "_umb_releasedate" + }, + { + label: 'Unpublish at', + description: 'Date/time to un-publish this document', + value: new Date().toIsoDateTimeString(), + view: "datepicker", + alias: "_umb_expiredate" + }, + { + label: 'Template', + value: "myTemplate", + view: "dropdown", + alias: "_umb_template", + config: { + items: { + "" : "-- Choose template --", + "myTemplate" : "My Templates", + "home" : "Home Page", + "news" : "News Page" + } + } + }, + { + label: 'Link to document', + value: ["/testing" + id, "http://localhost/testing" + id, "http://mydomain.com/testing" + id].join(), + view: "urllist", + alias: "_umb_urllist" + }, + { + alias: "test", label: "Stuff", view: "test", value: "", + config: { + fields: [ + { alias: "embedded", label: "Embbeded", view: "textstring", value: "" }, + { alias: "embedded2", label: "Embbeded 2", view: "contentpicker", value: "" }, + { alias: "embedded3", label: "Embbeded 3", view: "textarea", value: "" }, + { alias: "embedded4", label: "Embbeded 4", view: "datepicker", value: "" } + ] + } + } + ] + } + ] + } + ] + }; + + return node; + }, + getMockEntity : function(id){ return {name: "hello", id: id, icon: "icon-file"}; }, diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/variantcontent.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/variantcontent.mocks.js new file mode 100644 index 0000000000..1ff2d3a0a7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/variantcontent.mocks.js @@ -0,0 +1,56 @@ +angular.module('umbraco.mocks'). + factory('variantContentMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { + 'use strict'; + + function returnEmptyVariantNode(status, data, headers) { + + if (!mocksUtils.checkAuth()) { + return [401, null, null]; + } + + var response = returnVariantNodebyId(200, "", null); + var node = response[1]; + var parentId = mocksUtils.getParameterByName(data, "parentId") || 1234; + + node.name = ""; + node.id = 0; + node.parentId = parentId; + + $(node.tabs).each(function(i,tab){ + $(tab.properties).each(function(i, property){ + property.value = ""; + }); + }); + + return response; + } + + function returnVariantNodebyId(status, data, headers) { + + if (!mocksUtils.checkAuth()) { + return [401, null, null]; + } + + var id = mocksUtils.getParameterByName(data, "id") || "1234"; + id = parseInt(id, 10); + + var node = mocksUtils.getMockVariantContent(id); + + return [200, node, null]; + } + + + return { + register: function () { + + $httpBackend + .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Content/GetById?')) + .respond(returnVariantNodebyId); + + $httpBackend + .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Content/GetEmpty')) + .respond(returnEmptyVariantNode); + + } + }; +}]); diff --git a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js index 8a120b3165..501e43b043 100644 --- a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js +++ b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js @@ -10,6 +10,9 @@ module.exports = function (config) { // list of files / patterns to load in the browser files: [ + // Jasmine plugins + 'node_modules/jasmine-promise-matchers/dist/jasmine-promise-matchers.js', + //libraries 'node_modules/jquery/dist/jquery.min.js', 'node_modules/angular/angular.js', diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/filters/truncate-filters.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/filters/truncate-filters.spec.js index 1e9ea2ea46..64a984b499 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/filters/truncate-filters.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/filters/truncate-filters.spec.js @@ -53,7 +53,6 @@ testCases.forEach(function(test){ it('Expects \'' + test.input + '\' to be truncated as \''+ test.expectedResult + '\', when noOfChars=' + test.noOfChars + ', and appendDots=' + test.appendDots, function() { - console.log($truncate(test.input, test.noOfChars, test.appendDots)); expect($truncate(test.input, test.noOfChars, test.appendDots)).toBe(test.expectedResult); }); }); diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js new file mode 100644 index 0000000000..5c48b4a8dc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js @@ -0,0 +1,66 @@ +describe('blockEditorService tests', function () { + + var blockEditorService, $rootScope, $httpBackend, varaintMocks, contentResource; + + beforeEach(module('umbraco.services')); + beforeEach(module('umbraco.mocks')); + + beforeEach(inject(function ($injector, mocksUtils) { + + mocksUtils.disableAuth(); + + blockEditorService = $injector.get('blockEditorService'); + $rootScope = $injector.get('$rootScope'); + $httpBackend = $injector.get('$httpBackend'); + varaintMocks = $injector.get("variantContentMocks"); + varaintMocks.register(); + contentResource = $injector.get('contentResource'); + + })); + + + var simpleBlockConfigurationMock = {contentTypeAlias: "testAlias", label:"Test", settingsElementTypeAlias: null, view: "testview.html"}; + + + describe('init blockEditoModelObject', function () { + + it('fail if no model value', function () { + function createWithNoModelValue() { + blockEditorService.createModelObject(null, "test", []); + } + expect(createWithNoModelValue).toThrow(); + }); + + it('return a object, with methods', function () { + var modelObject = blockEditorService.createModelObject({}, "test", []); + + expect(modelObject).not.toBeUndefined(); + expect(modelObject.loadScaffolding).not.toBeUndefined(); + }); + + it('getBlockConfiguration provide the requested block configurtion', function () { + var modelObject = blockEditorService.createModelObject({}, "test", [simpleBlockConfigurationMock]); + + expect(modelObject.getBlockConfiguration(simpleBlockConfigurationMock.contentTypeAlias).label).toBe(simpleBlockConfigurationMock.label); + }); + + it('loadScaffolding provides data for itemPicker', function () { + var modelObject = blockEditorService.createModelObject({}, "test", [simpleBlockConfigurationMock]); + + var itemPickerOptions; + + var pendingPromise = modelObject.loadScaffolding(contentResource).then(() => { + itemPickerOptions = modelObject.getAvailableBlocksForItemPicker(); + }); + + $rootScope.$digest(); + $httpBackend.flush(); + + expect(itemPickerOptions.length).toBe(1); + + + }); + + }); + +}); From e30d90ac7e84fa1f88e153a8db0c66e6d1295e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 26 Feb 2020 17:03:24 +0100 Subject: [PATCH 052/508] safer code --- .../src/common/services/blockeditor.service.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index 6ed5b27211..a4f7ca9e7b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -85,12 +85,13 @@ this.blockConfigurations.forEach(blockConfiguration => { var scaffold = this.getScaffoldFor(blockConfiguration.contentTypeAlias); - - blocks.push({ - alias: scaffold.contentTypeAlias, - name: scaffold.contentTypeName, - icon: scaffold.icon - }); + if(scaffold) { + blocks.push({ + alias: scaffold.contentTypeAlias, + name: scaffold.contentTypeName, + icon: scaffold.icon + }); + } }); return blocks; @@ -110,6 +111,11 @@ var blockConfiguration = this.getBlockConfiguration(contentModel.contentTypeAlias); + if (blockConfiguration === null) { + // This is not an allowed block type, therefor we return null; + return null; + } + // TODO: make blockConfiguration the base for model, remeber to make a copy. var model = { label: "", From f43c0338f9fc43239e258278db969370bee19ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 26 Feb 2020 17:03:48 +0100 Subject: [PATCH 053/508] block list editor --- .../blocklist/blocklist.component.js | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 1cb7a8d259..5e450eb133 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -879,7 +879,10 @@ vm.layout = modelObject.getLayout(); vm.layout.forEach(entry => { - vm.blocks.push(modelObject.getEditingModel(entry)); + var block = modelObject.getEditingModel(entry); + if(block !== null) { + vm.blocks.push(block); + } }); vm.availableBlockTypes = modelObject.getAvailableBlocksForItemPicker(); @@ -916,13 +919,20 @@ var index = vm.blocks.indexOf(block); if(index !== -1) { vm.blocks.splice(index, 1); - } - if(vm.quickMenuIndex > index) { - vm.quickMenuIndex--; + + var layoutIndex = this.layout.findIndex(entry => entry.udi === block.udi); + if(layoutIndex !== -1) { + vm.layout.splice(layoutIndex, 1); + } + + this.modelObject.removeContentByUdi(block.udi); } } vm.editBlock = function(blockModel) { + + // TODO: test wether i need to clone or if that is done by overlay. + //var blockModelClone = angular.copy(blockModel); var elementEditor = { block: blockModel, @@ -930,6 +940,7 @@ size: blockModel.overlaySize, submit: function(model) { blockModel.content = model.block.content; + blockModel.settings = model.block.settings; editorService.close(); }, close: function() { @@ -1025,7 +1036,7 @@ }; $scope.blockApi = { - removeBlock: vm.removeBlock + deleteBlock: vm.deleteBlock } From 5b910c178a4f193d190367c4f1da3402aa8c4d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Mar 2020 11:12:04 +0100 Subject: [PATCH 054/508] rename view to overlayView --- .../blocklist/blocklist.component.js | 2 +- .../blocklist.elementtypepicker.controller.js | 13 +++++-------- .../prevalue/blocklist.elementtypepicker.html | 6 +++--- .../PropertyEditors/BlockListConfiguration.cs | 4 ++-- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 5e450eb133..eefe339569 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -936,7 +936,7 @@ var elementEditor = { block: blockModel, - view: "views/common/infiniteeditors/elementeditor/elementeditor.html", + view: blockModel.overlayView || "views/common/infiniteeditors/elementeditor/elementeditor.html", size: blockModel.overlaySize, submit: function(model) { blockModel.content = model.block.content; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js index 77751579c9..1f76afef8e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js @@ -150,7 +150,7 @@ var entry = { "contentTypeAlias": alias, - "view": null, + "overlayView": null, "labelTemplate": "", "settingsElementTypeAlias": null }; @@ -233,7 +233,7 @@ localizationService.localizeMany(["general_remove", "defaultdialogs_confirmremoveusageof"]).then(function (data) { overlayService.confirmRemove({ title: data[0], - content: localizationService.tokenReplace(data[1], [entry.view]), + content: localizationService.tokenReplace(data[1], [entry.overlayView]), close: function () { overlayService.close(); }, @@ -245,7 +245,7 @@ }); }; vm.removeViewForEntry = function(entry) { - entry.view = null; + entry.overlayView = null; }; vm.addViewForEntry = function(entry) { const filePicker = { @@ -255,13 +255,10 @@ entityType: "file", isDialog: true, filter: function (i) { - if (i.name.indexOf(".html") !== -1) { - return true; - } + return (i.name.indexOf(".html") !== -1); }, select: function (file) { - console.log(file); - entry.view = file.name; + entry.overlayView = file.name; editorService.close(); }, close: function () { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html index 8184761a1a..75a59a0f2d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html @@ -36,16 +36,16 @@
    -
    +
    - +
    -
    diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index f069804348..f76612db59 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -35,8 +35,8 @@ namespace Umbraco.Web.PropertyEditors [JsonProperty("settingsElementTypeAlias")] public string SettingsElementTypeAlias { get; set; } - [JsonProperty("view")] - public string View { get; set; } + [JsonProperty("overlayView")] + public string OverlayView { get; set; } [JsonProperty("label")] public string Label { get; set; } From c72a36c373802bff6a8daa2c8f9b67f2597d950e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Mar 2020 11:12:17 +0100 Subject: [PATCH 055/508] block editor work --- .../common/services/blockeditor.service.js | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index a4f7ca9e7b..2e761fa9e3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -37,7 +37,7 @@ // ensure basic part of data-structure is in place: this.value = propertyModelValue; - this.value.layout = this.value.layout || []; + this.value.layout = this.value.layout || {}; this.value.data = this.value.data || []; this.propertyEditorAlias = propertyEditorAlias; @@ -97,7 +97,7 @@ return blocks; }, - getScaffoldFor: function(contentTypeAlias, data) { + getScaffoldFor: function(contentTypeAlias) { return this.scaffolds.find(o => o.contentTypeAlias === contentTypeAlias); }, @@ -116,14 +116,10 @@ return null; } - // TODO: make blockConfiguration the base for model, remeber to make a copy. - var model = { - label: "", - labelInterpolate: $interpolate(blockConfiguration.label), - editor: blockConfiguration.view, - overlaySize: "medium" - }; - + var model = angular.copy(blockConfiguration); + model.labelInterpolate = $interpolate(model.label); + model.overlaySize = model.overlaySize || "medium"; + var scaffold = this.getScaffoldFor(blockConfiguration.contentTypeAlias); if(scaffold === null) { return null; @@ -169,12 +165,11 @@ return entry; }, - // You make entries in your layout your self. - + // private getContentByUdi: function(udi) { return this.value.data.find(entry => entry.udi === udi); }, - + // private createContent: function(elementTypeAlias) { var content = { contentTypeAlias: elementTypeAlias, @@ -183,7 +178,7 @@ this.value.data.push(content); return content.udi; }, - + // private removeContent: function(entry) { const index = this.value.data.indexOf(entry) if (index > -1) { @@ -205,8 +200,6 @@ }, getBlockLabel: function(blockModelObject, labelIndex) { - console.log("getBlockLabel", blockModelObject); - // TODO: we should do something about this for performance. var vars = new Object(); From 13aa5cf2bafb32d4b08ee95c5822432b07d7b95a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Mar 2020 11:23:57 +0100 Subject: [PATCH 056/508] Revert "rename view to overlayView" This reverts commit 5b910c178a4f193d190367c4f1da3402aa8c4d0e. --- .../blocklist/blocklist.component.js | 2 +- .../blocklist.elementtypepicker.controller.js | 13 ++++++++----- .../prevalue/blocklist.elementtypepicker.html | 6 +++--- .../PropertyEditors/BlockListConfiguration.cs | 4 ++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index eefe339569..5e450eb133 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -936,7 +936,7 @@ var elementEditor = { block: blockModel, - view: blockModel.overlayView || "views/common/infiniteeditors/elementeditor/elementeditor.html", + view: "views/common/infiniteeditors/elementeditor/elementeditor.html", size: blockModel.overlaySize, submit: function(model) { blockModel.content = model.block.content; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js index 1f76afef8e..77751579c9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js @@ -150,7 +150,7 @@ var entry = { "contentTypeAlias": alias, - "overlayView": null, + "view": null, "labelTemplate": "", "settingsElementTypeAlias": null }; @@ -233,7 +233,7 @@ localizationService.localizeMany(["general_remove", "defaultdialogs_confirmremoveusageof"]).then(function (data) { overlayService.confirmRemove({ title: data[0], - content: localizationService.tokenReplace(data[1], [entry.overlayView]), + content: localizationService.tokenReplace(data[1], [entry.view]), close: function () { overlayService.close(); }, @@ -245,7 +245,7 @@ }); }; vm.removeViewForEntry = function(entry) { - entry.overlayView = null; + entry.view = null; }; vm.addViewForEntry = function(entry) { const filePicker = { @@ -255,10 +255,13 @@ entityType: "file", isDialog: true, filter: function (i) { - return (i.name.indexOf(".html") !== -1); + if (i.name.indexOf(".html") !== -1) { + return true; + } }, select: function (file) { - entry.overlayView = file.name; + console.log(file); + entry.view = file.name; editorService.close(); }, close: function () { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html index 75a59a0f2d..8184761a1a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html @@ -36,16 +36,16 @@
    -
    +
    - +
    -
    diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index f76612db59..f069804348 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -35,8 +35,8 @@ namespace Umbraco.Web.PropertyEditors [JsonProperty("settingsElementTypeAlias")] public string SettingsElementTypeAlias { get; set; } - [JsonProperty("overlayView")] - public string OverlayView { get; set; } + [JsonProperty("view")] + public string View { get; set; } [JsonProperty("label")] public string Label { get; set; } From 36dab09cc31f18e8de36b7b5a68be8a5fee512d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 3 Mar 2020 13:02:13 +0100 Subject: [PATCH 057/508] block editor implementation --- .../common/services/blockeditor.service.js | 80 +- .../src/common/services/udi.service.js | 2 +- .../imageblock/imageblock.editor.html | 4 +- .../inlineblock/inlineblock.editor.html | 8 +- .../inlineblock/inlineblock.editor.less | 1 + .../labelblock/labelblock.editor.html | 6 +- .../elementContentEditor.component.html | 2 +- .../elementContentEditor.component.js | 2 - .../elementeditor/elementeditor.controller.js | 4 +- .../blocklist/blocklist.block.component.html | 20 + .../blocklist/blocklist.block.component.js | 65 + .../blocklist/blocklist.component.html | 71 +- .../blocklist/blocklist.component.js | 1063 +++-------------- .../propertyeditors/blocklist/blocklist.html | 2 +- .../PropertyEditors/BlockListConfiguration.cs | 4 +- 15 files changed, 322 insertions(+), 1012 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index 2e761fa9e3..bcea8efc5e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -5,24 +5,43 @@ function blockEditorService($interpolate, udiService) { - function applyModelToScaffold(scaffold, contentModel) { - - scaffold.key = contentModel.key; + function mapToEditingModel(editingModel, contentModel) { - var variant = scaffold.variants[0]; + var variant = editingModel.variants[0]; + + for (var t = 0; t < variant.tabs.length; t++) { + var tab = variant.tabs[t]; + + for (var p = 0; p < tab.properties.length; p++) { + var prop = tab.properties[p]; + if (contentModel[prop.alias]) { + console.log("mapping:", prop.alias, contentModel[prop.alias]) + prop.value = contentModel[prop.alias]; + } + } + } + } + + function mapToPropertyModel(editingModel, contentModel) { + + var variant = editingModel.variants[0]; for (var t = 0; t < variant.tabs.length; t++) { var tab = variant.tabs[t]; for (var p = 0; p < tab.properties.length; p++) { var prop = tab.properties[p]; - if (contentModel[prop.propertyAlias]) { - prop.value = contentModel[prop.propertyAlias]; + if (prop.value) { + contentModel[prop.propertyAlias] = prop.value; } } } } + function mapValueToPropertyModel(value, alias, contentModel) { + contentModel[alias] = value; + } + /** * @ngdoc factory @@ -107,7 +126,9 @@ */ getEditingModel: function(layoutEntry) { - var contentModel = this.getContentByUdi(layoutEntry.udi); + var udi = layoutEntry.udi; + + var contentModel = this.getContentByUdi(udi); var blockConfiguration = this.getBlockConfiguration(contentModel.contentTypeAlias); @@ -116,21 +137,44 @@ return null; } - var model = angular.copy(blockConfiguration); - model.labelInterpolate = $interpolate(model.label); - model.overlaySize = model.overlaySize || "medium"; - + var editingModel = {}; + editingModel.config = angular.copy(blockConfiguration); + editingModel.labelInterpolator = $interpolate(editingModel.config.label); + var scaffold = this.getScaffoldFor(blockConfiguration.contentTypeAlias); if(scaffold === null) { return null; } - model.content = angular.copy(scaffold); - applyModelToScaffold(model.content, contentModel); + // make basics from scaffold + editingModel.content = angular.copy(scaffold); + editingModel.content.udi = udi; + + mapToEditingModel(editingModel.content, contentModel); + + editingModel.contentModel = contentModel; + editingModel.layoutModel = layoutEntry; // TODO: settings - return model; + return editingModel; + + }, + + + /** + * Retrieve editing model of a layout entry + * @return {Object} Scaffolded Block Content object. + */ + setDataFromEditingModel: function(editingModel) { + + var udi = editingModel.content.key; + + var contentModel = this.getContentByUdi(udi); + + mapToPropertyModel(editingModel.content, contentModel); + + // TODO: sync settings to layout entry. }, @@ -165,7 +209,6 @@ return entry; }, - // private getContentByUdi: function(udi) { return this.value.data.find(entry => entry.udi === udi); }, @@ -198,12 +241,11 @@ createModelObject: function(propertyModelValue, propertyEditorAlias, blockConfigurations) { return new BlockEditorModelObject(propertyModelValue, propertyEditorAlias, blockConfigurations); }, - getBlockLabel: function(blockModelObject, labelIndex) { + getBlockLabel: function(blockModelObject) { // TODO: we should do something about this for performance. var vars = new Object(); - vars["$index"] = labelIndex; var variant = blockModelObject.content.variants[0]; var tab = variant.tabs[0]; @@ -212,8 +254,8 @@ vars[property.alias] = property.value; } - if(blockModelObject.labelInterpolate) { - return blockModelObject.labelInterpolate(vars); + if(blockModelObject.labelInterpolator) { + return blockModelObject.labelInterpolator(vars); } return blockModelObject.contentTypeName; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/udi.service.js b/src/Umbraco.Web.UI.Client/src/common/services/udi.service.js index b7d0cd05e8..dd289c96a6 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/udi.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/udi.service.js @@ -22,7 +22,7 @@ * @returns {string} The generated UDI */ create: function(entityType) { - return "umb://" + entityType + "/" + String.CreateGuid(); + return "umb://" + entityType + "/" + (String.CreateGuid().replace(/-/g, "")); } } } diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html index 0b82d2202d..e5e68dde9c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html @@ -1,3 +1,3 @@ - diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html index 9561d28a9e..31754466ab 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html @@ -1,10 +1,10 @@
    -
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less index dc014c5daf..ab6b21d898 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less @@ -55,6 +55,7 @@ } .blockelement-inlineblock-editor__inner { border-top: 1px solid @gray-8; + background-color: @gray-11; .umb-group-panel { background-color: transparent; diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html index 419a1fd8a9..78595fce29 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html @@ -1,4 +1,4 @@ - diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.html index 09724f6619..88a2b306ef 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.html @@ -2,7 +2,7 @@
    + ng-repeat="group in vm.content.variants[0].tabs track by group.label">
    {{ group.label }}
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.js index 637df93a72..347d8f1d7b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.js @@ -14,8 +14,6 @@ function ElementEditorComponentController() { - const vm = this; - // TODO: we might not need this.. } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js index ebabe86617..a054e64784 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js @@ -5,9 +5,9 @@ angular.module("umbraco") var vm = this; - vm.content = $scope.model.block.content; + vm.content = $scope.model.content; - vm.title = $scope.model.block.label; + vm.title = $scope.model.title; vm.saveAndClose = function() { if ($scope.model && $scope.model.submit) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.html new file mode 100644 index 0000000000..4231e5c83e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.html @@ -0,0 +1,20 @@ +
    +
    +
    + +
    + + +
    + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js new file mode 100644 index 0000000000..a1b38e509f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js @@ -0,0 +1,65 @@ +(function () { + "use strict"; + angular + .module("umbraco") + .component("blockListPropertyEditorBlock", { + templateUrl: "views/propertyeditors/blocklist/blocklist.block.component.html", + controller: BlockListBlockController, + controllerAs: "vm", + bindings: { + block: "=", + blockEditorApi: "=", + focusThisBlock: " -
    +
    - -
    - -
    -
    -
    - -

    No editor

    -
    - -
    - - -
    - -
    + +
    - - - -
    Minimum %0% entries, needs %1% more.
    -
    +
    Maximum %0% entries, %1% too many.
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 5e450eb133..5546fdeb8b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -8,7 +8,8 @@ controller: BlockListController, controllerAs: "vm", bindings: { - + model: "=", + propertyForm: "=" }, require: { umbProperty: "?^umbProperty" @@ -17,905 +18,135 @@ function BlockListController($scope, $interpolate, editorService, clipboardService, localizationService, overlayService, blockEditorService, contentResource) { + var modelObject; var unsubscribe = []; var vm = this; - var model = $scope.$parent.$parent.model; - vm.propertyForm = $scope.$parent.$parent.propertyForm; - $scope.moveFocusToBlock = null; + vm.moveFocusToBlock = null; - console.log("model JSON:", JSON.stringify(model)); - console.log("model:", model); - console.log("config:", model.config); + vm.$onInit = function() { - vm.validationLimit = model.config.validationLimit; + vm.validationLimit = vm.model.config.validationLimit; + + vm.model.value = vm.model.value || {}; + + modelObject = blockEditorService.createModelObject(vm.model.value, vm.model.editor, vm.model.config.blocks); + modelObject.loadScaffolding(contentResource).then(loaded); + + vm.layout = [];// Property models layout object specific to this Block Editor. + vm.blocks = [];// Runtime model of editing models, needs to be synced to property model on form submit. + vm.availableBlockTypes = [];// Available block entries of this property editor. - console.log("value:", model.value); - - /* - vm.availableBlockTypes = [ - { - alias: "pageModule", - name: "Module", - icon: "icon-document", - prototype_paste_data: { - - elementType: { - alias: "contentTypeAlias", - icon: "icon-document", - label: "Text" - }, - labelTemplate: "{{pageTitle | truncate:true:36}}", - labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), - editor: "views/blockelements/labelblock/labelblock.editor.html", - overlaySize: "medium", - content: { - apps: [ - { - name: "Content", - alias: "umbContent", - weight: -100, - icon: "icon-document", - view: "views/content/apps/content/content.html", - viewModel: 0, - active: true, - badge: null, - anchors: [], - hasError: false - }, - { - name: "Info", - alias: "umbInfo", - weight: 100, - icon: "icon-info", - view: "views/content/apps/info/info.html", - viewModel: null, - active: false, - badge: null, - hasError: false - } - ], - variants: [ - { - language: { - isDefault: true - } - } - ], - tabs: [ - { - id: 1234, - label: "Content", - properties: [ - { - label: "Page Title", - description: "The title of the page", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 441, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", - alias: "pageTitle", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - }, - { - label: "Image", - description: "", - view: "mediapicker", - config: {multiPicker: false, - onlyImages: true, - disableFolderSelect: true, - startNodeId: "umb://media/1fd2ecaff3714c009306867fa4585e7a", - ignoreUserStartNodes: false, - idType: "udi" - }, - hideLabel: false, - validation: {mandatory: false, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 495, - dataTypeKey: "e26a8d91-a9d7-475b-bc3b-2a09f4743754", - value: "umb://media/fa763e0d0ceb408c8720365d57e06e32", - alias: "photo", - editor: "Umbraco.MediaPicker", - isSensitive: false, - culture: null, - segment: null - }, - { - label: "Image Description", - description: "The title of the page", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 442, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "Let´s have a chat", - alias: "imageDesc", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - } - ] - }, - { - id: 1234, - label: "Styling", - properties: [ - { - label: "Background color", - description: "", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 441, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn´t distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", - alias: "pageTitle", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - } - ] - } - ] - } - } - }, - { - alias: "pageModule", - name: "Inline module", - icon: "icon-document", - prototype_paste_data: { - elementType: { - alias: "contentTypeAlias", - icon: "icon-document", - label: "Text" - }, - labelTemplate: "{{imageTitle | truncate:true:36}}", - labelInterpolate: $interpolate("{{imageTitle | truncate:true:36}}"), - key: 1, - editor: "views/blockelements/inlineblock/inlineblock.editor.html", - overlaySize: "medium", - content: { - apps: [ - { - name: "Content", - alias: "umbContent", - weight: -100, - icon: "icon-document", - view: "views/content/apps/content/content.html", - viewModel: 0, - active: true, - badge: null, - anchors: [], - hasError: false - }, - { - name: "Info", - alias: "umbInfo", - weight: 100, - icon: "icon-info", - view: "views/content/apps/info/info.html", - viewModel: null, - active: false, - badge: null, - hasError: false - } - ], - variants: [ - { - language: { - isDefault: true - } - } - ], - tabs: [ - { - id: 1234, - label: "Content", - properties: [ - { - label: "Image Title", - description: "The title on top of image", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 441, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn´t distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", - alias: "imageTitle", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - }, - { - label: "Image", - description: "", - view: "mediapicker", - config: {multiPicker: false, - onlyImages: true, - disableFolderSelect: true, - startNodeId: "umb://media/1fd2ecaff3714c009306867fa4585e7a", - ignoreUserStartNodes: false, - idType: "udi" - }, - hideLabel: false, - validation: {mandatory: false, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 495, - dataTypeKey: "e26a8d91-a9d7-475b-bc3b-2a09f4743754", - value: "umb://media/fa763e0d0ceb408c8720365d57e06e32", - alias: "photo", - editor: "Umbraco.MediaPicker", - isSensitive: false, - culture: null, - segment: null - } - ] - } - ] - } - } - }, - { - alias: "contentTypeAlias", - name: "Text", - icon: "icon-info", - prototype_paste_data: { - elementType: { - alias: "contentTypeAlias", - icon: "icon-document", - label: "Text" - }, - labelTemplate: "Label", - labelInterpolate: $interpolate("Label"), - editor: "views/blockelements/textareablock/textareablock.editor.html", - overlaySize: "medium", - content: { - apps: [ - { - name: "Content", - alias: "umbContent", - weight: -100, - icon: "icon-document", - view: "views/content/apps/content/content.html", - viewModel: 0, - active: true, - badge: null, - anchors: [], - hasError: false - }, - { - name: "Info", - alias: "umbInfo", - weight: 100, - icon: "icon-info", - view: "views/content/apps/info/info.html", - viewModel: null, - active: false, - badge: null, - hasError: false - } - ], - variants: [ - { - language: { - isDefault: true - } - } - ], - tabs: [ - { - id: 1234, - label: "Content", - properties: [ - { - label: "Page Title", - description: "The title of the page", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 441, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "", - alias: "pageTitle", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - } - ] - } - ] - } - } - }, - { - alias: "contentTypeAlias", - name: "Image", - icon: "icon-picture", - prototype_paste_data: { - elementType: { - alias: "contentTypeAlias", - icon: "icon-document", - label: "Text" - }, - labelTemplate: "Label", - labelInterpolate: $interpolate("Label"), - editor: "views/blockelements/imageblock/imageblock.editor.html", - overlaySize: "medium", - content: { - apps: [ - { - name: "Content", - alias: "umbContent", - weight: -100, - icon: "icon-document", - view: "views/content/apps/content/content.html", - viewModel: 0, - active: true, - badge: null, - anchors: [], - hasError: false - }, - { - name: "Info", - alias: "umbInfo", - weight: 100, - icon: "icon-info", - view: "views/content/apps/info/info.html", - viewModel: null, - active: false, - badge: null, - hasError: false - } - ], - variants: [ - { - language: { - isDefault: true - } - } - ], - tabs: [ - { - id: 1234, - label: "Content", - properties: [ - { - label: "Image", - description: "", - view: "mediapicker", - config: {multiPicker: false, - onlyImages: true, - disableFolderSelect: true, - startNodeId: "umb://media/1fd2ecaff3714c009306867fa4585e7a", - ignoreUserStartNodes: false, - idType: "udi" - }, - hideLabel: false, - validation: {mandatory: false, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 495, - dataTypeKey: "e26a8d91-a9d7-475b-bc3b-2a09f4743754", - value: "umb://media/fa763e0d0ceb408c8720365d57e06e32", - alias: "photo", - editor: "Umbraco.MediaPicker", - isSensitive: false, - culture: null, - segment: null - }, - { - label: "Image Description", - description: "The title of the page", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 441, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "Let´s have a chat", - alias: "imageDesc", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - } - ] - } - ], - temp_image: "/umbraco/assets/img/login.jpg" - } - } + var copyAllEntriesAction = { + labelKey: "clipboard_labelForCopyAllEntries", + labelTokens: [vm.model.label], + icon: "documents", + method: function () {}, + isDisabled: true } - ]; - - // var defaultBlockType... - - // TODO: get icon, properties etc. from available types? - vm.blocks = [ - { - elementType: { - alias: "contentTypeAlias", - icon: "icon-document", - label: "Text" - }, - labelTemplate: "{{pageTitle | truncate:true:36}}", - labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), - key: 1, - editor: "views/blockelements/labelblock/labelblock.editor.html", - overlaySize: "medium", - content: { - apps: [ - { - name: "Content", - alias: "umbContent", - weight: -100, - icon: "icon-document", - view: "views/common/infiniteeditors/elementeditor/elementeditor.content.html", - viewModel: 0, - active: true, - badge: null, - anchors: [], - hasError: false - }, - { - name: "Info", - alias: "umbInfo", - weight: 100, - icon: "icon-info", - view: "views/content/apps/info/info.html", - viewModel: null, - active: false, - badge: null, - hasError: false - } - ], - variants: [ - { - language: { - isDefault: true - } - } - ], - tabs: [ - { - id: 1234, - label: "Content", - properties: [ - { - label: "Page Title", - description: "The title of the page", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 441, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn´t distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", - alias: "pageTitle", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - }, - { - label: "Image", - description: "", - view: "mediapicker", - config: {multiPicker: false, - onlyImages: true, - disableFolderSelect: true, - startNodeId: "umb://media/1fd2ecaff3714c009306867fa4585e7a", - ignoreUserStartNodes: false, - idType: "udi" - }, - hideLabel: false, - validation: {mandatory: false, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 495, - dataTypeKey: "e26a8d91-a9d7-475b-bc3b-2a09f4743754", - value: "umb://media/fa763e0d0ceb408c8720365d57e06e32", - alias: "photo", - editor: "Umbraco.MediaPicker", - isSensitive: false, - culture: null, - segment: null - }, - { - label: "Image Description", - description: "The title of the page", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 442, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "Let´s have a chat", - alias: "imageDesc", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - } - ] - }, - { - id: 1234, - label: "Styling", - properties: [ - { - label: "Background color", - description: "", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 441, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn´t distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", - alias: "pageTitle", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - } - ] - } - ] - } - }, - { - elementType: { - alias: "contentTypeAlias", - icon: "icon-document", - label: "Text" - }, - labelTemplate: "{{pageTitle | truncate:true:36}}", - labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), - key: 2, - editor: "views/blockelements/labelblock/labelblock.editor.html", - overlaySize: "medium", - content: { - apps: [ - { - name: "Content", - alias: "umbContent", - weight: -100, - icon: "icon-document", - view: "views/content/apps/content/content.html", - viewModel: 0, - active: true, - badge: null, - anchors: [], - hasError: false - }, - { - name: "Info", - alias: "umbInfo", - weight: 100, - icon: "icon-info", - view: "views/content/apps/info/info.html", - viewModel: null, - active: false, - badge: null, - hasError: false - } - ], - variants: [ - { - language: { - isDefault: true - } - } - ], - tabs: [ - { - id: 1234, - label: "Content", - properties: [ - { - label: "Page Title", - description: "The title of the page", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 441, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", - alias: "pageTitle", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - }, - { - label: "Image", - description: "", - view: "mediapicker", - config: {multiPicker: false, - onlyImages: true, - disableFolderSelect: true, - startNodeId: "umb://media/1fd2ecaff3714c009306867fa4585e7a", - ignoreUserStartNodes: false, - idType: "udi" - }, - hideLabel: false, - validation: {mandatory: false, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 495, - dataTypeKey: "e26a8d91-a9d7-475b-bc3b-2a09f4743754", - value: "umb://media/fa763e0d0ceb408c8720365d57e06e32", - alias: "photo", - editor: "Umbraco.MediaPicker", - isSensitive: false, - culture: null, - segment: null - }, - { - label: "Image Description", - description: "The title of the page", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 442, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "Let´s have a chat", - alias: "imageDesc", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - } - ] - }, - { - id: 1234, - label: "Styling", - properties: [ - { - label: "Background color", - description: "", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 441, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn´t distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", - alias: "pageTitle", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - } - ] - } - ] - } - }, - { - - elementType: { - alias: "contentTypeAlias", - icon: "icon-document", - label: "Text" - }, - labelTemplate: "{{pageTitle | truncate:true:36}}", - labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), - key: 3, - editor: "views/blockelements/labelblock/labelblock.editor.html", - overlaySize: "medium", - content: { - apps: [ - { - name: "Content", - alias: "umbContent", - weight: -100, - icon: "icon-document", - view: "views/content/apps/content/content.html", - viewModel: 0, - active: true, - badge: null, - anchors: [], - hasError: false - }, - { - name: "Info", - alias: "umbInfo", - weight: 100, - icon: "icon-info", - view: "views/content/apps/info/info.html", - viewModel: null, - active: false, - badge: null, - hasError: false - } - ], - variants: [ - { - language: { - isDefault: true - } - } - ], - tabs: [ - { - id: 1234, - label: "Content", - properties: [ - { - label: "Page Title", - description: "The title of the page", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 441, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", - alias: "pageTitle", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - }, - { - label: "Image", - description: "", - view: "mediapicker", - config: {multiPicker: false, - onlyImages: true, - disableFolderSelect: true, - startNodeId: "umb://media/1fd2ecaff3714c009306867fa4585e7a", - ignoreUserStartNodes: false, - idType: "udi" - }, - hideLabel: false, - validation: {mandatory: false, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 495, - dataTypeKey: "e26a8d91-a9d7-475b-bc3b-2a09f4743754", - value: "umb://media/fa763e0d0ceb408c8720365d57e06e32", - alias: "photo", - editor: "Umbraco.MediaPicker", - isSensitive: false, - culture: null, - segment: null - }, - { - label: "Image Description", - description: "The title of the page", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 442, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "Let´s have a chat", - alias: "imageDesc", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - } - ] - }, - { - id: 1234, - label: "Styling", - properties: [ - { - label: "Background color", - description: "", - view: "textbox", - config: {maxChars: 500}, - hideLabel: false, - validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, - readonly: false, - id: 441, - dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn´t distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", - alias: "pageTitle", - editor: "Umbraco.TextBox", - isSensitive: false, - culture: null, - segment: null - } - ] - } - ] - } + + var propertyActions = [ + copyAllEntriesAction + ]; + + if (this.umbProperty) { + this.umbProperty.setPropertyActions(propertyActions); } - ]; + }; + + - */ - - - model.value = model.value || {}; - - var modelObject = blockEditorService.createModelObject(model.value, model.editor, model.config.blocks); - - modelObject.loadScaffolding(contentResource).then(loaded); - - vm.layout = []; - vm.blocks = []; - vm.availableBlockTypes = []; - + function loaded() { - console.log("Loading done!!!"); - console.log(modelObject); - - vm.layout = modelObject.getLayout(); + mapToBlocks(); + + vm.availableBlockTypes = modelObject.getAvailableBlocksForItemPicker(); + + } + + + function getEditingModel(entry) { + var block = modelObject.getEditingModel(entry); + + if (block === null) return null; + + block.view = block.config.view || vm.model.config.useInlineEditingAsDefault ? "views/blockelements/inlineblock/inlineblock.editor.html" : "views/blockelements/labelblock/labelblock.editor.html"; + + return block; + } + + /** + * Maps property model to the runtime editing model (blocks). + */ + function mapToBlocks() { + // clear blocks. + vm.blocks = []; + + // make all blocks. vm.layout.forEach(entry => { - var block = modelObject.getEditingModel(entry); + var block = getEditingModel(entry); if(block !== null) { vm.blocks.push(block); } }); - vm.availableBlockTypes = modelObject.getAvailableBlocksForItemPicker(); - console.log(vm.availableBlockTypes); - + $scope.$apply(); + } + + /** + * Maps content from runtime editing model (blocks) to the property model. + * Does not take care of ordering, we need the sort-UI to sync that, on the fly. + */ + function mapToContent() { + + // sync data from blocks to content models. + vm.blocks.forEach(block => { + modelObject.setDataFromEditingModel(block); + }); + } + + function sync() { + mapToContent(); + } + + function syncBlockData(block) { + modelObject.setDataFromEditingModel(block); } - function setDirty() { if (vm.propertyForm) { vm.propertyForm.$setDirty(); } - }; + } function addNewBlock(index, contentTypeAlias) { - // Create layout entry. + // Create layout entry. (not added to property model jet.) var layoutEntry = modelObject.createLayoutEntry(contentTypeAlias); - // add layout entry a decired location in layout. - vm.layout.splice(index, 0, layoutEntry); // make editing object - var blockEditingObject = modelObject.getEditingModel(layoutEntry); - // apply editing model at decired location in editing model. - vm.blocks.splice(index, 0, blockEditingObject); + var blockEditingObject = getEditingModel(layoutEntry); + + if (blockEditingObject !== null) { - $scope.moveFocusToBlock = blockEditingObject; + // add layout entry at the decired location in layout. + vm.layout.splice(index, 0, layoutEntry); + + // apply editing model at decired location in editing model. + vm.blocks.splice(index, 0, blockEditingObject); + + vm.moveFocusToBlock = blockEditingObject; + + } } - vm.deleteBlock = function(block) { + function deleteBlock(block) { var index = vm.blocks.indexOf(block); if(index !== -1) { vm.blocks.splice(index, 1); @@ -929,18 +160,18 @@ } } - vm.editBlock = function(blockModel) { + function editBlock(blockModel) { - // TODO: test wether i need to clone or if that is done by overlay. - //var blockModelClone = angular.copy(blockModel); + // TODO: make a clone to ensure edits arent made directly. + var blockContentModelClone = angular.copy(blockModel.content); - var elementEditor = { - block: blockModel, + var elementEditorModel = { + content: blockContentModelClone, + title: blockModel.label, view: "views/common/infiniteeditors/elementeditor/elementeditor.html", - size: blockModel.overlaySize, - submit: function(model) { - blockModel.content = model.block.content; - blockModel.settings = model.block.settings; + size: blockModel.config.overlaySize || "medium", + submit: function(elementEditorModel) { + blockModel.content = elementEditorModel.content; editorService.close(); }, close: function() { @@ -949,10 +180,11 @@ }; // open property settings editor - editorService.open(elementEditor); + editorService.open(elementEditorModel); } - vm.showCreateDialog = function (createIndex, $event) { + vm.showCreateDialog = showCreateDialog; + function showCreateDialog(createIndex, $event) { if (vm.blockTypePicker) { return; @@ -984,10 +216,10 @@ }; - vm.requestCopyBlock = function(block) { - console.log("copy") + function requestCopyBlock(block) { + console.log("copy still needs to be done.") } - vm.requestDeleteBlock = function(block) { + function requestDeleteBlock (block) { localizationService.localizeMany(["general_delete", "blockEditor_confirmDeleteBlockMessage", "contentTypeEditor_yesDelete"]).then(function (data) { const overlay = { title: data[0], @@ -997,7 +229,7 @@ overlayService.close(); }, submit: function () { - vm.deleteBlock(block); + deleteBlock(block); overlayService.close(); } }; @@ -1008,7 +240,7 @@ vm.showCopy = clipboardService.isSupported(); - + var runtimeSortVars = {}; vm.sorting = false; vm.sortableOptions = { @@ -1021,6 +253,7 @@ tolerance: "pointer", scroll: true, start: function (ev, ui) { + runtimeSortVars.moveFromIndex = ui.item.index(); $scope.$apply(function () { vm.sorting = true; }); @@ -1029,45 +262,42 @@ setDirty(); }, stop: function (ev, ui) { + + // Lets update the layout part of the property model to match the update. + var moveFromIndex = runtimeSortVars.moveFromIndex; + var moveToIndex = ui.item.index(); + + if (moveToIndex > -1 && moveFromIndex !== moveToIndex) { + var movedEntry = vm.layout[moveFromIndex]; + vm.layout.splice(moveFromIndex, 1); + vm.layout.splice(moveToIndex, 0, movedEntry); + } + $scope.$apply(function () { vm.sorting = false; }); } }; - $scope.blockApi = { - deleteBlock: vm.deleteBlock + vm.blockEditorApi = { + editBlock: editBlock, + requestCopyBlock: requestCopyBlock, + requestDeleteBlock: requestDeleteBlock, + deleteBlock: deleteBlock, + syncBlockData: syncBlockData } - var copyAllEntriesAction = { - labelKey: "clipboard_labelForCopyAllEntries", - labelTokens: [model.label], - icon: "documents", - method: function () {}, - isDisabled: true - } - - var propertyActions = [ - copyAllEntriesAction - ]; - - this.$onInit = function () { - if (this.umbProperty) { - this.umbProperty.setPropertyActions(propertyActions); - } - }; - - function validateLimits() { - if (vm.validationLimit.min && vm.blocks.length < vm.validationLimit.min) { - vm.propertyForm.minCount.$setValidity("minCount", false); + if (vm.validationLimit.min !== null) { + if (vm.blocks.length < vm.validationLimit.min) { + vm.propertyForm.minCount.$setValidity("minCount", false); + } + else { + vm.propertyForm.minCount.$setValidity("minCount", true); + } } - else { - vm.propertyForm.minCount.$setValidity("minCount", true); - } - - if (vm.validationLimit.max && vm.blocks.length > vm.validationLimit.max) { + if (vm.validationLimit.max !== null && vm.blocks.length > vm.validationLimit.max) { vm.propertyForm.maxCount.$setValidity("maxCount", false); } else { @@ -1079,15 +309,32 @@ // TODO: We need to investigate if we can do a specific watch on each block, so we dont re-render all blocks. - unsubscribe.push($scope.$watch("vm.blocks", onBlocksUpdated, true)); + /* + unsubscribe.push($scope.$watch("vm.blocks[0]", onBlocksUpdated, true)); function onBlocksUpdated(newVal, oldVal) { + + console.log("blocks update", oldVal, " > ", newVal); + //setDirty(); + var labelIndex = 1; for(const block of vm.blocks) { block.label = blockEditorService.getBlockLabel(block, labelIndex++); } } + */ + + unsubscribe.push($scope.$watch(() => vm.blocks.length, validateLimits)); + unsubscribe.push($scope.$on("formSubmitting", function (ev, args) { + + console.log("formSubmitting is happening, we need to make sure sub property editors are synced first.") + + console.log(vm.layout, vm.model.value); + + //sync(); + })); + $scope.$on("$destroy", function () { for (const subscription of unsubscribe) { subscription(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.html index 198dab4f5f..eea9f01ff3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.html @@ -1 +1 @@ - + diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index f069804348..20b6c00d68 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -42,8 +42,8 @@ namespace Umbraco.Web.PropertyEditors public string Label { get; set; } } - [ConfigurationField("useAccordionsAsDefault", "Inline editing mode", "boolean", Description = "Use the inline editor as the default block view")] - public bool useInlineEditingAsDefault { get; set; } + [ConfigurationField("useInlineEditingAsDefault", "Inline editing mode", "boolean", Description = "Use the inline editor as the default block view")] + public bool UseInlineEditingAsDefault { get; set; } } } From fd6d5dcedfce244af784ef74b37368d311bd173e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 3 Mar 2020 14:30:21 +0100 Subject: [PATCH 058/508] sync models --- .../common/services/blockeditor.service.js | 4 +--- .../blocklist/blocklist.block.component.js | 23 +++++++++++++++---- .../blocklist/blocklist.component.js | 12 +++++++--- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index bcea8efc5e..3ac9742171 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -11,11 +11,10 @@ for (var t = 0; t < variant.tabs.length; t++) { var tab = variant.tabs[t]; - + for (var p = 0; p < tab.properties.length; p++) { var prop = tab.properties[p]; if (contentModel[prop.alias]) { - console.log("mapping:", prop.alias, contentModel[prop.alias]) prop.value = contentModel[prop.alias]; } } @@ -89,7 +88,6 @@ scaffoldAliases.forEach((elementTypeAlias => { tasks.push(contentResource.getScaffold(-20, elementTypeAlias).then(scaffold => { - console.log(scaffold); this.scaffolds.push(scaffold); })); })); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js index a1b38e509f..8fe08888a9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js @@ -18,8 +18,6 @@ var unsubscribe = []; var vm = this; - console.log("BlockListBlockController", vm); - vm.$onInit = function() { // Start watching each property value. @@ -30,7 +28,9 @@ for (var p = 0; p < tab.properties.length; p++) { var prop = tab.properties[p]; - unsubscribe.push($scope.$watch("vm.block.content.variants[0].tabs["+t+"].properties["+p+"].value", createPropWatcher(prop))); + + // Sadly we need to deep watch, cause its our only way to make sure that complex values gets synced. Alternative solution would be to sync on a broadcasted event, fired on Save and Copy eventually more. + unsubscribe.push($scope.$watch("vm.block.content.variants[0].tabs["+t+"].properties["+p+"].value", createPropWatcher(prop), true)); } } } @@ -40,9 +40,10 @@ return function() { // sync data: - console.log(prop.alias, prop.value); vm.block.contentModel[prop.alias] = prop.value; + vm.blockEditorApi.sync(); + // update label: updateLabel(); } @@ -52,6 +53,20 @@ function updateLabel() { vm.block.label = blockEditorService.getBlockLabel(vm.block); } + + /** + * Listening for properties + */ + /* + function onBlockEditorValueUpdated($event) { + // Lets sync the value of the property that the event comes from, if we know that.. + + //$event.stopPropagation(); + //$event.preventDefault(); + }; + + unsubscribe.push($scope.$on("blockEditorValueUpdated", onBlockEditorValueUpdated)); + */ $scope.$on("$destroy", function () { for (const subscription of unsubscribe) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 5546fdeb8b..204988927d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -100,6 +100,7 @@ * Maps content from runtime editing model (blocks) to the property model. * Does not take care of ordering, we need the sort-UI to sync that, on the fly. */ + /* function mapToContent() { // sync data from blocks to content models. @@ -107,10 +108,15 @@ modelObject.setDataFromEditingModel(block); }); } + */ + /* function sync() { - mapToContent(); + // to avoid deep watches of block editors we use an event for those instead? + // Lets inform container of this property editor that we updated. + $scope.$emit("blockEditorValueUpdated"); } + */ function syncBlockData(block) { modelObject.setDataFromEditingModel(block); @@ -325,7 +331,7 @@ unsubscribe.push($scope.$watch(() => vm.blocks.length, validateLimits)); - +/* unsubscribe.push($scope.$on("formSubmitting", function (ev, args) { console.log("formSubmitting is happening, we need to make sure sub property editors are synced first.") @@ -334,7 +340,7 @@ //sync(); })); - +*/ $scope.$on("$destroy", function () { for (const subscription of unsubscribe) { subscription(); From 5a65719b0d41c01a63bf8a3b319df321f5459d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 4 Mar 2020 13:30:26 +0100 Subject: [PATCH 059/508] block list editor copy paste feature --- .../common/services/blockeditor.service.js | 78 +++-- .../imageblock/imageblock.editor.html | 4 +- .../inlineblock/inlineblock.editor.html | 8 +- .../labelblock/labelblock.editor.html | 6 +- .../textareablock.editor.controller.js | 2 +- .../textareablock/textareablock.editor.html | 2 +- .../blocklist/blocklist.block.component.html | 19 +- .../blocklist/blocklist.block.component.js | 13 +- .../blocklist/blocklist.component.html | 19 +- .../blocklist/blocklist.component.js | 266 ++++++++++++++---- 10 files changed, 304 insertions(+), 113 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index 3ac9742171..00308ae4ff 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -5,9 +5,13 @@ function blockEditorService($interpolate, udiService) { - function mapToEditingModel(editingModel, contentModel) { + /** + * Simple mapping from property model content entry to editing model, + * needs to stay simple to avoid deep watching. + */ + function mapToElementTypeModel(elementTypeModel, contentModel) { - var variant = editingModel.variants[0]; + var variant = elementTypeModel.variants[0]; for (var t = 0; t < variant.tabs.length; t++) { var tab = variant.tabs[t]; @@ -21,9 +25,13 @@ } } - function mapToPropertyModel(editingModel, contentModel) { + /** + * Simple mapping from elementTypeModel to property model content entry, + * needs to stay simple to avoid deep watching. + */ + function mapToPropertyModel(elementTypeModel, contentModel) { - var variant = editingModel.variants[0]; + var variant = elementTypeModel.variants[0]; for (var t = 0; t < variant.tabs.length; t++) { var tab = variant.tabs[t]; @@ -31,7 +39,7 @@ for (var p = 0; p < tab.properties.length; p++) { var prop = tab.properties[p]; if (prop.value) { - contentModel[prop.propertyAlias] = prop.value; + contentModel[prop.alias] = prop.value; } } } @@ -95,12 +103,19 @@ return Promise.all(tasks); }, + /** + * Retrive a list of aliases that are available for content of blocks in this property editor, does not contain aliases of block settings. + * @return {Array} array of strings representing alias. + */ + getAvailableAliasesForBlockContent: function() { + return this.blockConfigurations.map(blockConfiguration => blockConfiguration.contentTypeAlias); + }, + getAvailableBlocksForItemPicker: function() { var blocks = []; this.blockConfigurations.forEach(blockConfiguration => { - var scaffold = this.getScaffoldFor(blockConfiguration.contentTypeAlias); if(scaffold) { blocks.push({ @@ -148,7 +163,7 @@ editingModel.content = angular.copy(scaffold); editingModel.content.udi = udi; - mapToEditingModel(editingModel.content, contentModel); + mapToElementTypeModel(editingModel.content, contentModel); editingModel.contentModel = contentModel; editingModel.layoutModel = layoutEntry; @@ -168,17 +183,15 @@ var udi = editingModel.content.key; - var contentModel = this.getContentByUdi(udi); - - mapToPropertyModel(editingModel.content, contentModel); + mapToPropertyModel(editingModel.content, editingModel.contentModel); // TODO: sync settings to layout entry. }, /** - * Retrieve layout data - * @return layout object. + * Retrieve the layout object for this specific property editor. + * @return {Object} Layout object. */ getLayout: function() { if (!this.value.layout[this.propertyEditorAlias]) { @@ -188,13 +201,16 @@ }, /** - * Create layout entry - * @param {object} blockConfiguration - * @return layout entry, to be added in the layout. + * Create a empty layout entry + * @param {Object} blockConfiguration + * @return {Object} Layout entry object, to be inserted at a decired location in the layout object. */ - createLayoutEntry: function(contentTypeAlias) { + create: function(contentTypeAlias) { var blockConfiguration = this.getBlockConfiguration(contentTypeAlias); + if(blockConfiguration === null) { + return null; + } var entry = { udi: this.createContent(contentTypeAlias) @@ -222,16 +238,42 @@ // private removeContent: function(entry) { const index = this.value.data.indexOf(entry) - if (index > -1) { + if (index !== -1) { this.value.splice(index, 1); } }, removeContentByUdi: function(udi) { const index = this.value.data.findIndex(o => o.udi === udi); - if (index > -1) { + if (index !== -1) { this.value.splice(index, 1); } + }, + + /** + * Insert data from ElementType Model + * @return {Object} Layout entry object, to be inserted at a decired location in the layout object. + */ + createFromElementType: function(elementTypeContentModel) { + + elementTypeContentModel = angular.copy(elementTypeContentModel); + + var contentTypeAlias = elementTypeContentModel.contentTypeAlias; + + var layoutEntry = this.create(contentTypeAlias); + if(layoutEntry === null) { + return null; + } + + var contentModel = this.getContentByUdi(layoutEntry.udi); + + mapToPropertyModel(elementTypeContentModel, contentModel); + + console.log(elementTypeContentModel) + console.log(contentModel) + + return layoutEntry; + } } diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html index e5e68dde9c..62cd4384b8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html @@ -1,3 +1,3 @@ - diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html index 31754466ab..56fbe13e2b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html @@ -1,10 +1,10 @@
    -
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html index 78595fce29..2036e3ef3a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html @@ -1,4 +1,4 @@ - diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js index 868573fb72..9fc213cd4a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js @@ -5,7 +5,7 @@ angular.module("umbraco") var vm = this; - vm.firstProperty = $scope.block.content.variants[0].tabs[0].properties[0]; + vm.firstProperty = $scope.blockItem.content.variants[0].tabs[0].properties[0]; /* vm.onBlur = function() { if (vm.firstProperty.value === null || vm.firstProperty.value === "") { diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html index c64d42b6ac..80aba51a84 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html @@ -8,7 +8,7 @@ ng-model="vm.firstProperty.value" ng-keypress="vm.submitOnEnter($event)" ng-blur="vm.onBlur()" - focus-when="{{moveFocusToBlock === block}}" + focus-when="{{blockvm.focusThis}}" > diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.html index 4231e5c83e..36905978d5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.html @@ -1,20 +1,5 @@ -
    -
    -
    +
    -
    - - -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js index 8fe08888a9..330d8f7b69 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js @@ -4,12 +4,15 @@ .module("umbraco") .component("blockListPropertyEditorBlock", { templateUrl: "views/propertyeditors/blocklist/blocklist.block.component.html", + transclude: true, controller: BlockListBlockController, controllerAs: "vm", bindings: { block: "=", - blockEditorApi: "=", - focusThisBlock: " - + +
    +
    + +
    + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 204988927d..157d31b9cf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -12,59 +12,95 @@ propertyForm: "=" }, require: { - umbProperty: "?^umbProperty" + umbProperty: "?^umbProperty", + umbVariantContent: '?^^umbVariantContent' } }); - function BlockListController($scope, $interpolate, editorService, clipboardService, localizationService, overlayService, blockEditorService, contentResource) { + function BlockListController($scope, $interpolate, editorService, clipboardService, localizationService, overlayService, blockEditorService, contentResource, eventsService) { var modelObject; var unsubscribe = []; var vm = this; vm.moveFocusToBlock = null; + vm.showCopy = clipboardService.isSupported(); + vm.showPaste = false; + + vm.layout = [];// Property models layout object specific to this Block Editor. + vm.blocks = [];// Runtime model of editing models, needs to be synced to property model on form submit. + vm.availableBlockTypes = [];// Available block entries of this property editor. + + var labels = {}; + vm.labels = labels; + localizationService.localizeMany(["grid_addElement", "content_createEmpty"]).then(function (data) { + labels.grid_addElement = data[0]; + labels.content_createEmpty = data[1]; + }); + + function checkAbilityToPasteContent() { + vm.showPaste = clipboardService.hasEntriesOfType("elementType", vm.availableContentTypes) || clipboardService.hasEntriesOfType("elementTypeArray", vm.availableContentTypes); + } + eventsService.on("clipboardService.storageUpdate", checkAbilityToPasteContent); + + + var copyAllBlocksAction; + var deleteAllBlocksAction; + vm.$onInit = function() { vm.validationLimit = vm.model.config.validationLimit; - + vm.model.value = vm.model.value || {}; modelObject = blockEditorService.createModelObject(vm.model.value, vm.model.editor, vm.model.config.blocks); modelObject.loadScaffolding(contentResource).then(loaded); - - vm.layout = [];// Property models layout object specific to this Block Editor. - vm.blocks = [];// Runtime model of editing models, needs to be synced to property model on form submit. - vm.availableBlockTypes = [];// Available block entries of this property editor. - var copyAllEntriesAction = { + copyAllBlocksAction = { labelKey: "clipboard_labelForCopyAllEntries", labelTokens: [vm.model.label], icon: "documents", - method: function () {}, + method: requestCopyAllBlocks, isDisabled: true } - + deleteAllBlocksAction = { + labelKey: 'clipboard_labelForRemoveAllEntries', + labelTokens: [], + icon: 'trash', + method: requestDeleteAllBlocks, + isDisabled: true + } + var propertyActions = [ - copyAllEntriesAction + copyAllBlocksAction, + deleteAllBlocksAction ]; - if (this.umbProperty) { - this.umbProperty.setPropertyActions(propertyActions); + if (vm.umbProperty) { + vm.umbProperty.setPropertyActions(propertyActions); } }; - + + function setDirty() { + if (vm.propertyForm) { + vm.propertyForm.$setDirty(); + } + } function loaded() { vm.layout = modelObject.getLayout(); mapToBlocks(); + vm.availableContentTypes = modelObject.getAvailableAliasesForBlockContent(); vm.availableBlockTypes = modelObject.getAvailableBlocksForItemPicker(); + checkAbilityToPasteContent(); + } @@ -117,36 +153,35 @@ $scope.$emit("blockEditorValueUpdated"); } */ - + /* function syncBlockData(block) { modelObject.setDataFromEditingModel(block); } - - function setDirty() { - if (vm.propertyForm) { - vm.propertyForm.$setDirty(); - } - } + */ function addNewBlock(index, contentTypeAlias) { // Create layout entry. (not added to property model jet.) - var layoutEntry = modelObject.createLayoutEntry(contentTypeAlias); + var layoutEntry = modelObject.create(contentTypeAlias); + if (layoutEntry === null) { + return false; + } // make editing object var blockEditingObject = getEditingModel(layoutEntry); - - if (blockEditingObject !== null) { - - // add layout entry at the decired location in layout. - vm.layout.splice(index, 0, layoutEntry); - - // apply editing model at decired location in editing model. - vm.blocks.splice(index, 0, blockEditingObject); - - vm.moveFocusToBlock = blockEditingObject; - + if (blockEditingObject === null) { + return false; } + + // add layout entry at the decired location in layout. + vm.layout.splice(index, 0, layoutEntry); + + // apply editing model at decired location in editing model. + vm.blocks.splice(index, 0, blockEditingObject); + + vm.moveFocusToBlock = blockEditingObject; + + return true; } @@ -157,18 +192,22 @@ if(index !== -1) { vm.blocks.splice(index, 1); - var layoutIndex = this.layout.findIndex(entry => entry.udi === block.udi); + var layoutIndex = vm.layout.findIndex(entry => entry.udi === block.udi); if(layoutIndex !== -1) { - vm.layout.splice(layoutIndex, 1); + vm.layout.splice(index, 1); } - this.modelObject.removeContentByUdi(block.udi); + modelObject.removeContentByUdi(block.udi); } } + function deleteAllBlocks() { + vm.blocks.forEach(deleteBlock); + } + function editBlock(blockModel) { - // TODO: make a clone to ensure edits arent made directly. + // make a clone to avoid editing model directly. var blockContentModelClone = angular.copy(blockModel.content); var elementEditorModel = { @@ -178,6 +217,8 @@ size: blockModel.config.overlaySize || "medium", submit: function(elementEditorModel) { blockModel.content = elementEditorModel.content; + // TODO, investigate if we need to call a sync, for this scenario to work.. Concern is regarding wether the property-value watcher will pick this up. + //modelObject.setDataFromEditingModel(block); editorService.close(); }, close: function() { @@ -201,13 +242,26 @@ } vm.blockTypePicker = { - show: true, + show: false, size: vm.availableBlockTypes.length < 7 ? "small" : "medium", filter: vm.availableBlockTypes.length > 12 ? true : false, orderBy: "$index", view: "itempicker", event: $event, availableItems: vm.availableBlockTypes, + clickPasteItem: function(item) { + if (item.type === "elementTypeArray") { + var indexIncrementor = 0; + item.data.forEach(function (entry) { + if (requestPasteFromClipboard(createIndex + indexIncrementor, entry)) { + indexIncrementor++; + } + }); + } else { + requestPasteFromClipboard(createIndex, item.data); + } + vm.blockTypePicker.close(); + }, submit: function (model) { if (model && model.selectedItem) { addNewBlock(createIndex, model.selectedItem.alias); @@ -220,12 +274,97 @@ } }; + vm.blockTypePicker.pasteItems = []; + + var singleEntriesForPaste = clipboardService.retriveEntriesOfType("elementType", vm.availableContentTypes); + singleEntriesForPaste.forEach(function (entry) { + vm.blockTypePicker.pasteItems.push({ + type: "elementType", + name: entry.label, + data: entry.data, + icon: entry.icon + }); + }); + + var arrayEntriesForPaste = clipboardService.retriveEntriesOfType("elementTypeArray", vm.availableContentTypes); + arrayEntriesForPaste.forEach(function (entry) { + vm.blockTypePicker.pasteItems.push({ + type: "elementTypeArray", + name: entry.label, + data: entry.data, + icon: entry.icon + }); + }); + + vm.blockTypePicker.title = vm.blockTypePicker.pasteItems.length > 0 ? labels.grid_addElement : labels.content_createEmpty; + + vm.blockTypePicker.clickClearPaste = function ($event) { + $event.stopPropagation(); + $event.preventDefault(); + clipboardService.clearEntriesOfType("elementType", vm.availableContentTypes); + clipboardService.clearEntriesOfType("elementTypeArray", vm.availableContentTypes); + vm.blockTypePicker.pasteItems = [];// This dialog is not connected via the clipboardService events, so we need to update manually. + }; + + vm.blockTypePicker.show = true; + }; function requestCopyBlock(block) { - console.log("copy still needs to be done.") + clipboardService.copy("elementTypeArray", block.content.contentTypeAlias, block.content, block.label); } - function requestDeleteBlock (block) { + + var requestCopyAllBlocks = function() { + + // list aliases + var aliases = vm.blocks.map(block => block.content.contentTypeAlias); + + // remove dublicates + aliases = aliases.filter((item, index) => aliases.indexOf(item) === index); + + var contentNodeName = ""; + if(vm.umbVariantContent) { + contentNodeName = vm.umbVariantContent.editor.content.name; + } + + var elementTypesToCopy = vm.blocks.map(block => block.content); + + localizationService.localize("clipboard_labelForArrayOfItemsFrom", [vm.model.label, contentNodeName]).then(function(localizedLabel) { + clipboardService.copyArray("elementTypeArray", aliases, elementTypesToCopy, localizedLabel, "icon-thumbnail-list", vm.model.id); + }); + } + function requestCopyBlock(block) { + clipboardService.copy("elementType", block.content.contentTypeAlias, block.content, block.label); + } + function requestPasteFromClipboard(index, pasteEntry) { + + if (pasteEntry === undefined) { + return false; + } + + var layoutEntry = modelObject.createFromElementType(pasteEntry); + if (layoutEntry === null) { + return false; + } + + // make editing object + var blockEditingObject = getEditingModel(layoutEntry); + if (blockEditingObject === null) { + return false; + } + + // add layout entry at the decired location in layout. + vm.layout.splice(index, 0, layoutEntry); + + // apply editing model at decired location in editing model. + vm.blocks.splice(index, 0, blockEditingObject); + + vm.moveFocusToBlock = blockEditingObject; + + return true; + + } + function requestDeleteBlock(block) { localizationService.localizeMany(["general_delete", "blockEditor_confirmDeleteBlockMessage", "contentTypeEditor_yesDelete"]).then(function (data) { const overlay = { title: data[0], @@ -243,8 +382,22 @@ overlayService.confirmDelete(overlay); }); } + function requestDeleteAllBlocks() { + localizationService.localizeMany(["content_nestedContentDeleteAllItems", "general_delete"]).then(function (data) { + overlayService.confirmDelete({ + title: data[1], + content: data[0], + close: function () { + overlayService.close(); + }, + submit: function () { + deleteAllBlocks(); + overlayService.close(); + } + }); + }); + } - vm.showCopy = clipboardService.isSupported(); var runtimeSortVars = {}; @@ -289,12 +442,17 @@ editBlock: editBlock, requestCopyBlock: requestCopyBlock, requestDeleteBlock: requestDeleteBlock, - deleteBlock: deleteBlock, - syncBlockData: syncBlockData + deleteBlock: deleteBlock } - function validateLimits() { + function onAmountOfBlocksChanged() { + + // enable/disable property actions + copyAllBlocksAction.isDisabled = vm.blocks.length === 0; + deleteAllBlocksAction.isDisabled = vm.blocks.length === 0; + + // validate limits: if (vm.validationLimit.min !== null) { if (vm.blocks.length < vm.validationLimit.min) { vm.propertyForm.minCount.$setValidity("minCount", false); @@ -314,24 +472,8 @@ - // TODO: We need to investigate if we can do a specific watch on each block, so we dont re-render all blocks. + unsubscribe.push($scope.$watch(() => vm.blocks.length, onAmountOfBlocksChanged)); /* - unsubscribe.push($scope.$watch("vm.blocks[0]", onBlocksUpdated, true)); - function onBlocksUpdated(newVal, oldVal) { - - console.log("blocks update", oldVal, " > ", newVal); - //setDirty(); - - var labelIndex = 1; - for(const block of vm.blocks) { - block.label = blockEditorService.getBlockLabel(block, labelIndex++); - } - } - */ - - - unsubscribe.push($scope.$watch(() => vm.blocks.length, validateLimits)); -/* unsubscribe.push($scope.$on("formSubmitting", function (ev, args) { console.log("formSubmitting is happening, we need to make sure sub property editors are synced first.") @@ -340,7 +482,7 @@ //sync(); })); -*/ + */ $scope.$on("$destroy", function () { for (const subscription of unsubscribe) { subscription(); From beda2fdf72eefb8d8149e90374704a853e365009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 4 Mar 2020 13:32:58 +0100 Subject: [PATCH 060/508] order var declarations --- .../propertyeditors/blocklist/blocklist.component.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 157d31b9cf..3e04e62b45 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -19,8 +19,14 @@ function BlockListController($scope, $interpolate, editorService, clipboardService, localizationService, overlayService, blockEditorService, contentResource, eventsService) { - var modelObject; var unsubscribe = []; + var modelObject; + + // Property actions: + var copyAllBlocksAction; + var deleteAllBlocksAction; + + var vm = this; vm.moveFocusToBlock = null; @@ -44,8 +50,6 @@ eventsService.on("clipboardService.storageUpdate", checkAbilityToPasteContent); - var copyAllBlocksAction; - var deleteAllBlocksAction; vm.$onInit = function() { From 5c47352b0e73c9fc3ae8064291331d0ac51d3e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 4 Mar 2020 13:34:03 +0100 Subject: [PATCH 061/508] remove unused paste function --- .../propertyeditors/blocklist/blocklist.component.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 3e04e62b45..fcb2bc926c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -21,7 +21,7 @@ var unsubscribe = []; var modelObject; - + // Property actions: var copyAllBlocksAction; var deleteAllBlocksAction; @@ -31,7 +31,6 @@ vm.moveFocusToBlock = null; vm.showCopy = clipboardService.isSupported(); - vm.showPaste = false; vm.layout = [];// Property models layout object specific to this Block Editor. vm.blocks = [];// Runtime model of editing models, needs to be synced to property model on form submit. @@ -44,10 +43,6 @@ labels.content_createEmpty = data[1]; }); - function checkAbilityToPasteContent() { - vm.showPaste = clipboardService.hasEntriesOfType("elementType", vm.availableContentTypes) || clipboardService.hasEntriesOfType("elementTypeArray", vm.availableContentTypes); - } - eventsService.on("clipboardService.storageUpdate", checkAbilityToPasteContent); @@ -103,8 +98,6 @@ vm.availableContentTypes = modelObject.getAvailableAliasesForBlockContent(); vm.availableBlockTypes = modelObject.getAvailableBlocksForItemPicker(); - checkAbilityToPasteContent(); - } From 0c2a03bd46bd97d6db8a4b48337be5ecb0fc20bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 4 Mar 2020 13:44:53 +0100 Subject: [PATCH 062/508] block list editor better naming --- .../common/services/blockeditor.service.js | 44 +++++---- .../blocklist/blocklist.component.js | 91 ++++++------------- 2 files changed, 49 insertions(+), 86 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index 00308ae4ff..675746dca4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -134,10 +134,11 @@ }, /** - * Retrieve editing model of a layout entry + * Retrieve editor friendly model of a block. + * @param {Object} layoutEntry the layout entry to build the block model from. * @return {Object} Scaffolded Block Content object. */ - getEditingModel: function(layoutEntry) { + getBlockModel: function(layoutEntry) { var udi = layoutEntry.udi; @@ -150,9 +151,9 @@ return null; } - var editingModel = {}; - editingModel.config = angular.copy(blockConfiguration); - editingModel.labelInterpolator = $interpolate(editingModel.config.label); + var blockModel = {}; + blockModel.config = angular.copy(blockConfiguration); + blockModel.labelInterpolator = $interpolate(blockModel.config.label); var scaffold = this.getScaffoldFor(blockConfiguration.contentTypeAlias); if(scaffold === null) { @@ -160,30 +161,30 @@ } // make basics from scaffold - editingModel.content = angular.copy(scaffold); - editingModel.content.udi = udi; + blockModel.content = angular.copy(scaffold); + blockModel.content.udi = udi; - mapToElementTypeModel(editingModel.content, contentModel); + mapToElementTypeModel(blockModel.content, contentModel); - editingModel.contentModel = contentModel; - editingModel.layoutModel = layoutEntry; + blockModel.contentModel = contentModel; + blockModel.layoutModel = layoutEntry; // TODO: settings - return editingModel; + return blockModel; }, /** - * Retrieve editing model of a layout entry + * Retrieve block model of a layout entry * @return {Object} Scaffolded Block Content object. */ - setDataFromEditingModel: function(editingModel) { + setDataFromBlockModel: function(blockModel) { - var udi = editingModel.content.key; + var udi = blockModel.content.key; - mapToPropertyModel(editingModel.content, editingModel.contentModel); + mapToPropertyModel(blockModel.content, blockModel.contentModel); // TODO: sync settings to layout entry. @@ -269,9 +270,6 @@ mapToPropertyModel(elementTypeContentModel, contentModel); - console.log(elementTypeContentModel) - console.log(contentModel) - return layoutEntry; } @@ -281,24 +279,24 @@ createModelObject: function(propertyModelValue, propertyEditorAlias, blockConfigurations) { return new BlockEditorModelObject(propertyModelValue, propertyEditorAlias, blockConfigurations); }, - getBlockLabel: function(blockModelObject) { + getBlockLabel: function(blockModel) { // TODO: we should do something about this for performance. var vars = new Object(); - var variant = blockModelObject.content.variants[0]; + var variant = blockModel.content.variants[0]; var tab = variant.tabs[0]; // TODO: need to look up all tabs... for(const property of tab.properties) { vars[property.alias] = property.value; } - if(blockModelObject.labelInterpolator) { - return blockModelObject.labelInterpolator(vars); + if(blockModel.labelInterpolator) { + return blockModel.labelInterpolator(vars); } - return blockModelObject.contentTypeName; + return blockModel.contentTypeName; } } } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index fcb2bc926c..821a9362fa 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -32,8 +32,8 @@ vm.moveFocusToBlock = null; vm.showCopy = clipboardService.isSupported(); - vm.layout = [];// Property models layout object specific to this Block Editor. - vm.blocks = [];// Runtime model of editing models, needs to be synced to property model on form submit. + vm.layout = [];// The layout object specific to this Block Editor, will be a direct reference from Property Model. + vm.blocks = [];// Runtime list of block models, needs to be synced to property model on form submit. vm.availableBlockTypes = [];// Available block entries of this property editor. var labels = {}; @@ -93,16 +93,25 @@ function loaded() { vm.layout = modelObject.getLayout(); - mapToBlocks(); + + // maps layout entries to editor friendly models. + vm.layout.forEach(entry => { + var block = getBlockModel(entry); + if(block !== null) { + vm.blocks.push(block); + } + }); vm.availableContentTypes = modelObject.getAvailableAliasesForBlockContent(); vm.availableBlockTypes = modelObject.getAvailableBlocksForItemPicker(); + $scope.$apply(); + } - function getEditingModel(entry) { - var block = modelObject.getEditingModel(entry); + function getBlockModel(entry) { + var block = modelObject.getBlockModel(entry); if (block === null) return null; @@ -111,50 +120,6 @@ return block; } - /** - * Maps property model to the runtime editing model (blocks). - */ - function mapToBlocks() { - // clear blocks. - vm.blocks = []; - - // make all blocks. - vm.layout.forEach(entry => { - var block = getEditingModel(entry); - if(block !== null) { - vm.blocks.push(block); - } - }); - - $scope.$apply(); - } - - /** - * Maps content from runtime editing model (blocks) to the property model. - * Does not take care of ordering, we need the sort-UI to sync that, on the fly. - */ - /* - function mapToContent() { - - // sync data from blocks to content models. - vm.blocks.forEach(block => { - modelObject.setDataFromEditingModel(block); - }); - } - */ - - /* - function sync() { - // to avoid deep watches of block editors we use an event for those instead? - // Lets inform container of this property editor that we updated. - $scope.$emit("blockEditorValueUpdated"); - } - */ - /* - function syncBlockData(block) { - modelObject.setDataFromEditingModel(block); - } - */ function addNewBlock(index, contentTypeAlias) { @@ -164,19 +129,19 @@ return false; } - // make editing object - var blockEditingObject = getEditingModel(layoutEntry); - if (blockEditingObject === null) { + // make block model + var blockModel = getBlockModel(layoutEntry); + if (blockModel === null) { return false; } // add layout entry at the decired location in layout. vm.layout.splice(index, 0, layoutEntry); - // apply editing model at decired location in editing model. - vm.blocks.splice(index, 0, blockEditingObject); + // apply block model at decired location in blocks. + vm.blocks.splice(index, 0, blockModel); - vm.moveFocusToBlock = blockEditingObject; + vm.moveFocusToBlock = blockModel; return true; @@ -215,7 +180,7 @@ submit: function(elementEditorModel) { blockModel.content = elementEditorModel.content; // TODO, investigate if we need to call a sync, for this scenario to work.. Concern is regarding wether the property-value watcher will pick this up. - //modelObject.setDataFromEditingModel(block); + //modelObject.setDataFromBlockModel(block); editorService.close(); }, close: function() { @@ -344,19 +309,19 @@ return false; } - // make editing object - var blockEditingObject = getEditingModel(layoutEntry); - if (blockEditingObject === null) { + // make block model + var blockModel = getBlockModel(layoutEntry); + if (blockModel === null) { return false; } - // add layout entry at the decired location in layout. + // insert layout entry at the decired location in layout. vm.layout.splice(index, 0, layoutEntry); - // apply editing model at decired location in editing model. - vm.blocks.splice(index, 0, blockEditingObject); + // insert block model at the decired location in blocks. + vm.blocks.splice(index, 0, blockModel); - vm.moveFocusToBlock = blockEditingObject; + vm.moveFocusToBlock = blockModel; return true; From eda7c02fab93da3fd5cead03c8c3de1bb07903c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 4 Mar 2020 13:47:45 +0100 Subject: [PATCH 063/508] simpler label generation --- .../src/common/services/blockeditor.service.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index 675746dca4..f8c2c4789a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -280,20 +280,11 @@ return new BlockEditorModelObject(propertyModelValue, propertyEditorAlias, blockConfigurations); }, getBlockLabel: function(blockModel) { - - // TODO: we should do something about this for performance. - var vars = new Object(); - var variant = blockModel.content.variants[0]; - var tab = variant.tabs[0]; - // TODO: need to look up all tabs... - for(const property of tab.properties) { - vars[property.alias] = property.value; - } - if(blockModel.labelInterpolator) { - return blockModel.labelInterpolator(vars); + // We are just using the contentModel, since its a key/value object that is live synced. (if we need to add additional vars, we could make a shallow copy and apply those.) + return blockModel.labelInterpolator(blockModel.contentModel); } return blockModel.contentTypeName; From 51b0c81ead8bafc640a29b68b2ad2e043809235f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 4 Mar 2020 13:52:26 +0100 Subject: [PATCH 064/508] clean up --- .../common/services/blockeditor.service.js | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index f8c2c4789a..e076930b58 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -91,7 +91,7 @@ } }); - // remove dublicates. + // removing dublicates. scaffoldAliases = scaffoldAliases.filter((value, index, self) => self.indexOf(value) === index); scaffoldAliases.forEach((elementTypeAlias => { @@ -142,7 +142,7 @@ var udi = layoutEntry.udi; - var contentModel = this.getContentByUdi(udi); + var contentModel = this._getContentByUdi(udi); var blockConfiguration = this.getBlockConfiguration(contentModel.contentTypeAlias); @@ -214,7 +214,7 @@ } var entry = { - udi: this.createContent(contentTypeAlias) + udi: this._createContent(contentTypeAlias) } if (blockConfiguration.settingsElementTypeAlias != null) { @@ -224,33 +224,6 @@ return entry; }, - getContentByUdi: function(udi) { - return this.value.data.find(entry => entry.udi === udi); - }, - // private - createContent: function(elementTypeAlias) { - var content = { - contentTypeAlias: elementTypeAlias, - udi: udiService.create("element") - }; - this.value.data.push(content); - return content.udi; - }, - // private - removeContent: function(entry) { - const index = this.value.data.indexOf(entry) - if (index !== -1) { - this.value.splice(index, 1); - } - }, - - removeContentByUdi: function(udi) { - const index = this.value.data.findIndex(o => o.udi === udi); - if (index !== -1) { - this.value.splice(index, 1); - } - }, - /** * Insert data from ElementType Model * @return {Object} Layout entry object, to be inserted at a decired location in the layout object. @@ -266,12 +239,40 @@ return null; } - var contentModel = this.getContentByUdi(layoutEntry.udi); + var contentModel = this._getContentByUdi(layoutEntry.udi); mapToPropertyModel(elementTypeContentModel, contentModel); return layoutEntry; + }, + + // private + _createContent: function(elementTypeAlias) { + var content = { + contentTypeAlias: elementTypeAlias, + udi: udiService.create("element") + }; + this.value.data.push(content); + return content.udi; + }, + // private + _getContentByUdi: function(udi) { + return this.value.data.find(entry => entry.udi === udi); + }, + + removeContent: function(entry) { + const index = this.value.data.indexOf(entry) + if (index !== -1) { + this.value.splice(index, 1); + } + }, + + removeContentByUdi: function(udi) { + const index = this.value.data.findIndex(o => o.udi === udi); + if (index !== -1) { + this.value.splice(index, 1); + } } } From e82cbbd5000fdde3cf7568c60690a56b2035498b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 5 Mar 2020 11:54:20 +0100 Subject: [PATCH 065/508] compile config for test mode --- src/Umbraco.Web.UI.Client/gulp/config.js | 4 ++++ src/Umbraco.Web.UI.Client/gulp/modes.js | 12 +++++++++++- src/Umbraco.Web.UI.Client/gulp/tasks/test.js | 1 + src/Umbraco.Web.UI.Client/gulpfile.js | 8 ++++---- src/Umbraco.Web.UI.Client/test/config/karma.conf.js | 7 +++++++ 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/gulp/config.js b/src/Umbraco.Web.UI.Client/gulp/config.js index 92e0b6d21d..a397462fd4 100755 --- a/src/Umbraco.Web.UI.Client/gulp/config.js +++ b/src/Umbraco.Web.UI.Client/gulp/config.js @@ -9,6 +9,10 @@ module.exports = { dev: { sourcemaps: true, embedtemplates: false + }, + test: { + sourcemaps: false, + embedtemplates: true } }, sources: { diff --git a/src/Umbraco.Web.UI.Client/gulp/modes.js b/src/Umbraco.Web.UI.Client/gulp/modes.js index dc2947f2cc..21609cdcf8 100644 --- a/src/Umbraco.Web.UI.Client/gulp/modes.js +++ b/src/Umbraco.Web.UI.Client/gulp/modes.js @@ -10,4 +10,14 @@ function setDevelopmentMode(cb) { return cb(); }; -module.exports = { setDevelopmentMode: setDevelopmentMode }; +function setTestMode(cb) { + + config.compile.current = config.compile.test; + + return cb(); +}; + +module.exports = { + setDevelopmentMode: setDevelopmentMode, + setTestMode: setTestMode + }; diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/test.js b/src/Umbraco.Web.UI.Client/gulp/tasks/test.js index b5239d35e7..255fe17435 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/test.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/test.js @@ -23,6 +23,7 @@ function runUnitTestServer() { autoWatch: true, port: 9999, singleRun: false, + browsers: ['ChromeDebugging'], keepalive: true }) .start(); diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js index ec9b7bc508..542d45c479 100644 --- a/src/Umbraco.Web.UI.Client/gulpfile.js +++ b/src/Umbraco.Web.UI.Client/gulpfile.js @@ -13,7 +13,7 @@ const { src, dest, series, parallel, lastRun } = require('gulp'); const config = require('./gulp/config'); -const { setDevelopmentMode } = require('./gulp/modes'); +const { setDevelopmentMode, setTestMode } = require('./gulp/modes'); const { dependencies } = require('./gulp/tasks/dependencies'); const { js } = require('./gulp/tasks/js'); const { less } = require('./gulp/tasks/less'); @@ -31,6 +31,6 @@ exports.build = series(parallel(dependencies, js, less, views), testUnit); exports.dev = series(setDevelopmentMode, parallel(dependencies, js, less, views), watchTask); exports.watch = series(watchTask); // -exports.runTests = series(js, testUnit); -exports.runUnit = series(js, runUnitTestServer, watchTask); -exports.testE2e = series(testE2e); +exports.runTests = series(setTestMode, parallel(js, testUnit)); +exports.runUnit = series(setTestMode, parallel(js, runUnitTestServer), watchTask); +exports.testE2e = series(setTestMode, parallel(testE2e)); diff --git a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js index 501e43b043..f6fc5ca1be 100644 --- a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js +++ b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js @@ -103,6 +103,13 @@ module.exports = function (config) { // CLI --browsers Chrome,Firefox,Safari browsers: ['ChromeHeadless'], + customLaunchers: { + ChromeDebugging: { + base: 'Chrome', + flags: ['--remote-debugging-port=9333'] + } + }, + // allow waiting a bit longer, some machines require this browserNoActivityTimeout: 100000, // default 10,000ms From 07bca56c5525839f65cc75ac27f3b71822c90d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 5 Mar 2020 11:54:36 +0100 Subject: [PATCH 066/508] Chrome Debug for VS code --- src/.vscode/launch.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/.vscode/launch.json diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json new file mode 100644 index 0000000000..cd07033f8f --- /dev/null +++ b/src/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "attach", + "name": "Attach Karma Chrome against localhost", + "address": "127.0.0.1", + "port": 9333, + "pathMapping": { + "/": "${workspaceRoot}", + "/base/": "${workspaceRoot}" + } + } + ] +} From d01d55b621a159c6eb04808386b114a7ce6e81b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 5 Mar 2020 11:55:37 +0100 Subject: [PATCH 067/508] promise test working --- .../src/common/mocks/resources/_utils.js | 1 + .../mocks/resources/variantcontent.mocks.js | 42 +++++++++---------- .../common/services/blockeditor.service.js | 4 +- .../blocklist/blocklist.component.js | 2 +- .../services/block-editor-service.spec.js | 39 ++++++++--------- 5 files changed, 43 insertions(+), 45 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js index cf73e6a8ce..49a90f273e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js @@ -295,6 +295,7 @@ angular.module('umbraco.mocks'). updater: { name: "Per Ploug Krogslund", id: 1 }, path: "-1,1234,2455", allowedActions: ["U", "H", "A"], + contentTypeAlias: "testAlias", variants: [ { name: "", diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/variantcontent.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/variantcontent.mocks.js index 1ff2d3a0a7..9b5dcf5ad6 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/variantcontent.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/variantcontent.mocks.js @@ -1,31 +1,31 @@ angular.module('umbraco.mocks'). - factory('variantContentMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { - 'use strict'; - - function returnEmptyVariantNode(status, data, headers) { + factory('variantContentMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { + 'use strict'; - if (!mocksUtils.checkAuth()) { - return [401, null, null]; - } + function returnEmptyVariantNode(status, data, headers) { - var response = returnVariantNodebyId(200, "", null); - var node = response[1]; - var parentId = mocksUtils.getParameterByName(data, "parentId") || 1234; + if (!mocksUtils.checkAuth()) { + return [401, null, null]; + } - node.name = ""; - node.id = 0; - node.parentId = parentId; + var response = returnVariantNodebyId(200, "", null); + var node = response[1]; + var parentId = mocksUtils.getParameterByName(data, "parentId") || 1234; - $(node.tabs).each(function(i,tab){ - $(tab.properties).each(function(i, property){ - property.value = ""; - }); - }); + node.name = ""; + node.id = 0; + node.parentId = parentId; - return response; - } + node.tabs.forEach(function(tab){ + tab.properties.forEach(function( property){ + property.value = ""; + }); + }); - function returnVariantNodebyId(status, data, headers) { + return response; + } + + function returnVariantNodebyId(status, data, headers) { if (!mocksUtils.checkAuth()) { return [401, null, null]; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index e076930b58..9d0a695d02 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -2,7 +2,7 @@ 'use strict'; - function blockEditorService($interpolate, udiService) { + function blockEditorService($interpolate, udiService, contentResource) { /** @@ -79,7 +79,7 @@ return this.blockConfigurations.find(bc => bc.contentTypeAlias === alias); }, - loadScaffolding: function(contentResource) { + loadScaffolding: function() { var tasks = []; var scaffoldAliases = []; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 821a9362fa..617f92350d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -54,7 +54,7 @@ vm.model.value = vm.model.value || {}; modelObject = blockEditorService.createModelObject(vm.model.value, vm.model.editor, vm.model.config.blocks); - modelObject.loadScaffolding(contentResource).then(loaded); + modelObject.loadScaffolding().then(loaded); copyAllBlocksAction = { labelKey: "clipboard_labelForCopyAllEntries", diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js index 5c48b4a8dc..b656d5e56e 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js @@ -1,33 +1,34 @@ describe('blockEditorService tests', function () { - var blockEditorService, $rootScope, $httpBackend, varaintMocks, contentResource; + var blockEditorService, contentResource; beforeEach(module('umbraco.services')); + beforeEach(module('umbraco.resources')); beforeEach(module('umbraco.mocks')); beforeEach(inject(function ($injector, mocksUtils) { mocksUtils.disableAuth(); - blockEditorService = $injector.get('blockEditorService'); - $rootScope = $injector.get('$rootScope'); - $httpBackend = $injector.get('$httpBackend'); - varaintMocks = $injector.get("variantContentMocks"); - varaintMocks.register(); - contentResource = $injector.get('contentResource'); + contentResource = $injector.get("contentResource"); + spyOn(contentResource, "getScaffold").and.callFake( + function () { + return Promise.resolve(mocksUtils.getMockVariantContent(1234)) + } + ); + + blockEditorService = $injector.get('blockEditorService'); })); - var simpleBlockConfigurationMock = {contentTypeAlias: "testAlias", label:"Test", settingsElementTypeAlias: null, view: "testview.html"}; - describe('init blockEditoModelObject', function () { it('fail if no model value', function () { function createWithNoModelValue() { blockEditorService.createModelObject(null, "test", []); - } + } expect(createWithNoModelValue).toThrow(); }); @@ -44,23 +45,19 @@ expect(modelObject.getBlockConfiguration(simpleBlockConfigurationMock.contentTypeAlias).label).toBe(simpleBlockConfigurationMock.label); }); - it('loadScaffolding provides data for itemPicker', function () { + it('loadScaffolding provides data for itemPicker', function (done) { var modelObject = blockEditorService.createModelObject({}, "test", [simpleBlockConfigurationMock]); - var itemPickerOptions; - - var pendingPromise = modelObject.loadScaffolding(contentResource).then(() => { - itemPickerOptions = modelObject.getAvailableBlocksForItemPicker(); + var pendingPromise = modelObject.loadScaffolding().then(() => { + var itemPickerOptions = modelObject.getAvailableBlocksForItemPicker(); + expect(itemPickerOptions.length).toBe(1); + expect(itemPickerOptions[0].alias).toBe(simpleBlockConfigurationMock.contentTypeAlias); + done(); }); - - $rootScope.$digest(); - $httpBackend.flush(); - - expect(itemPickerOptions.length).toBe(1); - }); + }); }); From c4965dad24220a782d190afe3a21347923536b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 5 Mar 2020 11:56:23 +0100 Subject: [PATCH 068/508] space change --- .../src/common/mocks/resources/variantcontent.mocks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/variantcontent.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/variantcontent.mocks.js index 9b5dcf5ad6..3a434bdadc 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/variantcontent.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/variantcontent.mocks.js @@ -16,8 +16,8 @@ angular.module('umbraco.mocks'). node.id = 0; node.parentId = parentId; - node.tabs.forEach(function(tab){ - tab.properties.forEach(function( property){ + node.tabs.forEach(function(tab) { + tab.properties.forEach(function(property) { property.value = ""; }); }); From 74165ae8596ba09dfc8f6e7aa9c472fbe9afe215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 5 Mar 2020 13:14:51 +0100 Subject: [PATCH 069/508] another two tests --- .../services/block-editor-service.spec.js | 74 +++++++++++++++++-- 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js index b656d5e56e..6bfc7aea34 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js @@ -21,37 +21,95 @@ })); - var simpleBlockConfigurationMock = {contentTypeAlias: "testAlias", label:"Test", settingsElementTypeAlias: null, view: "testview.html"}; + + var blockConfigurationMock = {contentTypeAlias: "testAlias", label:"Test label", settingsElementTypeAlias: null, view: "testview.html"}; + + var propertyModelMock = { + layout: { + "Umbraco.TestBlockEditor": [ + { + udi: 1234 + } + ] + }, + data: [ + { + udi: 1234, + contentTypeAlias: "testAlias", + testvalue: "myTestValue" + } + ] + }; describe('init blockEditoModelObject', function () { it('fail if no model value', function () { function createWithNoModelValue() { - blockEditorService.createModelObject(null, "test", []); + blockEditorService.createModelObject(null, "Umbraco.TestBlockEditor", []); } expect(createWithNoModelValue).toThrow(); }); it('return a object, with methods', function () { - var modelObject = blockEditorService.createModelObject({}, "test", []); + var modelObject = blockEditorService.createModelObject({}, "Umbraco.TestBlockEditor", []); expect(modelObject).not.toBeUndefined(); expect(modelObject.loadScaffolding).not.toBeUndefined(); }); it('getBlockConfiguration provide the requested block configurtion', function () { - var modelObject = blockEditorService.createModelObject({}, "test", [simpleBlockConfigurationMock]); + var modelObject = blockEditorService.createModelObject({}, "Umbraco.TestBlockEditor", [blockConfigurationMock]); - expect(modelObject.getBlockConfiguration(simpleBlockConfigurationMock.contentTypeAlias).label).toBe(simpleBlockConfigurationMock.label); + expect(modelObject.getBlockConfiguration(blockConfigurationMock.contentTypeAlias).label).toBe(blockConfigurationMock.label); }); it('loadScaffolding provides data for itemPicker', function (done) { - var modelObject = blockEditorService.createModelObject({}, "test", [simpleBlockConfigurationMock]); + var modelObject = blockEditorService.createModelObject({}, "Umbraco.TestBlockEditor", [blockConfigurationMock]); - var pendingPromise = modelObject.loadScaffolding().then(() => { + modelObject.loadScaffolding().then(() => { var itemPickerOptions = modelObject.getAvailableBlocksForItemPicker(); expect(itemPickerOptions.length).toBe(1); - expect(itemPickerOptions[0].alias).toBe(simpleBlockConfigurationMock.contentTypeAlias); + expect(itemPickerOptions[0].alias).toBe(blockConfigurationMock.contentTypeAlias); + done(); + }); + + }); + + it('getLayoutEntry has right values', function (done) { + + + var modelObject = blockEditorService.createModelObject(propertyModelMock, "Umbraco.TestBlockEditor", [blockConfigurationMock]); + + modelObject.loadScaffolding().then(() => { + + var layout = modelObject.getLayout(); + + expect(layout).not.toBeUndefined(); + expect(layout.length).toBe(1); + expect(layout[0]).toBe(propertyModelMock.layout["Umbraco.TestBlockEditor"][0]); + expect(layout[0].udi).toBe(propertyModelMock.layout["Umbraco.TestBlockEditor"][0].udi); + + done(); + }); + + }); + + it('getBlockModel provide value', function (done) { + + + var modelObject = blockEditorService.createModelObject(propertyModelMock, "Umbraco.TestBlockEditor", [blockConfigurationMock]); + + modelObject.loadScaffolding().then(() => { + + var layout = modelObject.getLayout(); + expect(layout).not.toBeUndefined(); + + var blockModel = modelObject.getBlockModel(layout[0]); + + expect(blockModel).not.toBeUndefined(); + expect(blockModel[0].udi).toBe(propertyModelMock.data[0].udi); + expect(blockModel[0].testvalue).toBe(propertyModelMock.data[0].testvalue); + done(); }); From bc97861316b383a27cd01982c2ba8b464874bbec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 5 Mar 2020 17:38:14 +0100 Subject: [PATCH 070/508] more tests for block list editor --- .../src/common/mocks/resources/_utils.js | 1 + .../blockeditor.block.component.html} | 0 .../blockeditor.block.component.js} | 31 ++----- .../blocklist/blocklist.component.html | 4 +- .../blocklist/blocklist.component.js | 10 +-- .../blocklist/blocklist.component.less | 19 +++-- .../services/block-editor-service.spec.js | 83 +++++++++++++++++-- 7 files changed, 101 insertions(+), 47 deletions(-) rename src/Umbraco.Web.UI.Client/src/views/propertyeditors/{blocklist/blocklist.block.component.html => blockeditor/blockeditor.block.component.html} (100%) rename src/Umbraco.Web.UI.Client/src/views/propertyeditors/{blocklist/blocklist.block.component.js => blockeditor/blockeditor.block.component.js} (51%) diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js index 49a90f273e..fc28567ea3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js @@ -313,6 +313,7 @@ angular.module('umbraco.mocks'). label: "Content", id: 2, properties: [ + { alias: "testproperty", label: "Test property", view: "textbox", value: "asdfghjk" }, { alias: "valTest", label: "Validation test", view: "validationtest", value: "asdfasdf" }, { alias: "bodyText", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, { alias: "textarea", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.html similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.html rename to src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.html diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.js similarity index 51% rename from src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js rename to src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.js index 330d8f7b69..c3397e9433 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.js @@ -2,10 +2,10 @@ "use strict"; angular .module("umbraco") - .component("blockListPropertyEditorBlock", { - templateUrl: "views/propertyeditors/blocklist/blocklist.block.component.html", + .component("blockEditorBlock", { + templateUrl: "views/propertyeditors/blockeditor/blockeditor.block.component.html", transclude: true, - controller: BlockListBlockController, + controller: BlockEditorBlockBlockController, controllerAs: "vm", bindings: { block: "=", @@ -16,7 +16,7 @@ } }); - function BlockListBlockController($scope, blockEditorService) { + function BlockEditorBlockBlockController($scope, blockEditorService) { var unsubscribe = []; var vm = this; @@ -32,9 +32,10 @@ for (var p = 0; p < tab.properties.length; p++) { var prop = tab.properties[p]; - // Sadly we need to deep watch, cause its our only way to make sure that complex values gets synced. Alternative solution would be to sync on a broadcasted event, fired on Save and Copy eventually more. - // But to minimize the watch we only watch the value of properties. But because we are deep watching it means that we are watching everything of nested block editors, so this would only have a performance improvement for first levels of block editors. - // New thoughts, since the value of a property editors is just a pointer (if not primative) then we could properly live without deep watching? cause they reference the same?.. Lets investigate.. + // Watch value of property since this is the only value we want to keep synced. + // Do notice that it is not performing a deep watch, meaning that we are only watching primatives and changes directly to the object of property-value. + // But we like to sync non-primative values as well! Yes, and this does happen, just not through this code, but through the nature of JavaScript. + // Non-primative values act as references to the same data and are therefor synced. unsubscribe.push($scope.$watch("vm.block.content.variants[0].tabs["+t+"].properties["+p+"].value", createPropWatcher(prop))); } } @@ -47,8 +48,6 @@ // sync data: vm.block.contentModel[prop.alias] = prop.value; - //vm.blockEditorApi.sync(); - // update label: updateLabel(); } @@ -58,20 +57,6 @@ function updateLabel() { vm.block.label = blockEditorService.getBlockLabel(vm.block); } - - /** - * Listening for properties - */ - /* - function onBlockEditorValueUpdated($event) { - // Lets sync the value of the property that the event comes from, if we know that.. - - //$event.stopPropagation(); - //$event.preventDefault(); - }; - - unsubscribe.push($scope.$on("blockEditorValueUpdated", onBlockEditorValueUpdated)); - */ $scope.$on("$destroy", function () { for (const subscription of unsubscribe) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index 3761b42561..2004becba7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -14,7 +14,7 @@ > - +
    @@ -32,7 +32,7 @@
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 617f92350d..9904a0132b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -54,7 +54,7 @@ vm.model.value = vm.model.value || {}; modelObject = blockEditorService.createModelObject(vm.model.value, vm.model.editor, vm.model.config.blocks); - modelObject.loadScaffolding().then(loaded); + modelObject.loadScaffolding().then(onLoaded); copyAllBlocksAction = { labelKey: "clipboard_labelForCopyAllEntries", @@ -90,7 +90,7 @@ } } - function loaded() { + function onLoaded() { vm.layout = modelObject.getLayout(); @@ -105,7 +105,7 @@ vm.availableContentTypes = modelObject.getAvailableAliasesForBlockContent(); vm.availableBlockTypes = modelObject.getAvailableBlocksForItemPicker(); - $scope.$apply(); + $scope.$evalAsync(); } @@ -375,7 +375,7 @@ scroll: true, start: function (ev, ui) { runtimeSortVars.moveFromIndex = ui.item.index(); - $scope.$apply(function () { + $scope.$evalAsync(function () { vm.sorting = true; }); }, @@ -394,7 +394,7 @@ vm.layout.splice(moveToIndex, 0, movedEntry); } - $scope.$apply(function () { + $scope.$evalAsync(function () { vm.sorting = false; }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less index 02670e88b0..2ab2c37d3b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less @@ -16,25 +16,25 @@ position: relative; width: 100%; - .umb-block-list__block--head { + /*.umb-block-list__block--head { opacity: 0; transition: opacity 120ms; - } - .umb-block-list__block--actions { + }*/ + > ng-transclude > .umb-block-list__block--actions { opacity: 0; transition: opacity 120ms; } &:hover, &:focus, &:focus-within { - .umb-block-list__block--head { + /*.umb-block-list__block--head { opacity: 1; - } + }*/ - .umb-block-list__block--actions { + > ng-transclude > .umb-block-list__block--actions { opacity: 1; } } - + /* &:focus, &:focus-within { .umb-block-list__block--head { &::before { @@ -42,8 +42,9 @@ } } } + */ } - +/* .umb-block-list__block--head { position: absolute; top: 0; @@ -77,7 +78,7 @@ label.umb-block-list__block--head { cursor: grab; } - +*/ .umb-block-list__block--actions { position: absolute; top: 10px; diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js index 6bfc7aea34..b51ba1f9ee 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js @@ -1,15 +1,19 @@ describe('blockEditorService tests', function () { - var blockEditorService, contentResource; + var blockEditorService, contentResource, $rootScope, $componentController; beforeEach(module('umbraco.services')); beforeEach(module('umbraco.resources')); beforeEach(module('umbraco.mocks')); + beforeEach(module('umbraco')); - beforeEach(inject(function ($injector, mocksUtils) { + beforeEach(inject(function ($injector, mocksUtils, _$rootScope_, _$componentController_) { mocksUtils.disableAuth(); + $rootScope = _$rootScope_; + $componentController = _$componentController_; + contentResource = $injector.get("contentResource"); spyOn(contentResource, "getScaffold").and.callFake( function () { @@ -36,7 +40,7 @@ { udi: 1234, contentTypeAlias: "testAlias", - testvalue: "myTestValue" + testproperty: "myTestValue" } ] }; @@ -75,7 +79,7 @@ }); - it('getLayoutEntry has right values', function (done) { + it('getLayoutEntry has values', function (done) { var modelObject = blockEditorService.createModelObject(propertyModelMock, "Umbraco.TestBlockEditor", [blockConfigurationMock]); @@ -94,7 +98,7 @@ }); - it('getBlockModel provide value', function (done) { + it('getBlockModel has values', function (done) { var modelObject = blockEditorService.createModelObject(propertyModelMock, "Umbraco.TestBlockEditor", [blockConfigurationMock]); @@ -102,13 +106,76 @@ modelObject.loadScaffolding().then(() => { var layout = modelObject.getLayout(); - expect(layout).not.toBeUndefined(); var blockModel = modelObject.getBlockModel(layout[0]); expect(blockModel).not.toBeUndefined(); - expect(blockModel[0].udi).toBe(propertyModelMock.data[0].udi); - expect(blockModel[0].testvalue).toBe(propertyModelMock.data[0].testvalue); + expect(blockModel.contentModel.udi).toBe(propertyModelMock.data[0].udi); + expect(blockModel.content.variants[0].tabs[0].properties[0].value).toBe(propertyModelMock.data[0].testproperty); + + done(); + }); + + }); + + + it('getBlockModel syncs primative values', function (done) { + + var propertyModel = angular.copy(propertyModelMock); + + var modelObject = blockEditorService.createModelObject(propertyModel, "Umbraco.TestBlockEditor", [blockConfigurationMock]); + + modelObject.loadScaffolding().then(() => { + + var layout = modelObject.getLayout(); + + var blockModel = modelObject.getBlockModel(layout[0]); + + blockEditorBlockComponenet = $componentController("blockEditorBlock", null, {"block": blockModel, "blockEditorApi": {}, "class": "testClass"}); + blockEditorBlockComponenet.$onInit(); + + blockModel.content.variants[0].tabs[0].properties[0].value = "anotherTestValue"; + + $rootScope.$digest();// invoke angularJS Store. + + expect(blockModel.contentModel).toBe(propertyModel.data[0]); + expect(blockModel.contentModel.testproperty).toBe("anotherTestValue"); + expect(propertyModel.data[0].testproperty).toBe("anotherTestValue"); + + // + + done(); + }); + + }); + + + it('getBlockModel syncs values of object', function (done) { + + var propertyModel = angular.copy(propertyModelMock); + + var complexValue = {"list": ["A", "B", "C"]}; + propertyModel.data[0].testproperty = complexValue; + + + var modelObject = blockEditorService.createModelObject(propertyModel, "Umbraco.TestBlockEditor", [blockConfigurationMock]); + + modelObject.loadScaffolding().then(() => { + + var layout = modelObject.getLayout(); + + var blockModel = modelObject.getBlockModel(layout[0]); + + blockEditorBlockComponenet = $componentController("blockEditorBlock", null, {"block": blockModel, "blockEditorApi": {}, "class": "testClass"}); + blockEditorBlockComponenet.$onInit(); + + blockModel.content.variants[0].tabs[0].properties[0].value.list[0] = "AA"; + blockModel.content.variants[0].tabs[0].properties[0].value.list.push("D"); + + $rootScope.$digest();// invoke angularJS Store. + + expect(propertyModel.data[0].testproperty.list[0]).toBe("AA"); + expect(propertyModel.data[0].testproperty.list.length).toBe(4); done(); }); From 1715e1747fb8125c6127ae1b2a148e1b3bbb0d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 6 Mar 2020 10:41:22 +0100 Subject: [PATCH 071/508] provide key on blockModel for angularJS performance optimization --- .../src/common/services/blockeditor.service.js | 1 + .../views/propertyeditors/blocklist/blocklist.component.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index 9d0a695d02..0f87634aa6 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -152,6 +152,7 @@ } var blockModel = {}; + blockModel.key = String.CreateGuid(); blockModel.config = angular.copy(blockConfiguration); blockModel.labelInterpolator = $interpolate(blockModel.config.label); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index 2004becba7..05f7fff551 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -5,7 +5,7 @@
    -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html index 56fbe13e2b..cd7468af8d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html @@ -1,10 +1,10 @@
    -
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html index 2036e3ef3a..fecf1ea51e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html @@ -1,4 +1,4 @@ - diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html index 80aba51a84..bbcef1643b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html @@ -8,7 +8,7 @@ ng-model="vm.firstProperty.value" ng-keypress="vm.submitOnEnter($event)" ng-blur="vm.onBlur()" - focus-when="{{blockvm.focusThis}}" + focus-when="{{vm.moveFocusToBlock === block}}" > diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.js index c3397e9433..5945ea9b69 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.js @@ -17,7 +17,7 @@ }); function BlockEditorBlockBlockController($scope, blockEditorService) { - + /* var unsubscribe = []; var vm = this; @@ -63,7 +63,7 @@ subscription(); } }); - + */ } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index 05f7fff551..07a07ee529 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -5,7 +5,7 @@
    -
    +
    - -
    +
    +
    - -
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 93773b1598..b5c42f5cf6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -51,15 +51,13 @@ vm.validationLimit = vm.model.config.validationLimit; - console.log("Model TESTS:", vm.model.value === null, typeof vm.model.value !== 'object') - // We need to ensure that the property model value is an object, this is needed for modelObject to recive a reference and keep that updated. - if(vm.model.value === null || typeof vm.model.value !== 'object') {// testing if we have null or undefined value or if the value is set to another type than Object. + if(typeof vm.model.value !== 'object' || vm.model.value === null) {// testing if we have null or undefined value or if the value is set to another type than Object. vm.model.value = {}; } // Create Model Object, to manage our data for this Block Editor. - modelObject = blockEditorService.createModelObject(vm.model.value, vm.model.editor, vm.model.config.blocks); + modelObject = blockEditorService.createModelObject(vm.model.value, vm.model.editor, vm.model.config.blocks, $scope); modelObject.loadScaffolding().then(onLoaded); copyAllBlocksAction = { @@ -161,16 +159,20 @@ function deleteBlock(block) { + var index = vm.blocks.indexOf(block); if(index !== -1) { - vm.blocks.splice(index, 1); - var layoutIndex = layout.findIndex(entry => entry.udi === block.udi); + var layoutIndex = layout.findIndex(entry => entry.udi === block.content.udi); if(layoutIndex !== -1) { - vm.layout.splice(index, 1); + layout.splice(index, 1); + } else { + throw new Error("Could not find layout entry of block with udi: "+block.content.udi) } - modelObject.removeContentByUdi(block.udi); + vm.blocks.splice(index, 1); + + modelObject.removeDataAndDestroyModel(block); } } @@ -189,6 +191,7 @@ view: "views/common/infiniteeditors/elementeditor/elementeditor.html", size: blockModel.config.overlaySize || "medium", submit: function(elementEditorModel) { + // To ensure syncronization gets tricked we transfer blockEditorService.mapElementTypeValues(elementEditorModel.content, blockModel.content) editorService.close(); }, @@ -397,7 +400,7 @@ var moveFromIndex = runtimeSortVars.moveFromIndex; var moveToIndex = ui.item.index(); - if (moveToIndex > -1 && moveFromIndex !== moveToIndex) { + if (moveToIndex !== -1 && moveFromIndex !== moveToIndex) { var movedEntry = layout[moveFromIndex]; layout.splice(moveFromIndex, 1); layout.splice(moveToIndex, 0, movedEntry); @@ -424,19 +427,19 @@ deleteAllBlocksAction.isDisabled = vm.blocks.length === 0; // validate limits: - if (vm.validationLimit.min !== null) { - if (vm.blocks.length < vm.validationLimit.min) { + if (vm.propertyForm) { + if (vm.validationLimit.min !== null && vm.blocks.length < vm.validationLimit.min) { vm.propertyForm.minCount.$setValidity("minCount", false); } else { vm.propertyForm.minCount.$setValidity("minCount", true); } - } - if (vm.validationLimit.max !== null && vm.blocks.length > vm.validationLimit.max) { - vm.propertyForm.maxCount.$setValidity("maxCount", false); - } - else { - vm.propertyForm.maxCount.$setValidity("maxCount", true); + if (vm.validationLimit.max !== null && vm.blocks.length > vm.validationLimit.max) { + vm.propertyForm.maxCount.$setValidity("maxCount", false); + } + else { + vm.propertyForm.maxCount.$setValidity("maxCount", true); + } } } From ad93436178c5ae9f5481b48f62c1f01e9fa46516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 9 Mar 2020 12:43:53 +0100 Subject: [PATCH 074/508] more tests --- .../services/block-editor-service.spec.js | 77 +++++++++++++++---- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js index b51ba1f9ee..6abf85c9b6 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js @@ -1,18 +1,18 @@ describe('blockEditorService tests', function () { - var blockEditorService, contentResource, $rootScope, $componentController; + var blockEditorService, contentResource, $rootScope, $scope; beforeEach(module('umbraco.services')); beforeEach(module('umbraco.resources')); beforeEach(module('umbraco.mocks')); beforeEach(module('umbraco')); - beforeEach(inject(function ($injector, mocksUtils, _$rootScope_, _$componentController_) { + beforeEach(inject(function ($injector, mocksUtils, _$rootScope_) { mocksUtils.disableAuth(); $rootScope = _$rootScope_; - $componentController = _$componentController_; + $scope = $rootScope.$new(); contentResource = $injector.get("contentResource"); spyOn(contentResource, "getScaffold").and.callFake( @@ -49,26 +49,26 @@ it('fail if no model value', function () { function createWithNoModelValue() { - blockEditorService.createModelObject(null, "Umbraco.TestBlockEditor", []); + blockEditorService.createModelObject(null, "Umbraco.TestBlockEditor", [], $scope); } expect(createWithNoModelValue).toThrow(); }); it('return a object, with methods', function () { - var modelObject = blockEditorService.createModelObject({}, "Umbraco.TestBlockEditor", []); + var modelObject = blockEditorService.createModelObject({}, "Umbraco.TestBlockEditor", [], $scope); expect(modelObject).not.toBeUndefined(); expect(modelObject.loadScaffolding).not.toBeUndefined(); }); it('getBlockConfiguration provide the requested block configurtion', function () { - var modelObject = blockEditorService.createModelObject({}, "Umbraco.TestBlockEditor", [blockConfigurationMock]); + var modelObject = blockEditorService.createModelObject({}, "Umbraco.TestBlockEditor", [blockConfigurationMock], $scope); expect(modelObject.getBlockConfiguration(blockConfigurationMock.contentTypeAlias).label).toBe(blockConfigurationMock.label); }); it('loadScaffolding provides data for itemPicker', function (done) { - var modelObject = blockEditorService.createModelObject({}, "Umbraco.TestBlockEditor", [blockConfigurationMock]); + var modelObject = blockEditorService.createModelObject({}, "Umbraco.TestBlockEditor", [blockConfigurationMock], $scope); modelObject.loadScaffolding().then(() => { var itemPickerOptions = modelObject.getAvailableBlocksForItemPicker(); @@ -82,7 +82,7 @@ it('getLayoutEntry has values', function (done) { - var modelObject = blockEditorService.createModelObject(propertyModelMock, "Umbraco.TestBlockEditor", [blockConfigurationMock]); + var modelObject = blockEditorService.createModelObject(propertyModelMock, "Umbraco.TestBlockEditor", [blockConfigurationMock], $scope); modelObject.loadScaffolding().then(() => { @@ -101,7 +101,7 @@ it('getBlockModel has values', function (done) { - var modelObject = blockEditorService.createModelObject(propertyModelMock, "Umbraco.TestBlockEditor", [blockConfigurationMock]); + var modelObject = blockEditorService.createModelObject(propertyModelMock, "Umbraco.TestBlockEditor", [blockConfigurationMock], $scope); modelObject.loadScaffolding().then(() => { @@ -123,7 +123,7 @@ var propertyModel = angular.copy(propertyModelMock); - var modelObject = blockEditorService.createModelObject(propertyModel, "Umbraco.TestBlockEditor", [blockConfigurationMock]); + var modelObject = blockEditorService.createModelObject(propertyModel, "Umbraco.TestBlockEditor", [blockConfigurationMock], $scope); modelObject.loadScaffolding().then(() => { @@ -131,9 +131,6 @@ var blockModel = modelObject.getBlockModel(layout[0]); - blockEditorBlockComponenet = $componentController("blockEditorBlock", null, {"block": blockModel, "blockEditorApi": {}, "class": "testClass"}); - blockEditorBlockComponenet.$onInit(); - blockModel.content.variants[0].tabs[0].properties[0].value = "anotherTestValue"; $rootScope.$digest();// invoke angularJS Store. @@ -158,7 +155,7 @@ propertyModel.data[0].testproperty = complexValue; - var modelObject = blockEditorService.createModelObject(propertyModel, "Umbraco.TestBlockEditor", [blockConfigurationMock]); + var modelObject = blockEditorService.createModelObject(propertyModel, "Umbraco.TestBlockEditor", [blockConfigurationMock], $scope); modelObject.loadScaffolding().then(() => { @@ -166,9 +163,6 @@ var blockModel = modelObject.getBlockModel(layout[0]); - blockEditorBlockComponenet = $componentController("blockEditorBlock", null, {"block": blockModel, "blockEditorApi": {}, "class": "testClass"}); - blockEditorBlockComponenet.$onInit(); - blockModel.content.variants[0].tabs[0].properties[0].value.list[0] = "AA"; blockModel.content.variants[0].tabs[0].properties[0].value.list.push("D"); @@ -182,6 +176,55 @@ }); + it('layout is referencing layout of propertyModel', function (done) { + + var propertyModel = angular.copy(propertyModelMock); + + var modelObject = blockEditorService.createModelObject(propertyModel, "Umbraco.TestBlockEditor", [blockConfigurationMock], $scope); + + modelObject.loadScaffolding().then(() => { + + var layout = modelObject.getLayout(); + + // remove from layout; + layout.splice(0, 1); + + expect(propertyModel.layout["Umbraco.TestBlockEditor"].length).toBe(0); + expect(propertyModel.layout["Umbraco.TestBlockEditor"][0]).toBeUndefined(); + + done(); + }); + + }); + + it('removeDataAndDestroyModel removes data', function (done) { + + var propertyModel = angular.copy(propertyModelMock); + + var modelObject = blockEditorService.createModelObject(propertyModel, "Umbraco.TestBlockEditor", [blockConfigurationMock], $scope); + + modelObject.loadScaffolding().then(() => { + + var layout = modelObject.getLayout(); + + var blockModel = modelObject.getBlockModel(layout[0]); + + // remove from layout; + layout.splice(0, 1); + + // remove from data; + modelObject.removeDataAndDestroyModel(blockModel); + + expect(propertyModel.data.length).toBe(0); + expect(propertyModel.data[0]).toBeUndefined(); + expect(propertyModel.layout["Umbraco.TestBlockEditor"].length).toBe(0); + expect(propertyModel.layout["Umbraco.TestBlockEditor"][0]).toBeUndefined(); + + done(); + }); + + }); + }); From d92023e7195cde96ca7c191be2f82ca1469de59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 9 Mar 2020 15:28:42 +0100 Subject: [PATCH 075/508] fix C# test --- .../PropertyEditors/BlockListPropertyValueConverterTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs index a8a500d5c0..7f98ef5f18 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -48,11 +48,11 @@ namespace Umbraco.Tests.PropertyEditors private BlockListConfiguration ConfigForMany() => new BlockListConfiguration { Blocks = new[] { - new BlockListConfiguration.ElementType + new BlockListConfiguration.BlockConfiguration { Alias = "Test1" }, - new BlockListConfiguration.ElementType + new BlockListConfiguration.BlockConfiguration { Alias = "Test2" } @@ -62,7 +62,7 @@ namespace Umbraco.Tests.PropertyEditors private BlockListConfiguration ConfigForSingle() => new BlockListConfiguration { Blocks = new[] { - new BlockListConfiguration.ElementType + new BlockListConfiguration.BlockConfiguration { Alias = "Test1" } From 1117f37b9e37cb825ff0a02cdc012da69e1bf2b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 10 Mar 2020 15:04:50 +0100 Subject: [PATCH 076/508] remove unused block watcher component --- .../blockeditor.block.component.html | 5 -- .../blockeditor.block.component.js | 70 ------------------- 2 files changed, 75 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.html delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.js diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.html deleted file mode 100644 index 36905978d5..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.html +++ /dev/null @@ -1,5 +0,0 @@ -
    - - - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.js deleted file mode 100644 index 5945ea9b69..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.block.component.js +++ /dev/null @@ -1,70 +0,0 @@ -(function () { - "use strict"; - angular - .module("umbraco") - .component("blockEditorBlock", { - templateUrl: "views/propertyeditors/blockeditor/blockeditor.block.component.html", - transclude: true, - controller: BlockEditorBlockBlockController, - controllerAs: "vm", - bindings: { - block: "=", - blockEditorApi: "<", - focusThisBlock: " Date: Tue, 10 Mar 2020 15:05:32 +0100 Subject: [PATCH 077/508] clean css --- .../blocklist/blocklist.component.less | 69 ------------------- 1 file changed, 69 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less index 2ab2c37d3b..df19af462c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less @@ -16,69 +16,18 @@ position: relative; width: 100%; - /*.umb-block-list__block--head { - opacity: 0; - transition: opacity 120ms; - }*/ > ng-transclude > .umb-block-list__block--actions { opacity: 0; transition: opacity 120ms; } &:hover, &:focus, &:focus-within { - /*.umb-block-list__block--head { - opacity: 1; - }*/ > ng-transclude > .umb-block-list__block--actions { opacity: 1; } } - /* - &:focus, &:focus-within { - .umb-block-list__block--head { - &::before { - background-color: @blueMid; - } - } - } - */ } -/* -.umb-block-list__block--head { - position: absolute; - top: 0; - left: -180px;// 160px from control-header + 20px from spacing. - bottom: 0; - width: 180px;// 160px from control-header + 20px from spacing. - user-select: none; - padding-top: 6px; - padding-right: 14px; - box-sizing: border-box; - color: @gray-5; - background-color: rgba(255, 255, 255, .96); - box-shadow: 0 0 6px 6px rgba(255, 255, 255, .96); - text-align: right; - &::before { - content: ''; - position: absolute; - top: 6px; - bottom: 6px; - right: 4px; - width: 1px; - background-color: @gray-10; - } - - small { - text-align: left; - margin-left: 4px; - margin-bottom: 4px; - } -} -label.umb-block-list__block--head { - cursor: grab; -} -*/ .umb-block-list__block--actions { position: absolute; top: 10px; @@ -182,24 +131,6 @@ label.umb-block-list__block--head { } } } -/* -.umb-block-list__block--create-bar { - button { - display: inline-block; - width: 120px; - height: 120px; - border-radius: @baseBorderRadius; - text-align: center; - font-size: 12px; - i { - font-size: 30px; - line-height: 20px; - margin-bottom: 10px; - display: block; - } - } -} -*/ .umb-block-list__create-button { display: flex; width: 100%; From d8705831da5f23f2bdfb89c5fa8ef018e72b8faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 12 Mar 2020 11:31:06 +0100 Subject: [PATCH 078/508] only show on hover or focus for block-actions --- .../views/propertyeditors/blocklist/blocklist.component.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less index df19af462c..474657e417 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less @@ -16,14 +16,14 @@ position: relative; width: 100%; - > ng-transclude > .umb-block-list__block--actions { + > .umb-block-list__block--actions { opacity: 0; transition: opacity 120ms; } &:hover, &:focus, &:focus-within { - > ng-transclude > .umb-block-list__block--actions { + > .umb-block-list__block--actions { opacity: 1; } } From 9859f1b0d385972f14b85e971ac2c23bce46ee1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 12 Mar 2020 15:48:26 +0100 Subject: [PATCH 079/508] clean up and prepare for implementing settings --- .../common/services/blockeditor.service.js | 31 ++++++++++---- .../blockeditor.controller.js} | 5 ++- .../blockeditor.html} | 14 +++++-- .../blocklist/blocklist.component.html | 6 +++ .../blocklist/blocklist.component.js | 42 +++++++++++++------ src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 1 + src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + .../Umbraco/config/lang/en_us.xml | 1 + 8 files changed, 75 insertions(+), 26 deletions(-) rename src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/{elementeditor/elementeditor.controller.js => blockeditor/blockeditor.controller.js} (75%) rename src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/{elementeditor/elementeditor.html => blockeditor/blockeditor.html} (69%) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index c6613e0c60..8df95bb684 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -82,12 +82,10 @@ /** - * Used to create a scoped watcher for a property on a blockModel. + * Used to create a scoped watcher for a content property on a blockModel. */ - function createPropWatcher(blockModel, prop) { - + function createContentModelPropWatcher(blockModel, prop) { return function() { - // sync data: blockModel.contentModel[prop.alias] = prop.value; @@ -95,7 +93,16 @@ // TODO: could use a debounce. blockModel.label = getBlockLabel(blockModel); } + } + /** + * Used to create a scoped watcher for a settings property on a blockModel. + */ + function createSettingsModelPropWatcher(blockModel, prop) { + return function() { + // sync data: + blockModel.layoutModel.settings[prop.alias] = prop.value; + } } @@ -143,7 +150,7 @@ this.blockConfigurations.forEach(blockConfiguration => { scaffoldAliases.push(blockConfiguration.contentTypeAlias); if (blockConfiguration.settingsElementTypeAlias != null) { - scaffoldAliases.push(elementType.settingsElementTypeAlias); + scaffoldAliases.push(blockConfiguration.settingsElementTypeAlias); } }); @@ -233,7 +240,12 @@ blockModel.layoutModel = layoutEntry; blockModel.watchers = []; - // TODO: settings + // TODO: implement settings + + // create ElementTypeModel of settings + // store ElementTypeModel in blockModel.settings + // setup watchers for mapping + // Add blockModel to our isolated scope to enable watching its values: this.isolatedScope.blockModels["_"+blockModel.key] = blockModel; @@ -249,7 +261,7 @@ // Do notice that it is not performing a deep watch, meaning that we are only watching primatives and changes directly to the object of property-value. // But we like to sync non-primative values as well! Yes, and this does happen, just not through this code, but through the nature of JavaScript. // Non-primative values act as references to the same data and are therefor synced. - blockModel.watchers.push(this.isolatedScope.$watch("blockModels._"+blockModel.key+".content.variants[0].tabs["+t+"].properties["+p+"].value", createPropWatcher(blockModel, prop))); + blockModel.watchers.push(this.isolatedScope.$watch("blockModels._"+blockModel.key+".content.variants[0].tabs["+t+"].properties["+p+"].value", createContentModelPropWatcher(blockModel, prop))); } } @@ -286,7 +298,8 @@ mapToPropertyModel(blockModel.content, blockModel.contentModel); - // TODO: sync settings to layout entry. + // TODO: implement settings, sync settings to layout entry. + // mapToPropertyModel(blockModel.settings, blockModel.layoutModel.settings) }, @@ -318,7 +331,7 @@ } if (blockConfiguration.settingsElementTypeAlias != null) { - // TODO: Settings. + entry.settings = {}; } return entry; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js similarity index 75% rename from src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js index a054e64784..fea337f31f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js @@ -1,12 +1,15 @@ //used for the media picker dialog angular.module("umbraco") -.controller("Umbraco.Editors.ElementEditorController", +.controller("Umbraco.Editors.BlockEditorController", function ($scope) { var vm = this; vm.content = $scope.model.content; + // TODO: implement settings — do notice that settings is optional. + //vm.settings = $scope.model.settings; + vm.title = $scope.model.title; vm.saveAndClose = function() { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.html similarity index 69% rename from src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.html index e5c43468af..b52480c2a7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.html @@ -1,6 +1,6 @@ -
    +
    - + - + + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index 07a07ee529..7f002846d7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -19,6 +19,12 @@
    +
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js index 7380c9b0c8..74c61a419a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js @@ -4,45 +4,48 @@ angular.module("umbraco") function ($scope) { var vm = this; - function showContent() { - if (vm.settingsTab) vm.settingsTab.active = false; - vm.contentTab.active = true; - } - - function showSettings() { - if (vm.settingsTab) vm.settingsTab.active = true; - vm.contentTab.active = false; - } - vm.content = $scope.model.content; vm.settings = $scope.model.settings; + + vm.model = $scope.model; + console.log("blockeditor model:", vm.model) + vm.tabs = []; - var settingsOnly = vm.content && vm.content.variants ? false : true; - if (!settingsOnly) { - vm.contentTab = { - "name": "Content", - "alias": "content", - "icon": "icon-document", - "action": showContent, - "active": true - }; + if (vm.content && vm.content.variants) { - vm.tabs.push(vm.contentTab); + var apps = vm.content.apps; + + vm.tabs = apps; + + // replace view of content app. + var contentApp = apps.find(entry => entry.alias === "umbContent"); + contentApp.view = "views/common/infiniteeditors/elementeditor/elementeditor.content.html"; + + if($scope.model.hideContent) { + apps.splice(apps.indexOf(contentApp), 1); + } + + // remove info app: + var infoAppIndex = apps.findIndex(entry => entry.alias === "umbInfo"); + apps.splice(infoAppIndex, 1); + } if (vm.settings && vm.settings.variants) { - vm.settingsTab = { + var settingsTab = { "name": "Settings", "alias": "settings", "icon": "icon-settings", - "action": showSettings, - "active": settingsOnly + "view": "views/common/infiniteeditors/elementeditor/elementeditor.settings.html" }; - vm.tabs.push(vm.settingsTab); + vm.tabs.push(settingsTab); } - vm.title = (settingsOnly ? 'SETTINGS: ' : '') + $scope.model.title; + // activate frst app: + if (vm.tabs.length > 0) { + vm.tabs[0].active = true; + } vm.saveAndClose = function () { if ($scope.model && $scope.model.submit) { @@ -52,6 +55,7 @@ angular.module("umbraco") vm.close = function() { if ($scope.model && $scope.model.close) { + // TODO: If content has changed, we should notify user. $scope.model.close($scope.model); } } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.html index 51750756a8..4480648da2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.html @@ -4,7 +4,7 @@ - - -
    -
    -
    - -
    -
    +
    + +
    -
    -
    -
    - -
    -
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementEditor.content.component.html similarity index 66% rename from src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.html rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementEditor.content.component.html index 88a2b306ef..5308173c72 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementEditor.content.component.html @@ -2,7 +2,7 @@
    + ng-repeat="group in vm.model.variants[0].tabs track by group.label">
    {{ group.label }}
    @@ -13,13 +13,13 @@ data-element="property-{{property.alias}}" ng-repeat="property in group.properties track by property.alias" property="property" - show-inherit="vm.content.variants.length > 1 && !property.culture && !activeVariant.language.isDefault" + show-inherit="vm.model.variants.length > 1 && !property.culture && !activeVariant.language.isDefault" inherits-from="defaultVariant.language.name"> -
    +
    + preview="vm.model.variants.length > 1 && !activeVariant.language.isDefault && !property.culture && !property.unlockInvariantValue">
    @@ -29,7 +29,7 @@
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementEditor.content.component.js similarity index 54% rename from src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.js rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementEditor.content.component.js index 347d8f1d7b..5056576ca3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementContentEditor.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementEditor.content.component.js @@ -3,16 +3,16 @@ angular .module('umbraco.directives') - .component('umbElementContentEditor', { - templateUrl: 'views/common/infiniteeditors/elementeditor/elementContentEditor.component.html', - controller: ElementEditorComponentController, + .component('umbElementEditorContent', { + templateUrl: 'views/common/infiniteeditors/elementeditor/elementEditor.content.component.html', + controller: ElementEditorContentComponentController, controllerAs: 'vm', bindings: { - content: '=' + model: '=' } }); - function ElementEditorComponentController() { + function ElementEditorContentComponentController() { // TODO: we might not need this.. diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.content.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.content.html index d30b343c4d..eb8c72c579 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.content.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.content.html @@ -1 +1,3 @@ - +
    + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.settings.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.settings.html new file mode 100644 index 0000000000..df69e2e648 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.settings.html @@ -0,0 +1 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index 7f002846d7..4b54b85fe8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -19,7 +19,7 @@
    - -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less index ab6b21d898..30d1e6bd2d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less @@ -6,7 +6,7 @@ border-radius: @baseBorderRadius; transition: border-color 120ms; - &:not(.--open):hover { + .umb-block-list__block:not(.--open) &:hover { border-color: @gray-8; } @@ -43,7 +43,7 @@ } } - &.--open { + .umb-block-list__block.--open & { border-color: @gray-8; box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.05); > button { @@ -55,7 +55,7 @@ } .blockelement-inlineblock-editor__inner { border-top: 1px solid @gray-8; - background-color: @gray-11; + background-color: @gray-12; .umb-group-panel { background-color: transparent; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index 4b54b85fe8..4aa3b0f6b1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -14,7 +14,7 @@ > -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less index 474657e417..d470ee97cf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less @@ -21,7 +21,10 @@ transition: opacity 120ms; } - &:hover, &:focus, &:focus-within { + &:hover, + &:focus, + &:focus-within, + &.--open { > .umb-block-list__block--actions { opacity: 1; From e20c7578f8cc697d38a7da3983f22b7b7fdbc0ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 1 Apr 2020 13:44:56 +0200 Subject: [PATCH 087/508] NotSupported property editor, to be used when an editor is not supported in the given context. --- .../common/services/blockeditor.service.js | 24 ++++++++++++++++++- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../notsupported/notsupported.controller.js | 9 +++++++ .../notsupported/notsupported.html | 3 +++ .../notsupported/notsupported.less | 7 ++++++ src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 1 + src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + .../Umbraco/config/lang/en_us.xml | 1 + 8 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.less diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index 8703aeb385..a05535d3b4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -145,6 +145,28 @@ } + /** + * Used to highlight unsupported properties for the user, changes unsupported properties into a unsupported-property. + */ + var notSupportedProperties = [ + "Umbraco.Tags", + "Umbraco.UploadField", + "Umbraco.ImageCropper" + ]; + function replaceUnsupportedProperties(scaffold) { + scaffold.variants.forEach((variant) => { + variant.tabs.forEach((tab) => { + tab.properties.forEach((property) => { + if (notSupportedProperties.indexOf(property.editor) !== -1) { + property.view = "notsupported"; + } + }); + }); + }); + return scaffold; + } + + /** * @ngdoc factory * @name umbraco.factory.BlockEditorModelObject @@ -198,7 +220,7 @@ scaffoldAliases.forEach((elementTypeAlias => { tasks.push(contentResource.getScaffold(-20, elementTypeAlias).then(scaffold => { - this.scaffolds.push(scaffold); + this.scaffolds.push(replaceUnsupportedProperties(scaffold)); })); })); diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index e0e679924b..14a62ae790 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -200,6 +200,7 @@ // Property Editors @import "../views/propertyeditors/blocklist/blocklist.component.less"; @import "../views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less"; +@import "../views/propertyeditors/notsupported/notsupported.less"; // Utilities diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.controller.js new file mode 100644 index 0000000000..17e9f47e19 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.controller.js @@ -0,0 +1,9 @@ +angular.module('umbraco').controller("Umbraco.PropertyEditors.NotSupportedController", + function ($scope) { + + var vm = this; + + console.log($scope.umbProperty); + + } +); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.html new file mode 100644 index 0000000000..81b6da1996 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.html @@ -0,0 +1,3 @@ +
    + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.less new file mode 100644 index 0000000000..9bf3ffc2dc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.less @@ -0,0 +1,7 @@ +.umb-property-editor-notsupported { + background-color: @red; + color: white; + padding: 5px 10px; + width: auto; + border-radius: @baseBorderRadius * 2; +} diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 779e97b035..4c74c45d88 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -607,6 +607,7 @@ Du skal stå til venstre for de 2 celler du ønsker at samle! Du kan ikke opdele en celle, som ikke allerede er delt. Denne egenskab er ugyldig + Feltet %0% bruger editor %1% som ikke er supporteret for ElementTyper. Om diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 1dbfd85cd5..a09088efa1 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -608,6 +608,7 @@ %0% is a mandatory field %0% at %1% is not in a correct format %0% is not in a correct format + Property %0% uses editor %1% which is not supported in ElementTypes. Received an error from the server diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 348880965e..89fbceaea8 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -625,6 +625,7 @@ Please place cursor at the left of the two cells you wish to merge You cannot split a cell that hasn't been merged. This property is invalid + Property %0% uses editor %1% which is not supported in ElementTypes. Options From d6cad317509ced1a9b65d21f973c4518274f2a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 1 Apr 2020 14:31:56 +0200 Subject: [PATCH 088/508] remove unused controller --- .../notsupported/notsupported.controller.js | 9 --------- .../views/propertyeditors/notsupported/notsupported.html | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.controller.js deleted file mode 100644 index 17e9f47e19..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.controller.js +++ /dev/null @@ -1,9 +0,0 @@ -angular.module('umbraco').controller("Umbraco.PropertyEditors.NotSupportedController", - function ($scope) { - - var vm = this; - - console.log($scope.umbProperty); - - } -); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.html index 81b6da1996..a9d86de217 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.html @@ -1,3 +1,3 @@ -
    +
    From 9bdb0cb1b8d415416483123bdc59ef767b490d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 1 Apr 2020 15:29:58 +0200 Subject: [PATCH 089/508] Hide group header if only one group is presented --- .../inlineblock/inlineblock.editor.html | 2 +- .../inlineblock/inlineblock.editor.less | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html index 5a83a80234..028a10d434 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html @@ -4,7 +4,7 @@ {{block.label}} -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less index 30d1e6bd2d..7de7707896 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less @@ -56,13 +56,20 @@ .blockelement-inlineblock-editor__inner { border-top: 1px solid @gray-8; background-color: @gray-12; - - .umb-group-panel { + + > * > * > * > .umb-group-panel { background-color: transparent; box-shadow: none; + margin-top: 10px; margin-bottom: 0; + > .umb-group-panel__content > .umb-property { + margin-bottom: 10px; + } } - .umb-group-panel__header { - display:none; + .umb-group-panel + .umb-group-panel { + margin-top: 20px; + } + &.--singleGroup > * > * > * > .umb-group-panel .umb-group-panel__header { + display: none; } } From c04a68730dac81cd75c73d398dd51dd4058362a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 1 Apr 2020 15:34:50 +0200 Subject: [PATCH 090/508] rename notsupport property editor css class --- .../src/views/propertyeditors/notsupported/notsupported.html | 2 +- .../src/views/propertyeditors/notsupported/notsupported.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.html index a9d86de217..a2fbb0e907 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.html @@ -1,3 +1,3 @@ -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.less index 9bf3ffc2dc..5eaec3f67b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/notsupported/notsupported.less @@ -1,4 +1,4 @@ -.umb-property-editor-notsupported { +.umb-property-editor.umb-property-editor--notsupported { background-color: @red; color: white; padding: 5px 10px; From 52832a9b1c588f45e9428d1e66ca1504c63c06f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 1 Apr 2020 15:42:32 +0200 Subject: [PATCH 091/508] smaller header for property group --- .../src/less/components/html/umb-group-panel.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/html/umb-group-panel.less b/src/Umbraco.Web.UI.Client/src/less/components/html/umb-group-panel.less index 15f85e1c77..76c0c55fca 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/html/umb-group-panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/html/umb-group-panel.less @@ -8,11 +8,11 @@ .umb-group-panel__header { padding: 12px 20px; font-weight: bold; - font-size: 16px; + font-size: 14px; display: flex; align-items: center; justify-content: space-between; - color: @grayDark; + color: @grayDarker; border-bottom: 1px solid @gray-9; } From 47147612774532eb694db25a0b22955f33b6a9af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 1 Apr 2020 15:42:43 +0200 Subject: [PATCH 092/508] hide description if no description is presented --- .../src/views/components/property/umb-property.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html index ca57679f51..c7a7530181 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html @@ -23,7 +23,7 @@ - +
    From 62c4ecac0109e89666dd0ee1a0c69acb70e72358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 1 Apr 2020 16:13:16 +0200 Subject: [PATCH 093/508] css adjustments --- .../blockelements/inlineblock/inlineblock.editor.less | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less index 7de7707896..a1e5b54c35 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less @@ -63,13 +63,17 @@ margin-top: 10px; margin-bottom: 0; > .umb-group-panel__content > .umb-property { - margin-bottom: 10px; + margin-bottom: 20px; } } .umb-group-panel + .umb-group-panel { margin-top: 20px; } - &.--singleGroup > * > * > * > .umb-group-panel .umb-group-panel__header { - display: none; + &.--singleGroup > * > * > * > .umb-group-panel { + margin-top: 0; + > .umb-group-panel__header { + display: none; + } } + } From ee534f88cc42365997453be17ef015377be24fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 1 Apr 2020 16:42:09 +0200 Subject: [PATCH 094/508] Inline create button styling: Better spacing, darker blue color for Focus Outline, moving the plus icon to mouse position for better visual appearance. --- .../inlineblock/inlineblock.editor.less | 4 +-- ...klist.component.createButton.controller.js | 18 ++++++++++++ .../blocklist/blocklist.component.html | 3 ++ .../blocklist/blocklist.component.less | 28 ++++++++++--------- 4 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.createButton.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less index a1e5b54c35..2702ca163a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less @@ -1,7 +1,7 @@ .blockelement-inlineblock-editor { - margin-bottom: 2px; - margin-top: 2px; + margin-bottom: 4px; + margin-top: 4px; border: 1px solid @gray-9; border-radius: @baseBorderRadius; transition: border-color 120ms; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.createButton.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.createButton.controller.js new file mode 100644 index 0000000000..1a0b4f6241 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.createButton.controller.js @@ -0,0 +1,18 @@ +(function () { + "use strict"; + + angular + .module("umbraco") + .controller("Umbraco.PropertyEditors.BlockListPropertyEditor.CreateButtonController", + function BlockListController($scope) { + + var vm = this; + vm.plusPosX = 0; + + vm.onMouseMove = function($event) { + vm.plusPosX = $event.offsetX; + } + + }); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index 4aa3b0f6b1..fc957ce003 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -11,7 +11,10 @@ type="button" class="btn-reset umb-block-list__block--create-button" ng-click="vm.showCreateDialog($index, $event)" + ng-controller="Umbraco.PropertyEditors.BlockListPropertyEditor.CreateButtonController as vm" + ng-mousemove="vm.onMouseMove($event)" > +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less index d470ee97cf..4194b5d89e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less @@ -71,7 +71,7 @@ opacity: 0; outline: none; height: 12px; - margin-top: -6px; + margin-top: -9px; padding-top: 6px; margin-bottom: -6px; transition: opacity 240ms; @@ -79,7 +79,9 @@ &::before { content: ''; position: absolute; - background-color: @ui-outline; + background-color: @blueMid; + border-top:1px solid white; + border-bottom:1px solid white; border-radius: 2px; top:5px; right: 0; @@ -92,21 +94,21 @@ 100% { opacity: 0.5; } } } - &::after { - content: "+"; - margin-left: auto; - margin-right: auto; + > .__plus { + position: absolute; + pointer-events: none;// lets stop avoiding the mouse values in JS move event. + margin-left: -18px - 10px; margin-top: -18px; margin-bottom: -18px; width: 28px; height: 25px; padding-bottom: 3px; border-radius: 3em; - border: 2px solid @ui-outline; + border: 2px solid @blueMid; display: flex; justify-content: center; align-items: center; - color: @ui-outline; + color: @blueMid; font-size: 20px; font-weight: 800; background-color: rgba(255, 255, 255, .96); @@ -115,20 +117,20 @@ transition: transform 240ms ease-in; animation: umb-block-list__block--create-button_after 800ms ease-in-out infinite; @keyframes umb-block-list__block--create-button_after { - 0% { color: rgba(@ui-outline, 0.8); } - 50% { color: rgba(@ui-outline, 1); } - 100% { color: rgba(@ui-outline, 0.8); } + 0% { color: rgba(@blueMid, 0.8); } + 50% { color: rgba(@blueMid, 1); } + 100% { color: rgba(@blueMid, 0.8); } } } &:focus { - &::after { + > .__plus { border: 2px solid @ui-outline; } } &:hover, &:focus { opacity: 1; transition-duration: 120ms; - &::after { + > .__plus { transform: scale(1); transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275); } From f58f0547aca2a1ce125754b3bd74e661875237ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 1 Apr 2020 17:06:42 +0200 Subject: [PATCH 095/508] css correction --- .../views/propertyeditors/blocklist/blocklist.component.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less index 4194b5d89e..d08c862f88 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less @@ -71,7 +71,7 @@ opacity: 0; outline: none; height: 12px; - margin-top: -9px; + margin-top: -7px; padding-top: 6px; margin-bottom: -6px; transition: opacity 240ms; From 2ed740b22a5621357067473493a30f6e12ee2289 Mon Sep 17 00:00:00 2001 From: Benjamin Carleski Date: Wed, 1 Apr 2020 15:54:49 -0700 Subject: [PATCH 096/508] Add references for picked items --- .../V_8_7_0/StackedContentToBlockList.cs | 2 +- .../Models/Blocks/IBlockEditorDataHelper.cs | 11 + .../Models/Blocks/IBlockElement.cs | 15 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../BlockEditorPropertyEditor.cs | 262 +++++++++++++++++- .../BlockListPropertyEditor.cs | 41 ++- 6 files changed, 323 insertions(+), 9 deletions(-) create mode 100644 src/Umbraco.Core/Models/Blocks/IBlockEditorDataHelper.cs diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs index 0714c4b632..076b4f205c 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs @@ -132,7 +132,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 private void UpdateDataType(DataTypeDto dataType) { - dataType.DbType = ValueStorageType.Nvarchar.ToString(); + dataType.DbType = ValueStorageType.Ntext.ToString(); dataType.EditorAlias = Constants.PropertyEditors.Aliases.BlockList; Database.Update(dataType); diff --git a/src/Umbraco.Core/Models/Blocks/IBlockEditorDataHelper.cs b/src/Umbraco.Core/Models/Blocks/IBlockEditorDataHelper.cs new file mode 100644 index 0000000000..32f8431e65 --- /dev/null +++ b/src/Umbraco.Core/Models/Blocks/IBlockEditorDataHelper.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json.Linq; +using System.Collections.Generic; + +namespace Umbraco.Core.Models.Blocks +{ + public interface IBlockEditorDataHelper + { + IEnumerable GetBlockReferences(JObject layout); + bool IsEditorSpecificPropertyKey(string propertyKey); + } +} diff --git a/src/Umbraco.Core/Models/Blocks/IBlockElement.cs b/src/Umbraco.Core/Models/Blocks/IBlockElement.cs index eeb5a73e2c..38b4e96aae 100644 --- a/src/Umbraco.Core/Models/Blocks/IBlockElement.cs +++ b/src/Umbraco.Core/Models/Blocks/IBlockElement.cs @@ -1,8 +1,18 @@ namespace Umbraco.Core.Models.Blocks { + /// + /// Represents a data item reference for a Block Editor implementation + /// + /// + /// see: https://github.com/umbraco/rfcs/blob/907f3758cf59a7b6781296a60d57d537b3b60b8c/cms/0011-block-data-structure.md#strongly-typed + /// + public interface IBlockReference + { + Udi Udi { get; } + } + // TODO: IBlockElement doesn't make sense, this is a reference to an actual element with some settings // and always has to do with the "Layout", should possibly be called IBlockReference or IBlockLayout or IBlockLayoutReference - /// /// Represents a data item for a Block editor implementation /// @@ -10,9 +20,8 @@ /// /// see: https://github.com/umbraco/rfcs/blob/907f3758cf59a7b6781296a60d57d537b3b60b8c/cms/0011-block-data-structure.md#strongly-typed /// - public interface IBlockElement + public interface IBlockElement : IBlockReference { - Udi Udi { get; } TSettings Settings { get; } } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 148aa0052d..73bd8a1126 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -132,6 +132,7 @@ + diff --git a/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs index b9ee1b84fb..f8105c5651 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs @@ -1,5 +1,17 @@ -using Umbraco.Core.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text.RegularExpressions; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Blocks; +using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; namespace Umbraco.Web.PropertyEditors { @@ -9,9 +21,255 @@ namespace Umbraco.Web.PropertyEditors public abstract class BlockEditorPropertyEditor : DataEditor { public const string ContentTypeAliasPropertyKey = "contentTypeAlias"; + public const string UdiPropertyKey = "udi"; + private readonly IBlockEditorDataHelper _dataHelper; + private readonly Lazy _propertyEditors; + private readonly IDataTypeService _dataTypeService; + private readonly IContentTypeService _contentTypeService; - public BlockEditorPropertyEditor(ILogger logger) : base(logger) + public BlockEditorPropertyEditor(ILogger logger, Lazy propertyEditors, IDataTypeService dataTypeService, IContentTypeService contentTypeService, IBlockEditorDataHelper dataHelper) + : base(logger) { + _dataHelper = dataHelper; + _propertyEditors = propertyEditors; + _dataTypeService = dataTypeService; + _contentTypeService = contentTypeService; } + + // has to be lazy else circular dep in ctor + private PropertyEditorCollection PropertyEditors => _propertyEditors.Value; + + #region Value Editor + + protected override IDataValueEditor CreateValueEditor() => new BlockEditorPropertyValueEditor(Attribute, _dataHelper, PropertyEditors, _dataTypeService, _contentTypeService); + + internal class BlockEditorPropertyValueEditor : DataValueEditor, IDataValueReference + { + private readonly IBlockEditorDataHelper _dataHelper; + private readonly PropertyEditorCollection _propertyEditors; + private readonly IDataTypeService _dataTypeService; + private readonly BlockEditorValues _blockEditorValues; + + public BlockEditorPropertyValueEditor(DataEditorAttribute attribute, IBlockEditorDataHelper dataHelper, PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, IContentTypeService contentTypeService) + : base(attribute) + { + _dataHelper = dataHelper; + _propertyEditors = propertyEditors; + _dataTypeService = dataTypeService; + _blockEditorValues = new BlockEditorValues(dataHelper, contentTypeService); + Validators.Add(new BlockEditorValidator(propertyEditors, dataTypeService, _blockEditorValues)); + } + + public IEnumerable GetReferences(object value) + { + var rawJson = value == null ? string.Empty : value is string str ? str : value.ToString(); + + var result = new List(); + + foreach (var row in _blockEditorValues.GetPropertyValues(rawJson, out _)) + { + if (row.PropType == null) continue; + + var propEditor = _propertyEditors[row.PropType.PropertyEditorAlias]; + + var valueEditor = propEditor?.GetValueEditor(); + if (!(valueEditor is IDataValueReference reference)) continue; + + var val = row.JsonRowValue[row.PropKey]?.ToString(); + + var refs = reference.GetReferences(val); + + result.AddRange(refs); + } + + return result; + } + } + + internal class BlockEditorValidator : IValueValidator + { + private readonly PropertyEditorCollection _propertyEditors; + private readonly IDataTypeService _dataTypeService; + private readonly BlockEditorValues _blockEditorValues; + + public BlockEditorValidator(PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, BlockEditorValues blockEditorValues) + { + _propertyEditors = propertyEditors; + _dataTypeService = dataTypeService; + _blockEditorValues = blockEditorValues; + } + + public IEnumerable Validate(object rawValue, string valueType, object dataTypeConfiguration) + { + var validationResults = new List(); + + foreach (var row in _blockEditorValues.GetPropertyValues(rawValue, out _)) + { + if (row.PropType == null) continue; + + var config = _dataTypeService.GetDataType(row.PropType.DataTypeId).Configuration; + var propertyEditor = _propertyEditors[row.PropType.PropertyEditorAlias]; + if (propertyEditor == null) continue; + + foreach (var validator in propertyEditor.GetValueEditor().Validators) + { + foreach (var result in validator.Validate(row.JsonRowValue[row.PropKey], propertyEditor.GetValueEditor().ValueType, config)) + { + result.ErrorMessage = "Item " + (row.RowIndex + 1) + " '" + row.PropType.Name + "' " + result.ErrorMessage; + validationResults.Add(result); + } + } + + // Check mandatory + if (row.PropType.Mandatory) + { + if (row.JsonRowValue[row.PropKey] == null) + { + var message = string.IsNullOrWhiteSpace(row.PropType.MandatoryMessage) + ? $"'{row.PropType.Name}' cannot be null" + : row.PropType.MandatoryMessage; + validationResults.Add(new ValidationResult($"Item {(row.RowIndex + 1)}: {message}", new[] { row.PropKey })); + } + else if (row.JsonRowValue[row.PropKey].ToString().IsNullOrWhiteSpace() || (row.JsonRowValue[row.PropKey].Type == JTokenType.Array && !row.JsonRowValue[row.PropKey].HasValues)) + { + var message = string.IsNullOrWhiteSpace(row.PropType.MandatoryMessage) + ? $"'{row.PropType.Name}' cannot be empty" + : row.PropType.MandatoryMessage; + validationResults.Add(new ValidationResult($"Item {(row.RowIndex + 1)}: {message}", new[] { row.PropKey })); + } + } + + // Check regex + if (!row.PropType.ValidationRegExp.IsNullOrWhiteSpace() + && row.JsonRowValue[row.PropKey] != null && !row.JsonRowValue[row.PropKey].ToString().IsNullOrWhiteSpace()) + { + var regex = new Regex(row.PropType.ValidationRegExp); + if (!regex.IsMatch(row.JsonRowValue[row.PropKey].ToString())) + { + var message = string.IsNullOrWhiteSpace(row.PropType.ValidationRegExpMessage) + ? $"'{row.PropType.Name}' is invalid, it does not match the correct pattern" + : row.PropType.ValidationRegExpMessage; + validationResults.Add(new ValidationResult($"Item {(row.RowIndex + 1)}: {message}", new[] { row.PropKey })); + } + } + } + + return validationResults; + } + } + + internal class BlockEditorValues + { + private readonly IBlockEditorDataHelper _dataHelper; + private readonly Lazy> _contentTypes; + + public BlockEditorValues(IBlockEditorDataHelper dataHelper, IContentTypeService contentTypeService) + { + _dataHelper = dataHelper; + _contentTypes = new Lazy>(() => contentTypeService.GetAll().ToDictionary(c => c.Alias)); + } + + private IContentType GetElementType(JObject item) + { + var contentTypeAlias = item[ContentTypeAliasPropertyKey]?.ToObject() ?? string.Empty; + _contentTypes.Value.TryGetValue(contentTypeAlias, out var contentType); + return contentType; + } + + public IEnumerable GetPropertyValues(object propertyValue, out List deserialized) + { + var rowValues = new List(); + + deserialized = null; + + if (propertyValue == null || string.IsNullOrWhiteSpace(propertyValue.ToString())) + return Enumerable.Empty(); + + var data = JsonConvert.DeserializeObject(propertyValue.ToString()); + if (data?.Layout == null || data.Data == null || data.Data.Count == 0) + return Enumerable.Empty(); + + var blockRefs = _dataHelper.GetBlockReferences(data.Layout); + if (blockRefs == null) + return Enumerable.Empty(); + + var dataMap = new Dictionary(data.Data.Count); + data.Data.ForEach(d => + { + var udiObj = d?[UdiPropertyKey]; + if (Udi.TryParse(udiObj == null || udiObj.Type != JTokenType.String ? null : udiObj.ToString(), out var udi)) + dataMap[udi] = d; + }); + + deserialized = blockRefs.Select(r => dataMap.TryGetValue(r.Udi, out var block) ? block : null).Where(r => r != null).ToList(); + if (deserialized == null || deserialized.Count == 0) + return Enumerable.Empty(); + + var index = 0; + + foreach (var o in deserialized) + { + var propValues = o; + + var contentType = GetElementType(propValues); + if (contentType == null) + continue; + + var propertyTypes = contentType.CompositionPropertyTypes.ToDictionary(x => x.Alias, x => x); + var propAliases = propValues.Properties().Select(x => x.Name); + foreach (var propAlias in propAliases) + { + propertyTypes.TryGetValue(propAlias, out var propType); + rowValues.Add(new RowValue(propAlias, propType, propValues, index)); + } + index++; + } + + return rowValues; + } + + internal class RowValue + { + public RowValue(string propKey, PropertyType propType, JObject propValues, int index) + { + PropKey = propKey ?? throw new ArgumentNullException(nameof(propKey)); + PropType = propType; + JsonRowValue = propValues ?? throw new ArgumentNullException(nameof(propValues)); + RowIndex = index; + } + + /// + /// The current property key being iterated for the row value + /// + public string PropKey { get; } + + /// + /// The of the value (if any), this may be null + /// + public PropertyType PropType { get; } + + /// + /// The json values for the current row + /// + public JObject JsonRowValue { get; } + + /// + /// The Nested Content row index + /// + public int RowIndex { get; } + } + + private class BlockEditorData + { + [JsonProperty("layout")] + public JObject Layout { get; set; } + + [JsonProperty("data")] + public List Data { get; set; } + } + } + #endregion + + private static bool IsSystemPropertyKey(string propertyKey) => ContentTypeAliasPropertyKey == propertyKey || UdiPropertyKey == propertyKey; } } diff --git a/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs index 782122bccd..3f8288b0ac 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs @@ -1,5 +1,10 @@ -using Umbraco.Core; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core; using Umbraco.Core.Logging; +using Umbraco.Core.Models.Blocks; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; @@ -17,8 +22,8 @@ namespace Umbraco.Web.PropertyEditors Icon = "icon-thumbnail-list")] public class BlockListPropertyEditor : BlockEditorPropertyEditor { - public BlockListPropertyEditor(ILogger logger) - : base(logger) + public BlockListPropertyEditor(ILogger logger, Lazy propertyEditors, IDataTypeService dataTypeService, IContentTypeService contentTypeService) + : base(logger, propertyEditors, dataTypeService, contentTypeService, new DataHelper()) { } #region Pre Value Editor @@ -27,5 +32,35 @@ namespace Umbraco.Web.PropertyEditors #endregion + #region IBlockEditorDataHelper + + private class DataHelper : IBlockEditorDataHelper + { + public IEnumerable GetBlockReferences(JObject layout) + { + if (!(layout?[Constants.PropertyEditors.Aliases.BlockList] is JArray blLayouts)) + yield break; + + foreach (var blLayout in blLayouts) + { + if (!(blLayout is JObject blockRef) || !(blockRef[UdiPropertyKey] is JValue udiRef) || udiRef.Type != JTokenType.String || !Udi.TryParse(udiRef.ToString(), out var udi)) continue; + yield return new SimpleRef(udi); + } + } + + public bool IsEditorSpecificPropertyKey(string propertyKey) => false; + + private class SimpleRef : IBlockReference + { + public SimpleRef(Udi udi) + { + Udi = udi; + } + + public Udi Udi { get; } + } + } + + #endregion } } From 1bd490174620d4bc0340b44ae563999d00c7ff3c Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 2 Apr 2020 11:11:08 +0100 Subject: [PATCH 097/508] Revert commit 45e892f3505059674779c6e1a43084a367c2862f - Changes api to GetData --- .../Models/Blocks/BlockListLayoutReference.cs | 11 ++++++++++- src/Umbraco.Core/Models/Blocks/BlockListModel.cs | 14 +------------- .../BlockListPropertyValueConverterTests.cs | 11 ++++------- .../BlockListPropertyValueConverter.cs | 2 +- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs index 85d17fad24..19b30e6ea6 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs @@ -10,9 +10,10 @@ namespace Umbraco.Core.Models.Blocks [DataContract(Name = "blockListLayout", Namespace = "")] public class BlockListLayoutReference : IBlockElement { - public BlockListLayoutReference(Udi udi, IPublishedElement settings) + public BlockListLayoutReference(Udi udi, IPublishedElement data, IPublishedElement settings) { Udi = udi ?? throw new ArgumentNullException(nameof(udi)); + Data = data ?? throw new ArgumentNullException(nameof(data)); Settings = settings; // can be null } @@ -28,5 +29,13 @@ namespace Umbraco.Core.Models.Blocks [DataMember(Name = "settings")] public IPublishedElement Settings { get; } + /// + /// The data item referenced + /// + /// + /// This is ignored from serialization since it is just a reference to the actual data element + /// + [IgnoreDataMember] + public IPublishedElement Data { get; } } } diff --git a/src/Umbraco.Core/Models/Blocks/BlockListModel.cs b/src/Umbraco.Core/Models/Blocks/BlockListModel.cs index 153fe6be8a..089ca7e6a3 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockListModel.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockListModel.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using System.Runtime.Serialization; using Umbraco.Core.Models.PublishedContent; @@ -23,17 +22,6 @@ namespace Umbraco.Core.Models.Blocks [DataMember(Name = "layout")] public IEnumerable Layout { get; } - /// - /// Returns the data item associated with the layout udi reference - /// - /// - /// - public IPublishedElement GetData(Udi udi) - { - if (!(udi is GuidUdi guidUdi)) - return null; - return Data.FirstOrDefault(x => x.Key == guidUdi.Guid); - } - + } } diff --git a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs index 7f98ef5f18..f63485f4bf 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -317,15 +317,12 @@ data: []}"; Assert.AreEqual(2, converted.Layout.Count()); var item0 = converted.Layout.ElementAt(0); - var item0Data = converted.GetData(item0.Udi); - Assert.IsNotNull(item0Data); - Assert.AreEqual(Guid.Parse("1304E1DD-AC87-4396-84FE-8A399231CB3D"), item0Data.Key); - Assert.AreEqual("home", item0Data.ContentType.Alias); + Assert.AreEqual(Guid.Parse("1304E1DD-AC87-4396-84FE-8A399231CB3D"), item0.Data.Key); + Assert.AreEqual("home", item0.Data.ContentType.Alias); var item1 = converted.Layout.ElementAt(1); - var item1Data = converted.GetData(item1.Udi); - Assert.AreEqual(Guid.Parse("0A4A416E-547D-464F-ABCC-6F345C17809A"), item1Data.Key); - Assert.AreEqual("home", item1Data.ContentType.Alias); + Assert.AreEqual(Guid.Parse("0A4A416E-547D-464F-ABCC-6F345C17809A"), item1.Data.Key); + Assert.AreEqual("home", item1.Data.ContentType.Alias); } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs index 1e013e851c..d4e130cc0d 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -114,7 +114,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters if (element != null && string.IsNullOrWhiteSpace(blockConfig.SettingsElementTypeAlias)) element = null; - var layoutRef = new BlockListLayoutReference(udi, element); + var layoutRef = new BlockListLayoutReference(udi, data, element); layout.Add(layoutRef); } From 1b53b5c93869a88e0b6892e9b7ff7775c7bd28c0 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 2 Apr 2020 11:13:36 +0100 Subject: [PATCH 098/508] Use the .Data propertry as opposed to GetData in this PartialView --- src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml b/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml index a9f66d7419..d8a304826f 100644 --- a/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml @@ -8,7 +8,7 @@ @foreach (var layout in Model.Layout) { if (layout?.Udi == null) { continue; } - var data = Model.GetData(layout.Udi); + var data = layout.Data; @Html.Partial("BlockList/" + data.ContentType.Alias, (data, layout.Settings)) }
    From 5dfa998109a684439486d32c84c2a1c05764dcc6 Mon Sep 17 00:00:00 2001 From: Benjamin Carleski Date: Thu, 2 Apr 2020 14:29:52 -0700 Subject: [PATCH 099/508] Fix block list test failures --- .../BlockListPropertyValueConverterTests.cs | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs index f63485f4bf..6a7ec33a5a 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -24,11 +24,15 @@ namespace Umbraco.Tests.PropertyEditors /// private IPublishedSnapshotAccessor GetPublishedSnapshotAccessor() { - var homeContentType = Mock.Of(x => + var test1ContentType = Mock.Of(x => x.IsElement == true - && x.Alias == "home"); + && x.Alias == "Test1"); + var test2ContentType = Mock.Of(x => + x.IsElement == true + && x.Alias == "Test2"); var contentCache = new Mock(); - contentCache.Setup(x => x.GetContentType("home")).Returns(homeContentType); + contentCache.Setup(x => x.GetContentType("Test1")).Returns(test1ContentType); + contentCache.Setup(x => x.GetContentType("Test2")).Returns(test2ContentType); var publishedSnapshot = Mock.Of(x => x.Content == contentCache.Object); var publishedSnapshotAccessor = Mock.Of(x => x.PublishedSnapshot == publishedSnapshot); return publishedSnapshotAccessor; @@ -254,8 +258,8 @@ data: []}"; }, data: [ { - 'contentTypeAlias': 'home', - 'key': '1304E1DD-AC87-4396-84FE-8A399231CB3D' + 'contentTypeAlias': 'Test1', + 'udi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D' } ] }"; @@ -265,7 +269,7 @@ data: []}"; Assert.AreEqual(1, converted.Data.Count()); var item0 = converted.Data.ElementAt(0); Assert.AreEqual(Guid.Parse("1304E1DD-AC87-4396-84FE-8A399231CB3D"), item0.Key); - Assert.AreEqual("home", item0.ContentType.Alias); + Assert.AreEqual("Test1", item0.ContentType.Alias); Assert.AreEqual(1, converted.Layout.Count()); var layout0 = converted.Layout.ElementAt(0); Assert.IsNull(layout0.Settings); @@ -296,16 +300,16 @@ data: []}"; }, data: [ { - 'contentTypeAlias': 'home', - 'key': '1304E1DD-AC87-4396-84FE-8A399231CB3D' + 'contentTypeAlias': 'Test1', + 'udi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D' }, { - 'contentTypeAlias': 'home', - 'key': 'E05A0347-0442-4AB3-A520-E048E6197E79' + 'contentTypeAlias': 'Test2', + 'udi': 'umb://element/E05A034704424AB3A520E048E6197E79' }, { - 'contentTypeAlias': 'home', - 'key': '0A4A416E-547D-464F-ABCC-6F345C17809A' + 'contentTypeAlias': 'Test2', + 'udi': 'umb://element/0A4A416E547D464FABCC6F345C17809A' } ] }"; @@ -318,11 +322,11 @@ data: []}"; var item0 = converted.Layout.ElementAt(0); Assert.AreEqual(Guid.Parse("1304E1DD-AC87-4396-84FE-8A399231CB3D"), item0.Data.Key); - Assert.AreEqual("home", item0.Data.ContentType.Alias); + Assert.AreEqual("Test1", item0.Data.ContentType.Alias); var item1 = converted.Layout.ElementAt(1); Assert.AreEqual(Guid.Parse("0A4A416E-547D-464F-ABCC-6F345C17809A"), item1.Data.Key); - Assert.AreEqual("home", item1.Data.ContentType.Alias); + Assert.AreEqual("Test2", item1.Data.ContentType.Alias); } From a87a6caf85ecc0b821e089b47451089e365b15f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 3 Apr 2020 12:36:11 +0200 Subject: [PATCH 100/508] Just parsing layout as model for partial views. --- .../Views/Partials/BlockList/Default.cshtml | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml b/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml index d8a304826f..e8a03a7dcd 100644 --- a/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml @@ -1,14 +1,14 @@ -@inherits UmbracoViewPage -@using ContentModels = Umbraco.Web.PublishedModels; -@using Umbraco.Core.Models.Blocks -@{ - if (Model?.Layout == null || !Model.Layout.Any()) { return; } -} -
    - @foreach (var layout in Model.Layout) - { - if (layout?.Udi == null) { continue; } - var data = layout.Data; - @Html.Partial("BlockList/" + data.ContentType.Alias, (data, layout.Settings)) - } -
    +@inherits UmbracoViewPage +@using ContentModels = Umbraco.Web.PublishedModels; +@using Umbraco.Core.Models.Blocks +@{ + if (Model?.Layout == null || !Model.Layout.Any()) { return; } +} +
    + @foreach (var layout in Model.Layout) + { + if (layout?.Udi == null) { continue; } + var data = layout.Data; + @Html.Partial("BlockList/" + data.ContentType.Alias, layout) + } +
    From fb175c5af845a589e6064e42bbe6714474087183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 6 Apr 2020 09:32:03 +0200 Subject: [PATCH 101/508] minor adjustments --- .../imageblock.editor.controller.js | 21 +++++++++++++++++++ .../imageblock/imageblock.editor.html | 10 +++++++-- .../imageblock/imageblock.editor.less | 7 +++++-- .../textareablock.editor.controller.js | 3 ++- .../textareablock/textareablock.editor.html | 2 +- .../textareablock/textareablock.editor.less | 4 ++-- .../blocklist/blocklist.component.js | 4 ++-- 7 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.controller.js new file mode 100644 index 0000000000..8ebf7ec8c4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.controller.js @@ -0,0 +1,21 @@ + +(function () { + 'use strict'; + + function ImageBlockEditor($scope, entityResource) { + + const bc = this; + + var firstProperty = $scope.block.content.variants[0].tabs[0].properties[0]; + + entityResource.getById(firstProperty.value, "Media").then(function(ent) { + console.log(ent) + bc.imageUrl = ent.metaData.MediaPath; + }); + + } + + angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditor.ImageBlockEditor", ImageBlockEditor); + +})(); + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html index a1977cec55..32db64b16b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html @@ -1,3 +1,9 @@ - diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less index 99b1bb53f2..2ea03bd703 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less @@ -1,10 +1,13 @@ .blockelement-imageblock-editor { - padding-top: 2px; - padding-bottom: 2px; + width: 100%; + min-height: 42px; + padding-bottom: 10px; + padding-top: 10px; img { width: 100%; + max-width: 500px; border: none; resize: none; border-radius: @baseBorderRadius; diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js index 9fc213cd4a..1b074a0cb6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js @@ -5,7 +5,7 @@ angular.module("umbraco") var vm = this; - vm.firstProperty = $scope.blockItem.content.variants[0].tabs[0].properties[0]; + vm.firstProperty = $scope.block.content.variants[0].tabs[0].properties[0]; /* vm.onBlur = function() { if (vm.firstProperty.value === null || vm.firstProperty.value === "") { @@ -13,6 +13,7 @@ angular.module("umbraco") } } */ + // TODO: if text is empty and user hits backspace, then remove this block. } ); diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html index bbcef1643b..ca49a3f5b8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html @@ -1,5 +1,5 @@ -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less index 492025274a..fe11d0cd0c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less @@ -1,8 +1,8 @@ .blockelement-textareablock-editor { width: 100%; - padding-bottom: 24px; - padding-top: 24px; + padding-bottom: 10px; + padding-top: 10px; padding-left: 24px; padding-right: 24px; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index c7ac475d46..2b25a8b013 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -121,7 +121,7 @@ if (block === null) return null; // Lets apply fallback views, and make the view available directly on the blockModel. - block.view = block.config.view || vm.model.config.useInlineEditingAsDefault ? "views/blockelements/inlineblock/inlineblock.editor.html" : "views/blockelements/labelblock/labelblock.editor.html"; + block.view = block.config.view || (vm.model.config.useInlineEditingAsDefault ? "views/blockelements/inlineblock/inlineblock.editor.html" : "views/blockelements/labelblock/labelblock.editor.html"); block.showSettings = block.config.settingsElementTypeAlias != null; @@ -256,7 +256,7 @@ added = addNewBlock(createIndex, model.selectedItem.alias); } vm.blockTypePicker.close(); - if (added && vm.blocks.length > createIndex) { + if (added && vm.model.config.useInlineEditingAsDefault !== true && vm.blocks.length > createIndex) { editBlock(vm.blocks[createIndex]); } }, From 0fa045dc942234fcf3a3cc3ae001203a61c4afd9 Mon Sep 17 00:00:00 2001 From: Benjamin Carleski Date: Tue, 7 Apr 2020 11:53:41 -0700 Subject: [PATCH 102/508] Remove DB migrations so that they can be reviewed as a block --- src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs | 3 --- src/Umbraco.Core/Umbraco.Core.csproj | 1 - 2 files changed, 4 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index f0fcdc3d52..f65a60197f 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -194,9 +194,6 @@ namespace Umbraco.Core.Migrations.Upgrade To("{a78e3369-8ea3-40ec-ad3f-5f76929d2b20}"); - - // to 8.7.0... - To("{DFA35FA2-BFBB-433F-84E5-BD75940CDDF6}"); //FINAL } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 73bd8a1126..18c51fd905 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -131,7 +131,6 @@ - From f78e4fcdf67992baf7628c2053bb9347e73ca2be Mon Sep 17 00:00:00 2001 From: Benjamin Carleski Date: Tue, 7 Apr 2020 11:55:45 -0700 Subject: [PATCH 103/508] Add migrations for new block editor --- .../Migrations/Upgrade/UmbracoPlan.cs | 5 + .../Upgrade/V_8_7_0/ColorPickerPreValues.cs | 106 ++++++++++++++++++ .../Upgrade/V_8_7_0/ConvertToElements.cs | 92 +++++++++++++++ .../V_8_7_0/StackedContentToBlockList.cs | 74 ++++++++++-- src/Umbraco.Core/Umbraco.Core.csproj | 3 + 5 files changed, 268 insertions(+), 12 deletions(-) create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ColorPickerPreValues.cs create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ConvertToElements.cs diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index f65a60197f..1d30efa573 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -194,6 +194,11 @@ namespace Umbraco.Core.Migrations.Upgrade To("{a78e3369-8ea3-40ec-ad3f-5f76929d2b20}"); + + // to 8.7.0... + To("{DFA35FA2-BFBB-433F-84E5-BD75940CDDF6}"); + To("{711AC937-B11C-47AC-8D4A-5B8868A3C2C6}"); + To("{DA434576-3DEF-46D7-942A-CE34D7F7FB8A}"); //FINAL } } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ColorPickerPreValues.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ColorPickerPreValues.cs new file mode 100644 index 0000000000..6e959e86d7 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ColorPickerPreValues.cs @@ -0,0 +1,106 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Linq; +using System.Text.RegularExpressions; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 +{ + public class ColorPickerPreValues : MigrationBase + { + private static readonly Regex OldPreValuesPattern1 = new Regex("\\s*{(\\s*\"[0-9]+\"\\s*:\\s*\"[0-9a-fA-F]+\"\\s*,)*\\s*\"useLabel\"\\s*:\\s*\"[01]\"\\s*}\\s*", RegexOptions.Compiled); + private static readonly Regex OldPreValuesPattern2 = new Regex("\\s*{(\\s*\"[0-9]+\"\\s*:\\s*{\\s*\"value\"\\s*:\\s*\"[0-9a-fA-F]+\"\\s*(,\\s*\"label\"\\s*:\\s*\"[^\"]*\"\\s*)?(,\\s*\"sortOrder\"\\s*:\\s*[0-9]+\\s*)?}\\s*,)*\\s*\"useLabel\"\\s*:\\s*\"[01]\"\\s*}\\s*", RegexOptions.Compiled); + + public ColorPickerPreValues(IMigrationContext context) : base(context) + { + } + + public override void Migrate() + { + var sql = Sql() + .Select() + .From() + .Where(d => d.EditorAlias == Constants.PropertyEditors.Aliases.ColorPicker); + + var dtos = Database.Fetch(sql); + + foreach (var dto in dtos) + { + if (dto.Configuration.IsNullOrWhiteSpace()) continue; + + if (OldPreValuesPattern1.IsMatch(dto.Configuration)) ConvertPreValues(dto, ConvertStyle1); + else if (OldPreValuesPattern2.IsMatch(dto.Configuration)) ConvertPreValues(dto, ConvertStyle2); + else continue; + + Database.Update(dto); + } + } + + private void ConvertPreValues(DataTypeDto dto, Func converter) + { + var obj = JObject.Parse(dto.Configuration); + var config = new ColorPickerConfiguration(); + var id = 0; + + foreach (var prop in obj.Properties()) + { + if (prop.Name.ToLowerInvariant() == "uselabel") + { + config.UseLabel = prop.Value.ToString() == "1"; + } + else + { + id++; + config.Items.Add(new ValueListConfiguration.ValueListItem + { + Id = id, + Value = JsonConvert.SerializeObject(converter(id, prop.Value)) + }); + } + } + + dto.Configuration = JsonConvert.SerializeObject(config); + } + + private ItemValue ConvertStyle1(int index, JToken token) + { + var value = token.ToString(); + return new ItemValue + { + Color = value, + Label = value, + SortOrder = index + }; + } + + private ItemValue ConvertStyle2(int index, JToken token) + { + var obj = (JObject)token; + var value = obj["value"].ToString(); + var label = obj["label"]?.ToString(); + var order = obj["sortOrder"]?.ToString(); + + return new ItemValue + { + Color = value, + Label = label.IsNullOrWhiteSpace() ? value : label, + SortOrder = int.TryParse(order, out var o) ? o : index + }; + } + + private class ItemValue + { + [JsonProperty("value")] + public string Color { get; set; } + + [JsonProperty("label")] + public string Label { get; set; } + + [JsonProperty("sortOrder")] + public int SortOrder { get; set; } + } + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ConvertToElements.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ConvertToElements.cs new file mode 100644 index 0000000000..e42453a3fe --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ConvertToElements.cs @@ -0,0 +1,92 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 +{ + public class ConvertToElements : MigrationBase + { + public ConvertToElements(IMigrationContext context) : base(context) + { + } + + public override void Migrate() + { + // Get all document type IDs by alias + var docTypes = Database.Fetch(); + var docTypeMap = new Dictionary(docTypes.Count); + docTypes.ForEach(d => docTypeMap[d.Alias] = d.NodeId); + + // Find all Nested Content or Block List data types + var dataTypes = GetDataTypes(Constants.PropertyEditors.Aliases.NestedContent, Constants.PropertyEditors.Aliases.BlockList); + + // Find all document types listed in each + var elementTypeIds = dataTypes.SelectMany(d => GetDocTypeIds(d.Configuration, docTypeMap)).ToList(); + + // Find all compositions those document types use + var parentElementTypeIds = Database.Fetch(Sql() + .Select() + .From() + .WhereIn(c => c.ChildId, elementTypeIds) + ).Select(c => c.ParentId); + + elementTypeIds = elementTypeIds.Union(parentElementTypeIds).ToList(); + + // Convert all those document types to element type + foreach (var docType in docTypes) + { + if (!elementTypeIds.Contains(docType.NodeId)) continue; + + docType.IsElement = true; + Database.Update(docType); + } + } + + private List GetDataTypes(params string[] aliases) + { + var sql = Sql() + .Select() + .From() + .WhereIn(d => d.EditorAlias, aliases); + + return Database.Fetch(sql); + } + + private IEnumerable GetDocTypeIds(string configuration, Dictionary idMap) + { + if (configuration.IsNullOrWhiteSpace() || configuration[0] != '{') return Enumerable.Empty(); + + var obj = JObject.Parse(configuration); + if (obj["contentTypes"] is JArray ncArr) + { + var arr = ncArr.ToObject(); + return arr.Select(i => idMap.TryGetValue(i.Alias, out var id) ? id : 0).Where(i => i != 0); + } + else if (obj["blocks"] is JArray blArr) + { + var arr = blArr.ToObject(); + return arr.Select(i => idMap.TryGetValue(i.Alias, out var id) ? id : 0).Where(i => i != 0); + } + + return Enumerable.Empty(); + } + + public class ContentType + { + [JsonProperty("ncAlias")] + public string Alias { get; set; } + } + + public class BlockConfiguration + { + [JsonProperty("contentTypeAlias")] + public string Alias { get; set; } + } + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs index 076b4f205c..43ee85c2ea 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs @@ -20,6 +20,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 public override void Migrate() { + // Convert all Stacked Content properties to Block List properties, both in the data types and in the property data var refreshCache = Migrate(GetDataTypes("Our.Umbraco.StackedContent"), GetKnownDocumentTypes()); // if some data types have been updated directly in the database (editing DataTypeDto and/or PropertyDataDto), @@ -38,7 +39,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 return Database.Fetch(sql); } - private Dictionary GetKnownDocumentTypes() + private Dictionary GetKnownDocumentTypes() { var sql = Sql() .Select(r => r.Select(x => x.NodeDto)) @@ -47,12 +48,40 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 .On(c => c.NodeId, n => n.NodeId); var types = Database.Fetch(sql); - var map = new Dictionary(types.Count); - types.ForEach(t => map[t.NodeDto.UniqueId] = t.Alias); - return map; + var typeMap = new Dictionary(types.Count); + types.ForEach(t => typeMap[t.NodeId] = t); + + sql = Sql() + .Select() + .From(); + var joins = Database.Fetch(sql); + // Find all relationships between types, either inherited or composited + var joinLk = joins + .Union(types + .Where(t => typeMap.ContainsKey(t.NodeDto.ParentId)) + .Select(t => new ContentType2ContentTypeDto { ChildId = t.NodeId, ParentId = t.NodeDto.ParentId })) + .ToLookup(j => j.ChildId, j => j.ParentId); + + sql = Sql() + .Select(r => r.Select(x => x.DataTypeDto)) + .From() + .InnerJoin() + .On(c => c.DataTypeId, n => n.NodeId) + .Where(d => d.EditorAlias == Constants.PropertyEditors.Aliases.NestedContent); + var props = Database.Fetch(sql); + // Get all nested content property aliases by content type ID + var propLk = props.ToLookup(p => p.ContentTypeId, p => p.Alias); + + var knownMap = new Dictionary(types.Count); + types.ForEach(t => knownMap[t.NodeDto.UniqueId] = new KnownContentType + { + Alias = t.Alias, + NestedContentProperties = propLk[t.NodeId].Union(joinLk[t.NodeId].SelectMany(r => propLk[r])).ToArray() + }); + return knownMap; } - private bool Migrate(IEnumerable dataTypesToMigrate, Dictionary knownDocumentTypes) + private bool Migrate(IEnumerable dataTypesToMigrate, Dictionary knownDocumentTypes) { var refreshCache = false; @@ -73,14 +102,14 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 return refreshCache; } - private BlockListConfiguration UpdateConfiguration(DataTypeDto dataType, Dictionary knownDocumentTypes) + private BlockListConfiguration UpdateConfiguration(DataTypeDto dataType, Dictionary knownDocumentTypes) { var old = JsonConvert.DeserializeObject(dataType.Configuration); var config = new BlockListConfiguration { Blocks = old.ContentTypes?.Select(t => new BlockListConfiguration.BlockConfiguration { - Alias = knownDocumentTypes[t.IcContentTypeGuid], + Alias = knownDocumentTypes[t.IcContentTypeGuid].Alias, Label = t.NameTemplate }).ToArray(), UseInlineEditingAsDefault = old.SingleItemMode == "1" || old.SingleItemMode == bool.TrueString @@ -96,7 +125,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 return config; } - private void UpdatePropertyData(DataTypeDto dataType, BlockListConfiguration config, Dictionary knownDocumentTypes) + private void UpdatePropertyData(DataTypeDto dataType, BlockListConfiguration config, Dictionary knownDocumentTypes) { // get property data dtos var propertyDataDtos = Database.Fetch(Sql() @@ -115,7 +144,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 } - private bool UpdatePropertyDataDto(PropertyDataDto dto, BlockListConfiguration config, Dictionary knownDocumentTypes) + private bool UpdatePropertyDataDto(PropertyDataDto dto, BlockListConfiguration config, Dictionary knownDocumentTypes) { var model = new SimpleModel(); @@ -202,18 +231,33 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 [JsonProperty("data")] public List Data { get; } = new List(); - public void AddDataItem(JObject obj, Dictionary knownDocumentTypes) + public void AddDataItem(JObject obj, Dictionary knownDocumentTypes) { if (!Guid.TryParse(obj["key"].ToString(), out var key)) throw new ArgumentException("Could not find a valid key in the data item"); if (!Guid.TryParse(obj["icContentTypeGuid"].ToString(), out var ctGuid)) throw new ArgumentException("Could not find a valid content type GUID in the data item"); - if (!knownDocumentTypes.TryGetValue(ctGuid, out var ctAlias)) throw new ArgumentException($"Unknown content type GUID '{ctGuid}'"); + if (!knownDocumentTypes.TryGetValue(ctGuid, out var ct)) throw new ArgumentException($"Unknown content type GUID '{ctGuid}'"); obj.Remove("key"); obj.Remove("icContentTypeGuid"); var udi = new GuidUdi(Constants.UdiEntityType.Element, key).ToString(); obj["udi"] = udi; - obj["contentTypeAlias"] = ctAlias; + obj["contentTypeAlias"] = ct.Alias; + + if (ct.NestedContentProperties != null && ct.NestedContentProperties.Length > 0) + { + // Nested content inside a stacked content item used to be stored as a deserialized string of the JSON array + // Now we store the content as the raw JSON array, so we need to convert from the string form to the array + foreach (var prop in ct.NestedContentProperties) + { + var val = obj[prop]; + var value = val?.ToString(); + if (val != null && val.Type == JTokenType.String && !value.IsNullOrWhiteSpace() && value[0] == '[') + obj[prop] = JArray.Parse(value); + else if (val.Type != JTokenType.Array) + obj[prop] = new JArray(); + } + } Data.Add(obj); Layout.Refs.Add(new SimpleLayout.SimpleLayoutRef { Udi = udi }); @@ -231,5 +275,11 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 } } } + + private class KnownContentType + { + public string Alias { get; set; } + public string[] NestedContentProperties { get; set; } + } } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 18c51fd905..1ae5267b6a 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -131,6 +131,9 @@ + + + From cec7251bc3c1c5fb5d32704e144bc55361beae86 Mon Sep 17 00:00:00 2001 From: Benjamin Carleski Date: Tue, 7 Apr 2020 14:45:06 -0700 Subject: [PATCH 104/508] Update default rendering partial view --- src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml b/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml index e8a03a7dcd..3bc6308b55 100644 --- a/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml @@ -1,5 +1,4 @@ @inherits UmbracoViewPage -@using ContentModels = Umbraco.Web.PublishedModels; @using Umbraco.Core.Models.Blocks @{ if (Model?.Layout == null || !Model.Layout.Any()) { return; } @@ -9,6 +8,6 @@ { if (layout?.Udi == null) { continue; } var data = layout.Data; - @Html.Partial("BlockList/" + data.ContentType.Alias, layout) + @Html.Partial("BlockList/Components/" + data.ContentType.Alias, layout) }
    From cf418a8097fc00b3068ef7f81b494580ecf3e9e8 Mon Sep 17 00:00:00 2001 From: Benjamin Carleski Date: Tue, 7 Apr 2020 16:36:10 -0700 Subject: [PATCH 105/508] Add error handling to default template --- .../Views/Partials/BlockList/Default.cshtml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml b/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml index 3bc6308b55..22fd4283de 100644 --- a/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/BlockList/Default.cshtml @@ -8,6 +8,13 @@ { if (layout?.Udi == null) { continue; } var data = layout.Data; - @Html.Partial("BlockList/Components/" + data.ContentType.Alias, layout) + try + { + @Html.Partial("BlockList/Components/" + data.ContentType.Alias, (data, layout.Settings)) + } + catch (Exception ex) + { + global::Umbraco.Core.Composing.Current.Logger.Error(typeof(BlockListModel), ex, "Could not display block list component for content type {0}", data?.ContentType?.Alias); + } }
    From d6c95bb38069ab15724cf898d374dcc0540ea597 Mon Sep 17 00:00:00 2001 From: Benjamin Carleski Date: Tue, 7 Apr 2020 16:43:06 -0700 Subject: [PATCH 106/508] Handle color picker data in stacked content --- .../V_8_7_0/StackedContentToBlockList.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs index 43ee85c2ea..a3a8bd62c4 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs @@ -67,16 +67,16 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 .From() .InnerJoin() .On(c => c.DataTypeId, n => n.NodeId) - .Where(d => d.EditorAlias == Constants.PropertyEditors.Aliases.NestedContent); + .WhereIn(d => d.EditorAlias, new[] { Constants.PropertyEditors.Aliases.NestedContent, Constants.PropertyEditors.Aliases.ColorPicker }); var props = Database.Fetch(sql); - // Get all nested content property aliases by content type ID + // Get all nested content and color picker property aliases by content type ID var propLk = props.ToLookup(p => p.ContentTypeId, p => p.Alias); var knownMap = new Dictionary(types.Count); types.ForEach(t => knownMap[t.NodeDto.UniqueId] = new KnownContentType { Alias = t.Alias, - NestedContentProperties = propLk[t.NodeId].Union(joinLk[t.NodeId].SelectMany(r => propLk[r])).ToArray() + StringToRawProperties = propLk[t.NodeId].Union(joinLk[t.NodeId].SelectMany(r => propLk[r])).ToArray() }); return knownMap; } @@ -244,18 +244,16 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 obj["udi"] = udi; obj["contentTypeAlias"] = ct.Alias; - if (ct.NestedContentProperties != null && ct.NestedContentProperties.Length > 0) + if (ct.StringToRawProperties != null && ct.StringToRawProperties.Length > 0) { // Nested content inside a stacked content item used to be stored as a deserialized string of the JSON array // Now we store the content as the raw JSON array, so we need to convert from the string form to the array - foreach (var prop in ct.NestedContentProperties) + foreach (var prop in ct.StringToRawProperties) { var val = obj[prop]; var value = val?.ToString(); - if (val != null && val.Type == JTokenType.String && !value.IsNullOrWhiteSpace() && value[0] == '[') - obj[prop] = JArray.Parse(value); - else if (val.Type != JTokenType.Array) - obj[prop] = new JArray(); + if (val != null && val.Type == JTokenType.String && !value.IsNullOrWhiteSpace()) + obj[prop] = JsonConvert.DeserializeObject(value); } } @@ -279,7 +277,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 private class KnownContentType { public string Alias { get; set; } - public string[] NestedContentProperties { get; set; } + public string[] StringToRawProperties { get; set; } } } } From b26db075a3777f917d9e16cf2c4e5a9f7d4dfc71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 8 Apr 2020 11:58:25 +0200 Subject: [PATCH 107/508] BlockList PreValue Editor opens configurations as overlay --- .../common/resources/elementtype.resource.js | 11 +- src/Umbraco.Web.UI.Client/src/less/belle.less | 5 +- .../src/less/variables.less | 2 + .../blockcard/blockcard.component.html | 11 + .../blockcard/blockcard.component.js | 28 ++ .../blockcard/blockcard.component.less | 120 +++++++ .../blockcard/umb-block-card-grid.less | 15 + .../blocklist/blocklist.component.less | 5 +- ...blocklist.blockconfiguration.controller.js | 208 ++++++++++++ .../blocklist.blockconfiguration.html | 30 ++ .../blocklist.blockconfiguration.less | 28 ++ ...t.blockconfiguration.overlay.controller.js | 190 +++++++++++ .../blocklist.blockconfiguration.overlay.html | 310 ++++++++++++++++++ ...blocklist.blockconfiguration.overlay.less} | 59 +--- .../blocklist.elementtypepicker.controller.js | 285 ---------------- .../prevalue/blocklist.elementtypepicker.html | 81 ----- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 6 + .../Umbraco/config/lang/en_us.xml | 6 + .../Editors/ElementTypeController.cs | 29 ++ .../PropertyEditors/BlockListConfiguration.cs | 16 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 21 files changed, 1022 insertions(+), 424 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/umb-block-card-grid.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html rename src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/{blocklist.elementtypepicker.less => blocklist.blockconfiguration.overlay.less} (68%) delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html create mode 100644 src/Umbraco.Web/Editors/ElementTypeController.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/elementtype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/elementtype.resource.js index 680b75ac78..a235bcda3e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/elementtype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/elementtype.resource.js @@ -9,12 +9,11 @@ function elementTypeResource($q, $http, umbRequestHelper) { getAll: function () { - // TODO: Change this into a real api (ElementTypeApi). This is a temporary fix to get data. - var url = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + "/backoffice/UmbracoApi/NestedContent/GetContentTypes"; - return umbRequestHelper.resourcePromise( - $http.get(url), - 'Failed to retrieve content types' - ); + var url = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + "/backoffice/UmbracoApi/ElementType/GetAll"; + return umbRequestHelper.resourcePromise( + $http.get(url), + 'Failed to retrieve element types' + ); /* return umbRequestHelper.resourcePromise( diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 14a62ae790..6c021203a5 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -198,8 +198,11 @@ // Property Editors +@import "../views/propertyeditors/blockeditor/blockcard/umb-block-card-grid.less"; +@import "../views/propertyeditors/blockeditor/blockcard/blockcard.component.less"; @import "../views/propertyeditors/blocklist/blocklist.component.less"; -@import "../views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less"; +@import "../views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.less"; +@import "../views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.less"; @import "../views/propertyeditors/notsupported/notsupported.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index b645e3113b..c320a31807 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -202,6 +202,7 @@ @ui-icon: @blueNight; @ui-icon-hover: @blueMid; +@ui-drop-area-color: @blueMidLight; // Scaffolding @@ -252,6 +253,7 @@ // Disabled this to keep consistency throughout the backoffice UI. Untill a better solution is thought up, this will do. @baseBorderRadius: 3px; // 2px; +@doubleBorderRadius: 6px; @borderRadiusLarge: 3px; // 6px; @borderRadiusSmall: 3px; // 3px; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.html new file mode 100644 index 0000000000..c66879e864 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.html @@ -0,0 +1,11 @@ + +
    + +
    +
    +
    +
    +
    + + + diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.js new file mode 100644 index 0000000000..98e7303219 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.js @@ -0,0 +1,28 @@ +(function () { + "use strict"; + + angular + .module("umbraco") + .component("umbBlockCard", { + templateUrl: "views/propertyeditors/blockeditor/blockcard/blockcard.component.html", + controller: BlockCardController, + controllerAs: "vm", + transclude: true, + bindings: { + blockConfigModel: "<", + elementTypeModel: "<" + } + }); + + function BlockCardController() { + + var vm = this; + + vm.$onInit = function() { + console.log(vm.blockConfigModel); + console.log(vm.elementTypeModel); + } + + } + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.less new file mode 100644 index 0000000000..5adbded9bb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.less @@ -0,0 +1,120 @@ +.umb-block-card, +umb-block-card { + position: relative; + display: inline-block; + width: 100%; + height: auto; + margin-right: 20px; + margin-bottom: 20px; + background-color: white; + border-radius: @doubleBorderRadius; + box-shadow: 0 1px 2px rgba(0,0,0,.2); + overflow: hidden; + + cursor: pointer; + + &.--isOpen { + &::after { + content: ""; + position: absolute; + border: 2px solid @ui-active-border; + border-radius: @doubleBorderRadius; + top:0; + bottom: 0; + left: 0; + right: 0; + } + } + + &.--sortable-placeholder { + &::after { + content: ""; + position: absolute; + background-color:rgba(@ui-drop-area-color, .05); + border: 2px solid rgba(@ui-drop-area-color, .1); + border-radius: @doubleBorderRadius; + box-shadow: 0 0 4px rgba(@ui-drop-area-color, 0.05); + top:0; + bottom: 0; + left: 0; + right: 0; + animation: umb-block-card--sortable-placeholder 400ms ease-in-out alternate infinite; + @keyframes umb-block-card--sortable-placeholder { + 0% { opacity: 1; } + 100% { opacity: 0.5; } + } + } + box-shadow: none; + } + + .__showcase { + position: relative; + width: 100%; + padding-bottom: 10/16*100%; + background-color: @gray-11; + .__icon { + position: absolute; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + font-size:42px; + } + } + + .__info { + width: 100%; + background-color: #fff; + padding-bottom: 6px; + + .__name { + font-weight: bold; + font-size: 14px; + color: @ui-action-type; + margin-left: 16px; + margin-top: 8px; + margin-bottom: -1px; + } + .__subname { + color: @gray-4; + font-size: 12px; + margin-left: 16px; + margin-bottom: -1px; + } + } + + &:hover { + .__info { + .__name { + color: @ui-action-type-hover; + } + } + } + + .__actions { + position: absolute; + top: 10px; + right: 0; + opacity: 0; + transition: opacity 120ms; + .__action { + display: inline-block; + border-radius: 50%; + width: 28px; + height: 28px; + margin-right: 10px; + background-color: white; + color:@ui-action-type; + &:hover { + color: @ui-action-type-hover; + } + } + } + &:hover, &:focus, &:focus-within { + .__actions { + opacity: 1; + } + } + +} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/umb-block-card-grid.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/umb-block-card-grid.less new file mode 100644 index 0000000000..e4953999fd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/umb-block-card-grid.less @@ -0,0 +1,15 @@ +.umb-block-card-grid { + /* FlexBox Fallback */ + display: flex; + flex-wrap: wrap; + > * { + flex: 1 1 240px; + } + + /* Grid Setup */ + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + grid-auto-rows: minmax(200px, auto); + grid-gap: 20px; + +} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less index d08c862f88..d118e6356c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less @@ -87,10 +87,9 @@ right: 0; left: 0; height: 2px; - animation: umb-block-list__block--create-button_before 800ms ease-in-out infinite; + animation: umb-block-list__block--create-button_before 400ms ease-in-out alternate infinite; @keyframes umb-block-list__block--create-button_before { - 0% { opacity: 0.5; } - 50% { opacity: 1; } + 0% { opacity: 1; } 100% { opacity: 0.5; } } } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js new file mode 100644 index 0000000000..a058f465e0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js @@ -0,0 +1,208 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.BlockList.BlockConfigurationController + * @function + * + * @description + * The controller for the content type editor property settings dialog + */ + +(function () { + "use strict"; + + function TransferProperties(fromObject, toObject) { + for (var p in fromObject) { + toObject[p] = fromObject[p]; + } + } + + function BlockConfigurationController($scope, elementTypeResource, overlayService, localizationService, editorService) { + + var vm = this; + + vm.enableAddBlock = true; + vm.openBlock = null; + + function evaluateStatus() { + + if (!vm.elementTypes) return;// cancel if elementTypes isnt loaded jet. + + vm.enableAddBlock = vm.getAvailableElementTypes().length > 0; + + } + + function onInit() { + + if (!$scope.model.value) { + $scope.model.value = []; + } + + loadElementTypes(); + + } + + function loadElementTypes() { + return elementTypeResource.getAll().then(function (elementTypes) { + vm.elementTypes = elementTypes; + evaluateStatus(); + }); + } + + vm.requestRemoveBlockByIndex = function (index) { + localizationService.localizeMany(["general_delete", "blockEditor_confirmDeleteBlockMessage", "blockEditor_confirmDeleteBlockNotice"]).then(function (data) { + var contentElementType = vm.getElementTypeByAlias($scope.model.value[index].contentTypeAlias); + overlayService.confirmDelete({ + title: data[0], + content: localizationService.tokenReplace(data[1], [contentElementType.name]), + confirmMessage: data[2], + close: function () { + overlayService.close(); + }, + submit: function () { + vm.removeBlockByIndex(index); + overlayService.close(); + } + }); + }); + } + + vm.removeBlockByIndex = function (index) { + $scope.model.value.splice(index, 1); + }; + + vm.sortableOptions = { + "ui-floating": true, + items: "umb-block-card", + cursor: "grabbing", + placeholder: 'umb-block-card --sortable-placeholder' + }; + + + vm.getAvailableElementTypes = function () { + return vm.elementTypes.filter(function (type) { + return !$scope.model.value.find(function (entry) { + return type.alias === entry.contentTypeAlias; + }); + }); + }; + + vm.getElementTypeByAlias = function(alias) { + return _.find(vm.elementTypes, function (type) { + return type.alias === alias; + }); + }; + + vm.openAddDialog = function ($event, entry) { + + //we have to add the alias to the objects (they are stored as contentTypeAlias) + var selectedItems = _.each($scope.model.value, function (obj) { + obj.alias = obj.contentTypeAlias; + return obj; + }); + + var availableItems = vm.getAvailableElementTypes() + + var elemTypeSelectorOverlay = { + view: "itempicker", + title: "no title jet", + availableItems: availableItems, + selectedItems: selectedItems, + createNewItem: { + action: function() { + overlayService.close(); + vm.createElementTypeAndAdd(vm.addBlockFromElementTypeAlias); + }, + icon: "icon-add", + name: "Create new" + }, + position: "target", + event: $event, + size: availableItems.length < 7 ? "small" : "medium", + submit: function (overlay) { + vm.addBlockFromElementTypeAlias(overlay.selectedItem.alias); + overlayService.close(); + }, + close: function () { + overlayService.close(); + } + }; + + overlayService.open(elemTypeSelectorOverlay); + }; + + vm.createElementTypeAndAdd = function(callback) { + const editor = { + create: true, + infiniteMode: true, + isElement: true, + submit: function (model) { + loadElementTypes().then( function () { + callback(model.documentTypeAlias); + }); + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.documentTypeEditor(editor); + } + + vm.addBlockFromElementTypeAlias = function(alias) { + + var entry = { + "contentTypeAlias": alias, + "view": null, + "labelTemplate": "", + "settingsElementTypeAlias": null + }; + + $scope.model.value.push(entry); + }; + + + + + + vm.openBlockOverlay = function (block) { + + localizationService.localize("blockEditor_blockConfigurationOverlayTitle", [vm.getElementTypeByAlias(block.contentTypeAlias).name]).then(function (data) { + + var clonedBlockData = angular.copy(block); + vm.openBlock = block; + + var overlayModel = { + block: clonedBlockData, + title: data, + view: "views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html", + size: "small", + submit: function(overlayModel) { + TransferProperties(overlayModel.block, block);// transfer properties back to block object. (Doing this cause we dont know if block object is added to model jet, therefor we cant use index or replace the object.) + overlayModel.close(); + }, + close: function() { + editorService.close(); + vm.openBlock = null; + } + }; + + // open property settings editor + editorService.open(overlayModel); + + }); + + }; + + + + onInit(); + + $scope.$watchCollection('model.value', function(newVal, oldVal) { + evaluateStatus(); + }); + + } + + angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockList.BlockConfigurationController", BlockConfigurationController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html new file mode 100644 index 0000000000..06e9714b77 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html @@ -0,0 +1,30 @@ +
    + +
    + + +
    + +
    +
    + + +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.less new file mode 100644 index 0000000000..f4d9caa73b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.less @@ -0,0 +1,28 @@ +.umb-block-list-block-configuration { + + .__add-button { + position: relative; + display: inline-flex; + width: 100%; + height: auto; + margin-right: 20px; + margin-bottom: 20px; + + color: @ui-action-discreet-type; + border: 1px dashed @ui-action-discreet-border; + border-radius: @doubleBorderRadius; + + align-items: center; + justify-content: center; + + padding: 5px 15px; + box-sizing: border-box; + font-weight: bold; + } + + .__add-button:hover { + color: @ui-action-discreet-type-hover; + border-color: @ui-action-discreet-border-hover; + } + +} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js new file mode 100644 index 0000000000..015d25e9fb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js @@ -0,0 +1,190 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.BlockList.BlockConfigurationOverlayController + * @function + * + * @description + * The controller for the content type editor property settings dialog + */ + +(function () { + "use strict"; + + function BlockConfigurationOverlayController($scope, overlayService, localizationService, editorService, elementTypeResource) { + + var vm = this; + vm.block = $scope.model.block; + + loadElementTypes(); + + function loadElementTypes() { + return elementTypeResource.getAll().then(function (elementTypes) { + vm.elementTypes = elementTypes; + }); + } + + vm.getElementTypeByAlias = function(alias) { + return _.find(vm.elementTypes, function (type) { + return type.alias === alias; + }); + }; + + vm.openElementType = function(elementTypeAlias) { + var elementTypeId = vm.getElementTypeByAlias(elementTypeAlias).id; + const editor = { + id: elementTypeId, + submit: function (model) { + loadElementTypes(); + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.documentTypeEditor(editor); + } + + vm.addSettingsForBlock = function ($event, block) { + + var elemTypeSelectorOverlay = { + view: "itempicker", + title: "Pick settings (missing translation)", + availableItems: vm.elementTypes, + position: "target", + event: $event, + size: vm.elementTypes.length < 7 ? "small" : "medium", + createNewItem: { + action: function() { + overlayService.close(); + vm.createElementTypeAndAdd((alias) => { + vm.applySettingsToBlock(block, alias); + }); + }, + icon: "icon-add", + name: "Create new" + }, + submit: function (overlay) { + vm.applySettingsToBlock(block, overlay.selectedItem.alias); + overlayService.close(); + }, + close: function () { + overlayService.close(); + } + }; + + overlayService.open(elemTypeSelectorOverlay); + }; + vm.applySettingsToBlock = function(block, alias) { + block.settingsElementTypeAlias = alias; + }; + + vm.requestRemoveSettingsForBlock = function(block) { + localizationService.localizeMany(["general_remove", "defaultdialogs_confirmremoveusageof"]).then(function (data) { + + var settingsElementType = vm.getElementTypeByAlias(entry.settingsElementTypeAlias); + + overlayService.confirmRemove({ + title: data[0], + content: localizationService.tokenReplace(data[1], [settingsElementType.name]), + close: function () { + overlayService.close(); + }, + submit: function () { + vm.removeSettingsForEntry(entry); + overlayService.close(); + } + }); + }); + }; + vm.removeSettingsForEntry = function(entry) { + entry.settingsElementTypeAlias = null; + }; + + + vm.addViewForBlock = function(block) { + const filePicker = { + title: "Select view (TODO need translation)", + section: "settings", + treeAlias: "files", + entityType: "file", + isDialog: true, + filter: function (i) { + return i.name.indexOf(".html" !== -1); + }, + select: function (file) { + console.log(file); + block.view = file.name; + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.treePicker(filePicker); + } + vm.requestRemoveViewForBlock = function(block) { + localizationService.localizeMany(["general_remove", "defaultdialogs_confirmremoveusageof"]).then(function (data) { + overlayService.confirmRemove({ + title: data[0], + content: localizationService.tokenReplace(data[1], [block.view]), + close: function () { + overlayService.close(); + }, + submit: function () { + vm.removeViewForBlock(block); + overlayService.close(); + } + }); + }); + }; + vm.removeViewForBlock = function(block) { + block.view = null; + }; + + + + vm.addThumbnailForBlock = function(block) { + const thumbnailPicker = { + title: "Select thumbnail (TODO need translation)", + section: "settings", + treeAlias: "files", + entityType: "file", + isDialog: true, + filter: function (i) { + return (i.name.indexOf(".jpg") !== -1 || i.name.indexOf(".jpeg") !== -1 || i.name.indexOf(".png") !== -1 || i.name.indexOf(".svg") !== -1 || i.name.indexOf(".webp") !== -1 || i.name.indexOf(".gif") !== -1); + }, + select: function (file) { + block.thumbnail = file.name; + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.treePicker(thumbnailPicker); + } + vm.removeThumbnailForBlock = function(entry) { + entry.thumbnail = null; + }; + + + + + vm.submit = function () { + if ($scope.model && $scope.model.submit) { + $scope.model.submit($scope.model); + } + } + + vm.close = function() { + if ($scope.model && $scope.model.close) { + // TODO: If content has changed, we should notify user. + $scope.model.close($scope.model); + } + } + + } + + angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockList.BlockConfigurationOverlayController", BlockConfigurationOverlayController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html new file mode 100644 index 0000000000..255d27cb8d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html @@ -0,0 +1,310 @@ +
    + +
    + + + + + + + +
    + +
    + +
    +
    Block Editor
    +
    + +
    + + +
    +
    + +
    + +
    +
    +
    + + +
    +
    + +
    +
    + + +
    + +
    +
    + +
    +
    +
    + + +
    +
    + +
    + +
    +
    +
    + +
    + +
    + +
    + +
    +
    Data Settings
    +
    + +
    + + +
    +
    + +
    +
    + {{ contentPreview = vm.getElementTypeByAlias(vm.block.contentTypeAlias); "" }} + +
    + +
    +
    +
    +
    +
    + + +
    +
    + +
    +
    + {{ settingsPreview = vm.getElementTypeByAlias(vm.block.settingsElementTypeAlias); "" }} + +
    + + +
    +
    + +
    +
    +
    + + +
    +
    + +
    + +
    +
    Showcase
    +
    + +
    + + +
    +
    + +
    + +
    +
    +
    + + +
    +
    + +
    + +
    +
    +
    + + +
    +
    + +
    +
    + + +
    + +
    +
    + +
    +
    +
    + +
    + +
    + +
    +
    + + + + + + + + + + + + + + +
    +
    +
    + + + diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.less similarity index 68% rename from src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less rename to src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.less index 216856dee9..c4b6349747 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.less @@ -1,46 +1,11 @@ -.umb-block-list-element-type-picker { +.umb-block-list-block-configuration-overlay { - .block-entry { - cursor: grab; - background-color: white; - border-radius: @baseBorderRadius; - } - - .umb-table { - border:1px solid @gray-11; - } - - .umb-table-head { - button { - margin-left: 5px; - color: @ui-action-discreet-type; - &:hover { - color: @ui-action-discreet-type-hover; - } - } - } - - .umb-table-cell { - padding-left: 10px; - padding-right: 0; - &.action-cell { - padding-right: 15px; - } - } - - .action-cell { - flex: 0 0 30px; - } - - .text-input { - width: 100%; - } .umb-node-preview { flex-grow: 1; } - .cell-actions { + .__control-actions { position: absolute; display: flex; align-items: center; @@ -51,14 +16,14 @@ opacity: 0; transition: opacity 120ms; } - .umb-table-cell:hover, - .umb-table-cell:focus, - .umb-table-cell:focus-within { - .cell-actions { + .control-group:hover, + .control-group:focus, + .control-group:focus-within { + .__control-actions { opacity: 1; } } - .cell-actions-btn { + .__control-actions-btn { position: relative; color: @ui-action-discreet-type; height: 30px; @@ -76,14 +41,15 @@ border-bottom: none; } - .settings-input { + .__settings-input { position: relative; padding: 5px 8px; + margin-bottom: 10px; color: @ui-action-discreet-type; border: 1px dashed @ui-action-discreet-border; width: 100%; font-weight: bold; - display: flex; + display: inline-flex; flex-flow: row nowrap; localize { @@ -93,6 +59,7 @@ .umb-node-preview { padding: 3px 0; margin-left: 5px; + overflow: hidden; } &.--noValue { @@ -112,7 +79,7 @@ } } - .add-button { + .__add-button { width:100%; color: @ui-action-discreet-type; border: 1px dashed @ui-action-discreet-border; @@ -126,7 +93,7 @@ font-weight: bold; } - .add-button:hover { + .__add-button:hover { color: @ui-action-discreet-type-hover; border-color: @ui-action-discreet-border-hover; } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js deleted file mode 100644 index 77751579c9..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.controller.js +++ /dev/null @@ -1,285 +0,0 @@ -/** - * @ngdoc controller - * @name Umbraco.Editors.PropertySettingsController - * @function - * - * @description - * The controller for the content type editor property settings dialog - */ - -(function () { - "use strict"; - - function ElementTypePickerController($scope, elementTypeResource, overlayService, localizationService, editorService) { - - var vm = this; - - vm.enableAddEntry = true; - - function evaluateStatus() { - - if (!vm.elementTypes) return;// cancel if elementTypes isnt loaded jet. - - vm.enableAddEntry = vm.getAvailableElementTypes().length > 0; - - } - - function onInit() { - - if (!$scope.model.value) { - $scope.model.value = []; - } - - localizationService.localize("content_nestedContentSelectElementTypeModalTitle").then(function (value) { - //selectElementTypeModalTitle = value; - }); - - loadElementTypes(); - - } - - function loadElementTypes() { - return elementTypeResource.getAll().then(function (elementTypes) { - vm.elementTypes = elementTypes; - evaluateStatus(); - }); - } - - vm.requestRemoveEntryByIndex = function (index) { - localizationService.localizeMany(["general_delete", "blockEditor_confirmDeleteBlockMessage", "blockEditor_confirmDeleteBlockNotice"]).then(function (data) { - var contentElementType = vm.getElementTypeByAlias($scope.model.value[index].contentTypeAlias); - overlayService.confirmDelete({ - title: data[0], - content: localizationService.tokenReplace(data[1], [contentElementType.name]), - confirmMessage: data[2], - close: function () { - overlayService.close(); - }, - submit: function () { - vm.removeEntryByIndex(index); - overlayService.close(); - } - }); - }); - } - - vm.removeEntryByIndex = function (index) { - $scope.model.value.splice(index, 1); - }; - - vm.sortableOptions = { - axis: "y", - cursor: "grabbing", - placeholder: 'sortable-placeholder', - forcePlaceholderSize: true - }; - - - vm.getAvailableElementTypes = function () { - return vm.elementTypes.filter(function (type) { - return !$scope.model.value.find(function (entry) { - return type.alias === entry.contentTypeAlias; - }); - }); - }; - - vm.getElementTypeByAlias = function(alias) { - return _.find(vm.elementTypes, function (type) { - return type.alias === alias; - }); - }; - - vm.openAddDialog = function ($event, entry) { - - //we have to add the alias to the objects (they are stored as contentTypeAlias) - var selectedItems = _.each($scope.model.value, function (obj) { - obj.alias = obj.contentTypeAlias; - return obj; - }); - - var availableItems = vm.getAvailableElementTypes() - - var elemTypeSelectorOverlay = { - view: "itempicker", - title: "no title jet", - availableItems: availableItems, - selectedItems: selectedItems, - createNewItem: { - action: function() { - overlayService.close(); - vm.createElementTypeAndAdd(vm.addEntryFromElementTypeAlias); - }, - icon: "icon-add", - name: "Create new" - }, - position: "target", - event: $event, - size: availableItems.length < 7 ? "small" : "medium", - submit: function (overlay) { - vm.addEntryFromElementTypeAlias(overlay.selectedItem.alias); - overlayService.close(); - }, - close: function () { - overlayService.close(); - } - }; - - overlayService.open(elemTypeSelectorOverlay); - }; - - vm.createElementTypeAndAdd = function(callback) { - const editor = { - create: true, - infiniteMode: true, - isElement: true, - submit: function (model) { - console.log(model) - loadElementTypes().then( function () { - callback(model.documentTypeAlias); - }); - editorService.close(); - }, - close: function () { - editorService.close(); - } - }; - editorService.documentTypeEditor(editor); - } - - vm.addEntryFromElementTypeAlias = function(alias) { - - var entry = { - "contentTypeAlias": alias, - "view": null, - "labelTemplate": "", - "settingsElementTypeAlias": null - }; - - $scope.model.value.push(entry); - }; - - vm.requestRemoveSettingsForEntry = function(entry) { - localizationService.localizeMany(["general_remove", "defaultdialogs_confirmremoveusageof"]).then(function (data) { - - var settingsElementType = vm.getElementTypeByAlias(entry.settingsElementTypeAlias); - - overlayService.confirmRemove({ - title: data[0], - content: localizationService.tokenReplace(data[1], [settingsElementType.name]), - close: function () { - overlayService.close(); - }, - submit: function () { - vm.removeSettingsForEntry(entry); - overlayService.close(); - } - }); - }); - }; - vm.removeSettingsForEntry = function(entry) { - entry.settingsElementTypeAlias = null; - }; - - vm.addSettingsForEntry = function ($event, entry) { - - var elemTypeSelectorOverlay = { - view: "itempicker", - title: "Pick settings (missing translation)", - availableItems: vm.elementTypes, - position: "target", - event: $event, - size: vm.elementTypes.length < 7 ? "small" : "medium", - createNewItem: { - action: function() { - overlayService.close(); - vm.createElementTypeAndAdd((alias) => { - vm.addSettingsAtEntry(entry, alias); - }); - }, - icon: "icon-add", - name: "Create new" - }, - submit: function (overlay) { - vm.addSettingsAtEntry(entry, overlay.selectedItem.alias); - overlayService.close(); - }, - close: function () { - overlayService.close(); - } - }; - - overlayService.open(elemTypeSelectorOverlay); - }; - vm.addSettingsAtEntry = function(entry, alias) { - entry.settingsElementTypeAlias = alias; - }; - - vm.openElementType = function(elementTypeAlias) { - var elementTypeId = vm.getElementTypeByAlias(elementTypeAlias).id; - const editor = { - id: elementTypeId, - submit: function (model) { - loadElementTypes(); - editorService.close(); - }, - close: function () { - editorService.close(); - } - }; - editorService.documentTypeEditor(editor); - } - - vm.requestRemoveViewForEntry = function(entry) { - localizationService.localizeMany(["general_remove", "defaultdialogs_confirmremoveusageof"]).then(function (data) { - overlayService.confirmRemove({ - title: data[0], - content: localizationService.tokenReplace(data[1], [entry.view]), - close: function () { - overlayService.close(); - }, - submit: function () { - vm.removeViewForEntry(entry); - overlayService.close(); - } - }); - }); - }; - vm.removeViewForEntry = function(entry) { - entry.view = null; - }; - vm.addViewForEntry = function(entry) { - const filePicker = { - title: "Select view (TODO need translation)", - section: "settings", - treeAlias: "files", - entityType: "file", - isDialog: true, - filter: function (i) { - if (i.name.indexOf(".html") !== -1) { - return true; - } - }, - select: function (file) { - console.log(file); - entry.view = file.name; - editorService.close(); - }, - close: function () { - editorService.close(); - } - }; - editorService.treePicker(filePicker); - } - - - onInit(); - - $scope.$watchCollection('model.value', function(newVal, oldVal) { - evaluateStatus(); - }); - - } - - angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockList.ElementTypePickerController", ElementTypePickerController); - -})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html deleted file mode 100644 index 8184761a1a..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html +++ /dev/null @@ -1,81 +0,0 @@ -
    -
    -
    -
    -
    - Custom view -
    -
    - Label - -
    -
    - Custom view -
    -
    - Custom view -
    -
    -
    -
    -
    -
    -
    -
    - {{ contentPreview = vm.getElementTypeByAlias(entry.contentTypeAlias); "" }} - -
    - -
    -
    -
    - -
    -
    -
    - - -
    - -
    -
    - -
    -
    -
    - {{ settingsPreview = vm.getElementTypeByAlias(entry.settingsElementTypeAlias); "" }} - -
    - - -
    -
    - -
    -
    - -
    -
    -
    -
    - -
    diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index a09088efa1..409ba0cefe 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2403,14 +2403,20 @@ To manage your website, simply open the Umbraco back office and start adding con Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it! + Background color + Icon color Content model Label Custom view Settings model + Overlay editor size Add custom view Add settings Overwrite label template %0%.]]> Content using this block will be lost. + + Thumbnail + Add thumbnail diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 89fbceaea8..404993a201 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2414,14 +2414,20 @@ To manage your website, simply open the Umbraco back office and start adding con Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it! + Background color + Icon color Content model Label Custom view Settings model + Overlay editor size Add custom view Add settings Overwrite label template %0%.]]> Content using this block will be lost. + + Thumbnail + Add thumbnail diff --git a/src/Umbraco.Web/Editors/ElementTypeController.cs b/src/Umbraco.Web/Editors/ElementTypeController.cs new file mode 100644 index 0000000000..64dfbbb4ab --- /dev/null +++ b/src/Umbraco.Web/Editors/ElementTypeController.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Services; +using Umbraco.Web.Editors; +using Umbraco.Web.Mvc; + +namespace Umbraco.Web.Editors +{ + [PluginController("UmbracoApi")] + public class ElementTypeController : UmbracoAuthorizedJsonController + { + [System.Web.Http.HttpGet] + public IEnumerable GetAll() + { + return Services.ContentTypeService + .GetAllElementTypes() + .OrderBy(x => x.SortOrder) + .Select(x => new + { + id = x.Id, + key = x.Key, + name = x.Name, + description = x.Description, + alias = x.Alias, + icon = x.Icon + }); + } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index 20b6c00d68..584ed5f1f8 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web.PropertyEditors public class BlockListConfiguration { - [ConfigurationField("blocks", "Available Blocks", "views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html", Description = "Define the available blocks.")] + [ConfigurationField("blocks", "Available Blocks", "views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html", Description = "Define the available blocks.")] public BlockConfiguration[] Blocks { get; set; } @@ -28,7 +28,16 @@ namespace Umbraco.Web.PropertyEditors public class BlockConfiguration { - // TODO: rename this to contentElementTypeAlias, I would like this to be specific, since we have the settings. + + [JsonProperty("backgroundColor")] + public string BackgroundColor { get; set; } + + [JsonProperty("iconColor")] + public string IconColor { get; set; } + + [JsonProperty("thumbnail")] + public string Thumbnail { get; set; } + [JsonProperty("contentTypeAlias")] public string Alias { get; set; } @@ -40,6 +49,9 @@ namespace Umbraco.Web.PropertyEditors [JsonProperty("label")] public string Label { get; set; } + + [JsonProperty("editorSize")] + public string EditorSize { get; set; } } [ConfigurationField("useInlineEditingAsDefault", "Inline editing mode", "boolean", Description = "Use the inline editor as the default block view")] diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 975f242dee..c657a52dcc 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -149,6 +149,7 @@ + From 7855870077cac7b21a428a206fe9765aeeab2cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 8 Apr 2020 13:23:52 +0200 Subject: [PATCH 108/508] translation for prevalue editor property group headlines --- .../prevalue/blocklist.blockconfiguration.overlay.html | 6 +++--- .../prevalue/blocklist.blockconfiguration.overlay.less | 3 +-- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 3 +++ src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 3 +++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html index 255d27cb8d..806930ffb2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html @@ -18,7 +18,7 @@
    -
    Block Editor
    + Block appearance
    @@ -75,7 +75,7 @@
    -
    Data Settings
    + Data Models
    @@ -147,7 +147,7 @@
    -
    Showcase
    + Showcase
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.less index c4b6349747..8d063c77fb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.less @@ -26,9 +26,8 @@ .__control-actions-btn { position: relative; color: @ui-action-discreet-type; - height: 30px; + height: 32px; width: 26px; - margin-top: 2px; &:hover { color: @ui-action-discreet-type-hover; } diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 409ba0cefe..a1de35c266 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2403,6 +2403,9 @@ To manage your website, simply open the Umbraco back office and start adding con Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it! + Block apperance + Data models + Showcase Background color Icon color Content model diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 404993a201..cb00bae3d1 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2414,6 +2414,9 @@ To manage your website, simply open the Umbraco back office and start adding con Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it! + Block apperance + Data models + Showcase Background color Icon color Content model From 33f507a5757135f9ca5b3ee36edd86dfd8126fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 8 Apr 2020 15:33:04 +0200 Subject: [PATCH 109/508] blockcard corrections --- .../blockeditor/blockcard/blockcard.component.js | 12 ------------ .../blockeditor/blockcard/blockcard.component.less | 3 +++ 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.js index 98e7303219..3e7728c583 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.js @@ -5,8 +5,6 @@ .module("umbraco") .component("umbBlockCard", { templateUrl: "views/propertyeditors/blockeditor/blockcard/blockcard.component.html", - controller: BlockCardController, - controllerAs: "vm", transclude: true, bindings: { blockConfigModel: "<", @@ -14,15 +12,5 @@ } }); - function BlockCardController() { - - var vm = this; - - vm.$onInit = function() { - console.log(vm.blockConfigModel); - console.log(vm.elementTypeModel); - } - - } })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.less index 5adbded9bb..703c1bca26 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.less @@ -52,6 +52,9 @@ umb-block-card { width: 100%; padding-bottom: 10/16*100%; background-color: @gray-11; + + background-size: cover; + background-position: 50% 50%; .__icon { position: absolute; width: 100%; From 83d590ff084adddb8b9cb50e53a130b0a3f47709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 8 Apr 2020 15:33:44 +0200 Subject: [PATCH 110/508] block list prevalue corrections --- .../blocklist.blockconfiguration.overlay.controller.js | 2 +- .../prevalue/blocklist.blockconfiguration.overlay.html | 6 +++--- src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs | 5 ++++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js index 015d25e9fb..a752448e2c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js @@ -151,7 +151,7 @@ entityType: "file", isDialog: true, filter: function (i) { - return (i.name.indexOf(".jpg") !== -1 || i.name.indexOf(".jpeg") !== -1 || i.name.indexOf(".png") !== -1 || i.name.indexOf(".svg") !== -1 || i.name.indexOf(".webp") !== -1 || i.name.indexOf(".gif") !== -1); + return !(i.name.indexOf(".jpg") !== -1 || i.name.indexOf(".jpeg") !== -1 || i.name.indexOf(".png") !== -1 || i.name.indexOf(".svg") !== -1 || i.name.indexOf(".webp") !== -1 || i.name.indexOf(".gif") !== -1); }, select: function (file) { block.thumbnail = file.name; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html index 806930ffb2..ae147d488f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html @@ -18,7 +18,7 @@
    - Block appearance
    + Block appearance
    @@ -75,7 +75,7 @@
    - Data Models
    + Data Models
    @@ -147,7 +147,7 @@
    - Showcase
    + Showcase
    diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index 584ed5f1f8..e0157ca36c 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -54,8 +54,11 @@ namespace Umbraco.Web.PropertyEditors public string EditorSize { get; set; } } - [ConfigurationField("useInlineEditingAsDefault", "Inline editing mode", "boolean", Description = "Use the inline editor as the default block view")] + [ConfigurationField("useInlineEditingAsDefault", "Inline editing mode", "boolean", Description = "Use the inline editor as the default block view.")] public bool UseInlineEditingAsDefault { get; set; } + [ConfigurationField("propertyWidth", "Property editor width", "textstring", Description = "optional css overwrite, example: 800px or 100%")] + public string propertyWidth { get; set; } + } } From 676f2ab847660c564ed1c8638c4de5b8f0a61f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 8 Apr 2020 15:46:24 +0200 Subject: [PATCH 111/508] revert agressive clean up --- .../blockeditor/blockcard/blockcard.component.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.js index 3e7728c583..8f1cb00c6d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.js @@ -5,6 +5,8 @@ .module("umbraco") .component("umbBlockCard", { templateUrl: "views/propertyeditors/blockeditor/blockcard/blockcard.component.html", + controller: BlockCardController, + controllerAs: "vm", transclude: true, bindings: { blockConfigModel: "<", @@ -12,5 +14,10 @@ } }); + function BlockCardController() { + + var vm = this; + + } })(); From b4387e757a3b70c8e7433aa303132688c657abd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 8 Apr 2020 15:46:37 +0200 Subject: [PATCH 112/508] Block Picker --- .../common/services/blockeditor.service.js | 7 ++-- .../blockpicker/blockpicker.controller.js | 21 ++++++++++ .../blockpicker/blockpicker.html | 42 +++++++++++++++++++ .../blocklist/blocklist.component.js | 41 +++++++++--------- 4 files changed, 85 insertions(+), 26 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index a05535d3b4..3524982a90 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -235,7 +235,7 @@ return this.blockConfigurations.map(blockConfiguration => blockConfiguration.contentTypeAlias); }, - getAvailableBlocksForItemPicker: function() { + getAvailableBlocksForBlockPicker: function() { var blocks = []; @@ -243,9 +243,8 @@ var scaffold = this.getScaffoldFor(blockConfiguration.contentTypeAlias); if(scaffold) { blocks.push({ - alias: scaffold.contentTypeAlias, - name: scaffold.contentTypeName, - icon: scaffold.icon + blockConfigModel: blockConfiguration, + elementTypeModel: scaffold }); } }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js new file mode 100644 index 0000000000..5d02010485 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js @@ -0,0 +1,21 @@ +//used for the media picker dialog +angular.module("umbraco") +.controller("Umbraco.Editors.BlockPickerController", + function ($scope) { + var vm = this; + + vm.model = $scope.model; + + vm.selectItem = function() { + vm.model.selectedItem = item; + vm.model.submit($scope.model); + } + + vm.close = function() { + if ($scope.model && $scope.model.close) { + $scope.model.close($scope.model); + } + } + + } +); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html new file mode 100644 index 0000000000..3eb0425751 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html @@ -0,0 +1,42 @@ +
    + + + + + + +
    + +
    + + + +
    + +
    + + + + + + + + + + + +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 2b25a8b013..f97ab1e19a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -108,7 +108,7 @@ }); vm.availableContentTypes = modelObject.getAvailableAliasesForBlockContent(); - vm.availableBlockTypes = modelObject.getAvailableBlocksForItemPicker(); + vm.availableBlockTypes = modelObject.getAvailableBlocksForBlockPicker(); $scope.$evalAsync(); @@ -198,7 +198,7 @@ settings: blockSettingsModelClone, title: blockModel.label, view: "views/common/infiniteeditors/blockeditor/blockeditor.html", - size: blockModel.config.overlaySize || "medium", + size: blockModel.config.editorSize || "medium", submit: function(blockEditorModel) { // To ensure syncronization gets tricked we transfer if (blockEditorModel.content !== null) { @@ -229,14 +229,13 @@ return; } - vm.blockTypePicker = { - show: false, - size: vm.availableBlockTypes.length < 7 ? "small" : "medium", - filter: vm.availableBlockTypes.length > 12 ? true : false, - orderBy: "$index", - view: "itempicker", - event: $event, + var amountOfAvailableTypes = vm.availableBlockTypes.length; + var blockPickerModel = { availableItems: vm.availableBlockTypes, + title: vm.labels.grid_addElement, + orderBy: "$index", + view: "views/common/infiniteeditors/blockpicker/blockpicker.html", + size: (amountOfAvailableTypes > 8 ? "medium" : "small"), clickPasteItem: function(item) { if (item.type === "elementTypeArray") { var indexIncrementor = 0; @@ -248,29 +247,28 @@ } else { requestPasteFromClipboard(createIndex, item.data); } - vm.blockTypePicker.close(); + blockPickerModel.close(); }, - submit: function (model) { + submit: function(blockPickerModel) { var added = false; if (model && model.selectedItem) { added = addNewBlock(createIndex, model.selectedItem.alias); } - vm.blockTypePicker.close(); + blockPickerModel.close(); if (added && vm.model.config.useInlineEditingAsDefault !== true && vm.blocks.length > createIndex) { editBlock(vm.blocks[createIndex]); } }, - close: function () { - vm.blockTypePicker.show = false; - delete vm.blockTypePicker; + close: function() { + editorService.close(); } }; - vm.blockTypePicker.pasteItems = []; + blockPickerModel.pasteItems = []; var singleEntriesForPaste = clipboardService.retriveEntriesOfType("elementType", vm.availableContentTypes); singleEntriesForPaste.forEach(function (entry) { - vm.blockTypePicker.pasteItems.push({ + blockPickerModel.pasteItems.push({ type: "elementType", name: entry.label, data: entry.data, @@ -280,7 +278,7 @@ var arrayEntriesForPaste = clipboardService.retriveEntriesOfType("elementTypeArray", vm.availableContentTypes); arrayEntriesForPaste.forEach(function (entry) { - vm.blockTypePicker.pasteItems.push({ + blockPickerModel.pasteItems.push({ type: "elementTypeArray", name: entry.label, data: entry.data, @@ -288,9 +286,7 @@ }); }); - vm.blockTypePicker.title = vm.blockTypePicker.pasteItems.length > 0 ? labels.grid_addElement : labels.content_createEmpty; - - vm.blockTypePicker.clickClearPaste = function ($event) { + blockPickerModel.clickClearPaste = function ($event) { $event.stopPropagation(); $event.preventDefault(); clipboardService.clearEntriesOfType("elementType", vm.availableContentTypes); @@ -298,7 +294,8 @@ vm.blockTypePicker.pasteItems = [];// This dialog is not connected via the clipboardService events, so we need to update manually. }; - vm.blockTypePicker.show = true; + // open block picker overlay + editorService.open(blockPickerModel); }; From 525513cdeabb7b8842f7d7e038715e4359bfd4c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 8 Apr 2020 16:18:36 +0200 Subject: [PATCH 113/508] MaxPropertyWidth PreValue + Implementation --- .../views/propertyeditors/blocklist/blocklist.component.html | 2 +- .../views/propertyeditors/blocklist/blocklist.component.js | 2 ++ src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index fc957ce003..360201c27d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -1,7 +1,7 @@
    -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index f97ab1e19a..d175e42241 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -50,6 +50,8 @@ vm.$onInit = function() { vm.validationLimit = vm.model.config.validationLimit; + + vm.listWrapperSyles = {'max-width': vm.model.config.maxPropertyWidth}; // We need to ensure that the property model value is an object, this is needed for modelObject to recive a reference and keep that updated. if(typeof vm.model.value !== 'object' || vm.model.value === null) {// testing if we have null or undefined value or if the value is set to another type than Object. diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index e0157ca36c..fba80bd57d 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -57,8 +57,8 @@ namespace Umbraco.Web.PropertyEditors [ConfigurationField("useInlineEditingAsDefault", "Inline editing mode", "boolean", Description = "Use the inline editor as the default block view.")] public bool UseInlineEditingAsDefault { get; set; } - [ConfigurationField("propertyWidth", "Property editor width", "textstring", Description = "optional css overwrite, example: 800px or 100%")] - public string propertyWidth { get; set; } + [ConfigurationField("maxPropertyWidth", "Property editor width", "textstring", Description = "optional css overwrite, example: 800px or 100%")] + public string MaxPropertyWidth { get; set; } } } From ff8ef00847c78920b9871ea1f34a64a8f6566fb8 Mon Sep 17 00:00:00 2001 From: Benjamin Carleski Date: Wed, 8 Apr 2020 07:34:54 -0700 Subject: [PATCH 114/508] Incorporate latest block list editor changes, update migration for changed configuration --- .../V_8_7_0/StackedContentToBlockList.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs index a3a8bd62c4..c75998a66d 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs @@ -170,11 +170,11 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 private class BlockListConfiguration { - [ConfigurationField("blocks", "Available Blocks", "views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html", Description = "Define the available blocks.")] + [JsonProperty("blocks")] public BlockConfiguration[] Blocks { get; set; } - [ConfigurationField("validationLimit", "Amount", "numberrange", Description = "Set a required range of blocks")] + [JsonProperty("validationLimit")] public NumberRange ValidationLimit { get; set; } = new NumberRange(); public class NumberRange @@ -188,6 +188,15 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 public class BlockConfiguration { + [JsonProperty("backgroundColor")] + public string BackgroundColor { get; set; } + + [JsonProperty("iconColor")] + public string IconColor { get; set; } + + [JsonProperty("thumbnail")] + public string Thumbnail { get; set; } + [JsonProperty("contentTypeAlias")] public string Alias { get; set; } @@ -199,11 +208,16 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 [JsonProperty("label")] public string Label { get; set; } + + [JsonProperty("editorSize")] + public string EditorSize { get; set; } } - [ConfigurationField("useInlineEditingAsDefault", "Inline editing mode", "boolean", Description = "Use the inline editor as the default block view")] + [JsonProperty("useInlineEditingAsDefault")] public bool UseInlineEditingAsDefault { get; set; } + [JsonProperty("maxPropertyWidth")] + public string MaxPropertyWidth { get; set; } } private class StackedContentConfiguration From 5bbf68e432daabedaa60017f6e42272e9102a84c Mon Sep 17 00:00:00 2001 From: Benjamin Carleski Date: Wed, 8 Apr 2020 08:49:11 -0700 Subject: [PATCH 115/508] Change declared converter type --- .../ValueConverters/BlockListPropertyValueConverter.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs index d4e130cc0d..21105b531e 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -32,13 +32,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.BlockList); /// - public override Type GetPropertyValueType(IPublishedPropertyType propertyType) - { - var contentTypes = propertyType.DataType.ConfigurationAs().Blocks; - return contentTypes.Length == 1 - ? typeof(IEnumerable<>).MakeGenericType(ModelType.For(contentTypes[0].Alias)) - : typeof(IEnumerable); - } + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof(BlockListModel); /// public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) From 133d30791f1c2502b6ad6379a0cfb7a0003bb36f Mon Sep 17 00:00:00 2001 From: Benjamin Carleski Date: Wed, 8 Apr 2020 14:27:50 -0700 Subject: [PATCH 116/508] Handle invalid data type references --- .../Upgrade/V_8_7_0/StackedContentToBlockList.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs index c75998a66d..1c27f1c48a 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs @@ -109,9 +109,9 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 { Blocks = old.ContentTypes?.Select(t => new BlockListConfiguration.BlockConfiguration { - Alias = knownDocumentTypes[t.IcContentTypeGuid].Alias, + Alias = knownDocumentTypes.TryGetValue(t.IcContentTypeGuid, out var ct) ? ct.Alias : null, Label = t.NameTemplate - }).ToArray(), + }).Where(c => c.Alias != null).ToArray(), UseInlineEditingAsDefault = old.SingleItemMode == "1" || old.SingleItemMode == bool.TrueString }; @@ -247,9 +247,9 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 public void AddDataItem(JObject obj, Dictionary knownDocumentTypes) { - if (!Guid.TryParse(obj["key"].ToString(), out var key)) throw new ArgumentException("Could not find a valid key in the data item"); - if (!Guid.TryParse(obj["icContentTypeGuid"].ToString(), out var ctGuid)) throw new ArgumentException("Could not find a valid content type GUID in the data item"); - if (!knownDocumentTypes.TryGetValue(ctGuid, out var ct)) throw new ArgumentException($"Unknown content type GUID '{ctGuid}'"); + if (!Guid.TryParse(obj["key"].ToString(), out var key)) key = Guid.NewGuid(); + if (!Guid.TryParse(obj["icContentTypeGuid"].ToString(), out var ctGuid)) ctGuid = Guid.Empty; + if (!knownDocumentTypes.TryGetValue(ctGuid, out var ct)) ct = new KnownContentType { Alias = ctGuid.ToString() }; obj.Remove("key"); obj.Remove("icContentTypeGuid"); From e8a31ef7b61452f0f06b7152fd01118602f7c2e8 Mon Sep 17 00:00:00 2001 From: Benjamin Carleski Date: Mon, 13 Apr 2020 16:40:09 -0700 Subject: [PATCH 117/508] Remove code duplicated from PR #7957 --- .../Migrations/Upgrade/UmbracoPlan.cs | 1 - .../Upgrade/V_8_7_0/ColorPickerPreValues.cs | 106 ------------------ .../Upgrade/V_8_7_0/ConvertToElements.cs | 18 +-- src/Umbraco.Core/Umbraco.Core.csproj | 1 - 4 files changed, 2 insertions(+), 124 deletions(-) delete mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ColorPickerPreValues.cs diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index 1d30efa573..1d4cef45d4 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -197,7 +197,6 @@ namespace Umbraco.Core.Migrations.Upgrade // to 8.7.0... To("{DFA35FA2-BFBB-433F-84E5-BD75940CDDF6}"); - To("{711AC937-B11C-47AC-8D4A-5B8868A3C2C6}"); To("{DA434576-3DEF-46D7-942A-CE34D7F7FB8A}"); //FINAL } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ColorPickerPreValues.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ColorPickerPreValues.cs deleted file mode 100644 index 6e959e86d7..0000000000 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ColorPickerPreValues.cs +++ /dev/null @@ -1,106 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Linq; -using System.Text.RegularExpressions; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.Dtos; -using Umbraco.Core.PropertyEditors; - -namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 -{ - public class ColorPickerPreValues : MigrationBase - { - private static readonly Regex OldPreValuesPattern1 = new Regex("\\s*{(\\s*\"[0-9]+\"\\s*:\\s*\"[0-9a-fA-F]+\"\\s*,)*\\s*\"useLabel\"\\s*:\\s*\"[01]\"\\s*}\\s*", RegexOptions.Compiled); - private static readonly Regex OldPreValuesPattern2 = new Regex("\\s*{(\\s*\"[0-9]+\"\\s*:\\s*{\\s*\"value\"\\s*:\\s*\"[0-9a-fA-F]+\"\\s*(,\\s*\"label\"\\s*:\\s*\"[^\"]*\"\\s*)?(,\\s*\"sortOrder\"\\s*:\\s*[0-9]+\\s*)?}\\s*,)*\\s*\"useLabel\"\\s*:\\s*\"[01]\"\\s*}\\s*", RegexOptions.Compiled); - - public ColorPickerPreValues(IMigrationContext context) : base(context) - { - } - - public override void Migrate() - { - var sql = Sql() - .Select() - .From() - .Where(d => d.EditorAlias == Constants.PropertyEditors.Aliases.ColorPicker); - - var dtos = Database.Fetch(sql); - - foreach (var dto in dtos) - { - if (dto.Configuration.IsNullOrWhiteSpace()) continue; - - if (OldPreValuesPattern1.IsMatch(dto.Configuration)) ConvertPreValues(dto, ConvertStyle1); - else if (OldPreValuesPattern2.IsMatch(dto.Configuration)) ConvertPreValues(dto, ConvertStyle2); - else continue; - - Database.Update(dto); - } - } - - private void ConvertPreValues(DataTypeDto dto, Func converter) - { - var obj = JObject.Parse(dto.Configuration); - var config = new ColorPickerConfiguration(); - var id = 0; - - foreach (var prop in obj.Properties()) - { - if (prop.Name.ToLowerInvariant() == "uselabel") - { - config.UseLabel = prop.Value.ToString() == "1"; - } - else - { - id++; - config.Items.Add(new ValueListConfiguration.ValueListItem - { - Id = id, - Value = JsonConvert.SerializeObject(converter(id, prop.Value)) - }); - } - } - - dto.Configuration = JsonConvert.SerializeObject(config); - } - - private ItemValue ConvertStyle1(int index, JToken token) - { - var value = token.ToString(); - return new ItemValue - { - Color = value, - Label = value, - SortOrder = index - }; - } - - private ItemValue ConvertStyle2(int index, JToken token) - { - var obj = (JObject)token; - var value = obj["value"].ToString(); - var label = obj["label"]?.ToString(); - var order = obj["sortOrder"]?.ToString(); - - return new ItemValue - { - Color = value, - Label = label.IsNullOrWhiteSpace() ? value : label, - SortOrder = int.TryParse(order, out var o) ? o : index - }; - } - - private class ItemValue - { - [JsonProperty("value")] - public string Color { get; set; } - - [JsonProperty("label")] - public string Label { get; set; } - - [JsonProperty("sortOrder")] - public int SortOrder { get; set; } - } - } -} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ConvertToElements.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ConvertToElements.cs index e42453a3fe..2bc017b643 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ConvertToElements.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ConvertToElements.cs @@ -1,10 +1,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; @@ -24,7 +21,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 docTypes.ForEach(d => docTypeMap[d.Alias] = d.NodeId); // Find all Nested Content or Block List data types - var dataTypes = GetDataTypes(Constants.PropertyEditors.Aliases.NestedContent, Constants.PropertyEditors.Aliases.BlockList); + var dataTypes = GetDataTypes(Constants.PropertyEditors.Aliases.BlockList); // Find all document types listed in each var elementTypeIds = dataTypes.SelectMany(d => GetDocTypeIds(d.Configuration, docTypeMap)).ToList(); @@ -63,12 +60,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 if (configuration.IsNullOrWhiteSpace() || configuration[0] != '{') return Enumerable.Empty(); var obj = JObject.Parse(configuration); - if (obj["contentTypes"] is JArray ncArr) - { - var arr = ncArr.ToObject(); - return arr.Select(i => idMap.TryGetValue(i.Alias, out var id) ? id : 0).Where(i => i != 0); - } - else if (obj["blocks"] is JArray blArr) + if (obj["blocks"] is JArray blArr) { var arr = blArr.ToObject(); return arr.Select(i => idMap.TryGetValue(i.Alias, out var id) ? id : 0).Where(i => i != 0); @@ -77,12 +69,6 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 return Enumerable.Empty(); } - public class ContentType - { - [JsonProperty("ncAlias")] - public string Alias { get; set; } - } - public class BlockConfiguration { [JsonProperty("contentTypeAlias")] diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 1ae5267b6a..c0d14a580e 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -131,7 +131,6 @@ - From 3d820f36972462dae665fbe156e5dcdb85a29965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 14 Apr 2020 17:08:35 +0200 Subject: [PATCH 118/508] use ElementModel for the ContentModel of an ElementType. So we can use ElementTypeModel for the ModelDefinition aka. the Type. --- .../common/services/blockeditor.service.js | 32 ++++++++++--------- .../blocklist/blocklist.component.js | 11 ++++--- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index 3524982a90..75f3efe322 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -9,9 +9,9 @@ * Simple mapping from property model content entry to editing model, * needs to stay simple to avoid deep watching. */ - function mapToElementTypeModel(elementTypeModel, contentModel) { + function mapToElementModel(elementModel, contentModel) { - var variant = elementTypeModel.variants[0]; + var variant = elementModel.variants[0]; for (var t = 0; t < variant.tabs.length; t++) { var tab = variant.tabs[t]; @@ -26,12 +26,12 @@ } /** - * Simple mapping from elementTypeModel to property model content entry, + * Simple mapping from elementModel to property model content entry, * needs to stay simple to avoid deep watching. */ - function mapToPropertyModel(elementTypeModel, contentModel) { + function mapToPropertyModel(elementModel, contentModel) { - var variant = elementTypeModel.variants[0]; + var variant = elementModel.variants[0]; for (var t = 0; t < variant.tabs.length; t++) { var tab = variant.tabs[t]; @@ -50,12 +50,12 @@ } /** - * Map property values from an ElementTypeModel to another ElementTypeModel. + * Map property values from an ElementModel to another ElementModel. * Used to tricker watchers for synchronization. - * @param {Object} fromModel ElementTypeModel to recive property values from. - * @param {Object} toModel ElementTypeModel to recive property values from. + * @param {Object} fromModel ElementModel to recive property values from. + * @param {Object} toModel ElementModel to recive property values from. */ - function mapElementTypeValues(fromModel, toModel) { + function mapElementValues(fromModel, toModel) { if (!fromModel || !fromModel.variants) { toModel.variants = null; return; @@ -244,7 +244,7 @@ if(scaffold) { blocks.push({ blockConfigModel: blockConfiguration, - elementTypeModel: scaffold + elementTypeModel: scaffold.documentType }); } }); @@ -283,8 +283,10 @@ var blockModel = {}; blockModel.key = String.CreateGuid().replace(/-/g, ""); blockModel.config = angular.copy(blockConfiguration); - blockModel.labelInterpolator = $interpolate(blockModel.config.label); - + if (blockModel.config.label) { + blockModel.labelInterpolator = $interpolate(blockModel.config.label); + } + var contentScaffold = this.getScaffoldFor(blockConfiguration.contentTypeAlias); if(contentScaffold === null) { return null; @@ -294,7 +296,7 @@ blockModel.content = angular.copy(contentScaffold); blockModel.content.udi = udi; - mapToElementTypeModel(blockModel.content, contentModel); + mapToElementModel(blockModel.content, contentModel); blockModel.contentModel = contentModel; blockModel.layoutModel = layoutEntry; @@ -311,7 +313,7 @@ layoutEntry.settings = layoutEntry.settings || { key: String.CreateGuid(), contentTypeAlias: blockConfiguration.settingsElementTypeAlias }; if (!layoutEntry.settings.key) { layoutEntry.settings.key = String.CreateGuid(); } if (!layoutEntry.settings.contentTypeAlias) { layoutEntry.settings.contentTypeAlias = blockConfiguration.settingsElementTypeAlias; } - mapToElementTypeModel(blockModel.settings, layoutEntry.settings); + mapToElementModel(blockModel.settings, layoutEntry.settings); } else { layoutEntry.settings = null; } @@ -458,7 +460,7 @@ createModelObject: function(propertyModelValue, propertyEditorAlias, blockConfigurations, propertyScope) { return new BlockEditorModelObject(propertyModelValue, propertyEditorAlias, blockConfigurations, propertyScope); }, - mapElementTypeValues: mapElementTypeValues, + mapElementValues: mapElementValues, getBlockLabel: getBlockLabel } } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index d175e42241..21af10b792 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -202,12 +202,12 @@ view: "views/common/infiniteeditors/blockeditor/blockeditor.html", size: blockModel.config.editorSize || "medium", submit: function(blockEditorModel) { - // To ensure syncronization gets tricked we transfer + // To ensure syncronization gets tricked we transfer each property. if (blockEditorModel.content !== null) { - blockEditorService.mapElementTypeValues(blockEditorModel.content, blockModel.content) + blockEditorService.mapElementValues(blockEditorModel.content, blockModel.content) } if (blockModel.config.settingsElementTypeAlias !== null) { - blockEditorService.mapElementTypeValues(blockEditorModel.settings, blockModel.settings) + blockEditorService.mapElementValues(blockEditorModel.settings, blockModel.settings) } editorService.close(); }, @@ -253,8 +253,9 @@ }, submit: function(blockPickerModel) { var added = false; - if (model && model.selectedItem) { - added = addNewBlock(createIndex, model.selectedItem.alias); + if (blockPickerModel && blockPickerModel.selectedItem) { + console.log(blockPickerModel.selectedItem) + added = addNewBlock(createIndex, blockPickerModel.selectedItem.blockConfigModel.contentTypeAlias); } blockPickerModel.close(); if (added && vm.model.config.useInlineEditingAsDefault !== true && vm.blocks.length > createIndex) { From 82294e3f2a80956a07a2fe6d5a28cc823de5e115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 14 Apr 2020 17:09:15 +0200 Subject: [PATCH 119/508] do still show itempicker for BlockConfiguration even though there is no ElementTypes to pick. This enables the option to create a new ElementType to be used. --- .../blocklist.blockconfiguration.controller.js | 14 -------------- .../prevalue/blocklist.blockconfiguration.html | 2 +- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js index a058f465e0..494ef35137 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js @@ -20,17 +20,8 @@ var vm = this; - vm.enableAddBlock = true; vm.openBlock = null; - function evaluateStatus() { - - if (!vm.elementTypes) return;// cancel if elementTypes isnt loaded jet. - - vm.enableAddBlock = vm.getAvailableElementTypes().length > 0; - - } - function onInit() { if (!$scope.model.value) { @@ -44,7 +35,6 @@ function loadElementTypes() { return elementTypeResource.getAll().then(function (elementTypes) { vm.elementTypes = elementTypes; - evaluateStatus(); }); } @@ -197,10 +187,6 @@ onInit(); - $scope.$watchCollection('model.value', function(newVal, oldVal) { - evaluateStatus(); - }); - } angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockList.BlockConfigurationController", BlockConfigurationController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html index 06e9714b77..bb5326e769 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html @@ -23,7 +23,7 @@
    -
    From d6782fd752eb3853439245befedaee6b30a6c671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 14 Apr 2020 17:09:41 +0200 Subject: [PATCH 120/508] use the right wrapper, for correct spacing --- .../blockpicker/blockpicker.html | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html index 3eb0425751..35f7f38d97 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html @@ -11,15 +11,16 @@
    +
    +
    -
    - - - + + +
    From dbd3de14a4e14bdca762b5e3c7968680d311c265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 14 Apr 2020 17:09:48 +0200 Subject: [PATCH 121/508] parse item --- .../infiniteeditors/blockpicker/blockpicker.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js index 5d02010485..d5525f8dd9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js @@ -6,7 +6,7 @@ angular.module("umbraco") vm.model = $scope.model; - vm.selectItem = function() { + vm.selectItem = function(item) { vm.model.selectedItem = item; vm.model.submit($scope.model); } From 69950cfe3612096f2a685cd7f4a55df0181cc97a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 15 Apr 2020 14:37:44 +0200 Subject: [PATCH 122/508] correct fallback for label --- .../src/common/services/blockeditor.service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index 75f3efe322..67def3c75a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -91,7 +91,7 @@ // We are just using the contentModel, since its a key/value object that is live synced. (if we need to add additional vars, we could make a shallow copy and apply those.) return blockModel.labelInterpolator(blockModel.contentModel); } - return blockModel.contentTypeName; + return blockModel.content.contentTypeName; } @@ -286,7 +286,7 @@ if (blockModel.config.label) { blockModel.labelInterpolator = $interpolate(blockModel.config.label); } - + var contentScaffold = this.getScaffoldFor(blockConfiguration.contentTypeAlias); if(contentScaffold === null) { return null; From d81dcc153768b5a6edde64438d76e58aca1876ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 15 Apr 2020 14:38:05 +0200 Subject: [PATCH 123/508] removed unused callback --- .../views/common/infiniteeditors/blockeditor/blockeditor.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.html index 301db23abd..85ce9298c6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.html @@ -9,8 +9,7 @@ navigation="vm.tabs" hide-alias="true" hide-icon="true" - hide-description="true" - on-select-navigation-item="vm.selectTab"> + hide-description="true">
    From 061be643c84fd9568aea86d776e5617dbce68412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 15 Apr 2020 14:38:25 +0200 Subject: [PATCH 124/508] paste feature for block-picker --- .../blockpicker/blockpicker.controller.js | 29 ++++++++++ .../blockpicker/blockpicker.html | 43 ++++++++++++++- .../blocklist/blocklist.component.js | 55 +++++++++++-------- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 1 + src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + .../Umbraco/config/lang/en_us.xml | 1 + 6 files changed, 103 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js index d5525f8dd9..4957e13fad 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js @@ -4,6 +4,35 @@ angular.module("umbraco") function ($scope) { var vm = this; + vm.navigation = [ + { + "alias": "empty", + "name": "Create empty", + "icon": "icon-add", + "active": true, + "view": "" + }, + { + "alias": "clipboard", + "name": "Clipboard", + "icon": "icon-paste-in", + "view": "" + } + ]; + + vm.activeTab = vm.navigation[0]; + vm.onNavigationChanged = function(tab) { + vm.activeTab.active = false; + vm.activeTab = tab; + vm.activeTab.active = true; + } + + vm.clickClearClipboard = function() { + vm.onNavigationChanged(vm.navigation[0]); + vm.model.clipboardItems = [];// This dialog is not connected via the clipboardService events, so we need to update manually. + vm.model.clickClearClipboard(); + } + vm.model = $scope.model; vm.selectItem = function(item) { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html index 35f7f38d97..5206c7e66d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html @@ -4,6 +4,8 @@
    -
    + +
    + + + +
    + + + +
    +
    + +
    +
    + + +
    +
    + ng-repeat="block in vm.model.clipboardItems" + ng-click="vm.model.clickPasteItem(block)">
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 21af10b792..69bfb1a71a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -241,20 +241,19 @@ clickPasteItem: function(item) { if (item.type === "elementTypeArray") { var indexIncrementor = 0; - item.data.forEach(function (entry) { + item.pasteData.forEach(function (entry) { if (requestPasteFromClipboard(createIndex + indexIncrementor, entry)) { indexIncrementor++; } }); } else { - requestPasteFromClipboard(createIndex, item.data); + requestPasteFromClipboard(createIndex, item.pasteData); } blockPickerModel.close(); }, submit: function(blockPickerModel) { var added = false; if (blockPickerModel && blockPickerModel.selectedItem) { - console.log(blockPickerModel.selectedItem) added = addNewBlock(createIndex, blockPickerModel.selectedItem.blockConfigModel.contentTypeAlias); } blockPickerModel.close(); @@ -267,36 +266,44 @@ } }; - blockPickerModel.pasteItems = []; + blockPickerModel.clickClearClipboard = function ($event) { + clipboardService.clearEntriesOfType("elementType", vm.availableContentTypes); + clipboardService.clearEntriesOfType("elementTypeArray", vm.availableContentTypes); + }; + + blockPickerModel.clipboardItems = []; var singleEntriesForPaste = clipboardService.retriveEntriesOfType("elementType", vm.availableContentTypes); singleEntriesForPaste.forEach(function (entry) { - blockPickerModel.pasteItems.push({ - type: "elementType", - name: entry.label, - data: entry.data, - icon: entry.icon - }); + console.log("paste Entry: ", entry) + blockPickerModel.clipboardItems.push( + { + type: "elementType", + pasteData: entry.data, + blockConfigModel: modelObject.getScaffoldFor(entry.alias), + elementTypeModel: { + name: entry.label, + icon: entry.icon + } + } + ); }); var arrayEntriesForPaste = clipboardService.retriveEntriesOfType("elementTypeArray", vm.availableContentTypes); arrayEntriesForPaste.forEach(function (entry) { - blockPickerModel.pasteItems.push({ - type: "elementTypeArray", - name: entry.label, - data: entry.data, - icon: entry.icon - }); + blockPickerModel.clipboardItems.push( + { + type: "elementTypeArray", + pasteData: entry.data, + blockConfigModel: {}, // no block configuration for paste items of elementTypeArray. + elementTypeModel: { + name: entry.label, + icon: entry.icon + } + } + ); }); - blockPickerModel.clickClearPaste = function ($event) { - $event.stopPropagation(); - $event.preventDefault(); - clipboardService.clearEntriesOfType("elementType", vm.availableContentTypes); - clipboardService.clearEntriesOfType("elementTypeArray", vm.availableContentTypes); - vm.blockTypePicker.pasteItems = [];// This dialog is not connected via the clipboardService events, so we need to update manually. - }; - // open block picker overlay editorService.open(blockPickerModel); diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 4c74c45d88..f0625ad767 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -1791,6 +1791,7 @@ Mange hilsner fra Umbraco robotten Kopier %0% %0% fra %1% Fjern alle elementer + Ryd udklipsholder Åben egenskabshandlinger diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index a1de35c266..acd63dea4f 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2279,6 +2279,7 @@ To manage your website, simply open the Umbraco back office and start adding con Copy %0% %0% from %1% Remove all items + Clear clipboard Open Property Actions diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index cb00bae3d1..d370de60bf 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2290,6 +2290,7 @@ To manage your website, simply open the Umbraco back office and start adding con Copy %0% %0% from %1% Remove all items + Clear clipboard Open Property Actions From e2f60db9ebe36c54d7827a68e372f140f53da9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 15 Apr 2020 14:58:13 +0200 Subject: [PATCH 125/508] localize block-picker tabs --- .../blockpicker/blockpicker.controller.js | 43 +++++++++++-------- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 2 + .../Umbraco/config/lang/en_us.xml | 2 + 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js index 4957e13fad..1deb010073 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js @@ -1,26 +1,35 @@ //used for the media picker dialog angular.module("umbraco") .controller("Umbraco.Editors.BlockPickerController", - function ($scope) { + function ($scope, localizationService) { var vm = this; - vm.navigation = [ - { - "alias": "empty", - "name": "Create empty", - "icon": "icon-add", - "active": true, - "view": "" - }, - { - "alias": "clipboard", - "name": "Clipboard", - "icon": "icon-paste-in", - "view": "" - } - ]; - vm.activeTab = vm.navigation[0]; + vm.navigation = []; + + + localizationService.localizeMany(["blockEditor_tabCreateEmpty", "blockEditor_tabClipboard"]).then( + function (data) { + + vm.navigation = [{ + "alias": "empty", + "name": data[0], + "icon": "icon-add", + "active": true, + "view": "" + }, + { + "alias": "clipboard", + "name": data[1], + "icon": "icon-paste-in", + "view": "" + }]; + + vm.activeTab = vm.navigation[0]; + } + ); + + vm.onNavigationChanged = function(tab) { vm.activeTab.active = false; vm.activeTab = tab; diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index acd63dea4f..d3e410e39c 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2422,5 +2422,7 @@ To manage your website, simply open the Umbraco back office and start adding con Thumbnail Add thumbnail + Create empty + Clipboard diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index d370de60bf..5c833e84a5 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2433,5 +2433,7 @@ To manage your website, simply open the Umbraco back office and start adding con Thumbnail Add thumbnail + Create empty + Clipboard From 3647cc366a70a952fbbb695261599b26b069b440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 15 Apr 2020 15:07:32 +0200 Subject: [PATCH 126/508] Slightly change for shadow on block-picker item hover --- .../blockeditor/blockcard/blockcard.component.less | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.less index 703c1bca26..d269a87b48 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockcard/blockcard.component.less @@ -11,8 +11,14 @@ umb-block-card { box-shadow: 0 1px 2px rgba(0,0,0,.2); overflow: hidden; + transition: box-shadow 120ms; + cursor: pointer; + &:hover { + box-shadow: 0 1px 3px rgba(@ui-action-type-hover, .5); + } + &.--isOpen { &::after { content: ""; From 9fa34764ac6c662d781aefc22eefcb54646884f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 15 Apr 2020 15:18:48 +0200 Subject: [PATCH 127/508] Localization of BlockEditor Settings Tab --- .../blockeditor/blockeditor.controller.js | 20 +++++++++++-------- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js index c5e3776e91..8db36220bd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js @@ -32,16 +32,20 @@ angular.module("umbraco") } if (vm.settings && vm.settings.variants) { - var settingsTab = { - "name": "Settings", - "alias": "settings", - "icon": "icon-settings", - "view": "views/common/infiniteeditors/elementeditor/elementeditor.settings.html" - }; - vm.tabs.push(settingsTab); + localizationService.localize("blockEditor_tabBlockSettings").then( + function (settingsName) { + var settingsTab = { + "name": settingsName, + "alias": "settings", + "icon": "icon-settings", + "view": "views/common/infiniteeditors/elementeditor/elementeditor.settings.html" + }; + vm.tabs.push(settingsTab); + } + ); } - // activate frst app: + // activate first app: if (vm.tabs.length > 0) { vm.tabs[0].active = true; } diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index d3e410e39c..1902ec4dbf 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2424,5 +2424,6 @@ To manage your website, simply open the Umbraco back office and start adding con Add thumbnail Create empty Clipboard + Settings From e9a2b92658ad12675609892f1504d4dd0c14f34d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 15 Apr 2020 15:20:45 +0200 Subject: [PATCH 128/508] localizationService --- .../infiniteeditors/blockeditor/blockeditor.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js index 8db36220bd..81d9bb754d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js @@ -1,7 +1,7 @@ //used for the media picker dialog angular.module("umbraco") .controller("Umbraco.Editors.BlockEditorController", - function ($scope) { + function ($scope, localizationService) { var vm = this; vm.content = $scope.model.content; From a19ebaa8d02c9101269e065f24b4f52a98117853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 15 Apr 2020 15:51:55 +0200 Subject: [PATCH 129/508] only filter when more than 8 items available --- .../src/views/propertyeditors/blocklist/blocklist.component.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 69bfb1a71a..185b22d764 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -238,6 +238,7 @@ orderBy: "$index", view: "views/common/infiniteeditors/blockpicker/blockpicker.html", size: (amountOfAvailableTypes > 8 ? "medium" : "small"), + filter: (amountOfAvailableTypes > 8), clickPasteItem: function(item) { if (item.type === "elementTypeArray") { var indexIncrementor = 0; From 5236dbff7536fc8120f6b3440efb2cf00fd05616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 15 Apr 2020 15:58:26 +0200 Subject: [PATCH 130/508] Add multiple blocks if hold down CTRL or SuperKey --- .../blockpicker/blockpicker.controller.js | 4 ++-- .../blockpicker/blockpicker.html | 4 ++-- .../blocklist/blocklist.component.js | 17 +++++++++++------ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js index 1deb010073..77be5b741a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js @@ -44,9 +44,9 @@ angular.module("umbraco") vm.model = $scope.model; - vm.selectItem = function(item) { + vm.selectItem = function(item, $event) { vm.model.selectedItem = item; - vm.model.submit($scope.model); + vm.model.submit($scope.model, $event); } vm.close = function() { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html index 5206c7e66d..196eec4cdb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html @@ -33,7 +33,7 @@ ng-repeat="block in vm.model.availableItems | filter:searchTerm" block-config-model="block.blockConfigModel" element-type-model="block.elementTypeModel" - ng-click="vm.selectItem(block)"> + ng-click="vm.selectItem(block, $event)">
    @@ -55,7 +55,7 @@ block-config-model="block.blockConfigModel" element-type-model="block.elementTypeModel" ng-repeat="block in vm.model.clipboardItems" - ng-click="vm.model.clickPasteItem(block)"> + ng-click="vm.model.clickPasteItem(block, $event)">
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 185b22d764..efc0e49d21 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -239,7 +239,7 @@ view: "views/common/infiniteeditors/blockpicker/blockpicker.html", size: (amountOfAvailableTypes > 8 ? "medium" : "small"), filter: (amountOfAvailableTypes > 8), - clickPasteItem: function(item) { + clickPasteItem: function(item, mouseEvent) { if (item.type === "elementTypeArray") { var indexIncrementor = 0; item.pasteData.forEach(function (entry) { @@ -250,16 +250,21 @@ } else { requestPasteFromClipboard(createIndex, item.pasteData); } - blockPickerModel.close(); + if(!(mouseEvent.ctrlKey || mouseEvent.metaKey)) { + blockPickerModel.close(); + } }, - submit: function(blockPickerModel) { + submit: function(blockPickerModel, mouseEvent) { var added = false; if (blockPickerModel && blockPickerModel.selectedItem) { added = addNewBlock(createIndex, blockPickerModel.selectedItem.blockConfigModel.contentTypeAlias); } - blockPickerModel.close(); - if (added && vm.model.config.useInlineEditingAsDefault !== true && vm.blocks.length > createIndex) { - editBlock(vm.blocks[createIndex]); + + if(!(mouseEvent.ctrlKey || mouseEvent.metaKey)) { + blockPickerModel.close(); + if (added && vm.model.config.useInlineEditingAsDefault !== true && vm.blocks.length > createIndex) { + editBlock(vm.blocks[createIndex]); + } } }, close: function() { From efcce3d25d4efa582773495bd0718114e2983042 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 17 Apr 2020 10:43:18 +1000 Subject: [PATCH 131/508] adds notes --- .../Migrations/Upgrade/V_8_7_0/ConvertToElements.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ConvertToElements.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ConvertToElements.cs index 2bc017b643..fbcd55ec92 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ConvertToElements.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/ConvertToElements.cs @@ -36,6 +36,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 elementTypeIds = elementTypeIds.Union(parentElementTypeIds).ToList(); // Convert all those document types to element type + // TODO: We need to wait on an update from @benjaminc to make this 'safe' + // see https://github.com/umbraco/Umbraco-CMS/pull/7910#discussion_r409927495 foreach (var docType in docTypes) { if (!elementTypeIds.Contains(docType.NodeId)) continue; From dcce1f4a2e97dc241aaa6427aa645e5d24a6093f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 22 Apr 2020 15:18:25 +0200 Subject: [PATCH 132/508] ability to add a scoped stylesheet for block view --- .../imageblock/imageblock.editor.html | 7 ++- .../inlineblock/inlineblock.editor.html | 4 +- .../labelblock/labelblock.editor.html | 2 +- .../textareablock/textareablock.editor.html | 3 -- .../blocklist/blocklist.block.component.js | 36 ++++++++++++++ .../blocklist/blocklist.component.html | 7 ++- .../blocklist/blocklist.component.js | 22 +++++++-- .../blocklist.scopedblock.component.js | 44 +++++++++++++++++ ...t.blockconfiguration.overlay.controller.js | 49 ++++++++++++++++--- .../blocklist.blockconfiguration.overlay.html | 21 ++++++++ .../PropertyEditors/BlockListConfiguration.cs | 3 ++ 11 files changed, 175 insertions(+), 23 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.scopedblock.component.js diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html index 32db64b16b..7f09aab525 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html @@ -1,9 +1,8 @@ diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html index 028a10d434..d61d343a64 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html @@ -1,5 +1,5 @@ -
    - diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html index ca49a3f5b8..06f7cf49d3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html @@ -6,9 +6,6 @@ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js new file mode 100644 index 0000000000..72229e773f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js @@ -0,0 +1,36 @@ +(function () { + "use strict"; + + + /** + * @ngdoc component + * @name Umbraco.umbBlockListBlockContent + * @function + * + * @description + * The component for a style-inheriting block of the block list property editor. + */ + angular + .module("umbraco") + .component("umbBlockListBlockContent", { + template: '
    ', + controller: BlockListBlockContentController, + controllerAs: "vm", + bindings: { + view: "@", + block: "=", + api: "=" + } + } + ); + + function BlockListBlockContentController($scope) { + var vm = this; + vm.$onInit = function() { + $scope.block = vm.block; + $scope.api = vm.api; + }; + } + + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index 360201c27d..9a14f1148b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -18,8 +18,11 @@
    -
    -
    + + + + +
    + +
    +
    + +
    +
    + + +
    + +
    +
    + +
    +
    +
    +
    diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index fba80bd57d..5fc016f694 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -47,6 +47,9 @@ namespace Umbraco.Web.PropertyEditors [JsonProperty("view")] public string View { get; set; } + [JsonProperty("stylesheet")] + public string Stylesheet { get; set; } + [JsonProperty("label")] public string Label { get; set; } From acc90011f6a033326fb72ec6ffa2d711e0dd4643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 23 Apr 2020 07:37:17 +0200 Subject: [PATCH 133/508] make scoped block draggable + style adjustments --- .../views/blockelements/inlineblock/inlineblock.editor.less | 1 + .../views/blockelements/labelblock/labelblock.editor.less | 5 +++-- .../views/propertyeditors/blocklist/blocklist.component.html | 4 ++-- .../views/propertyeditors/blocklist/blocklist.component.less | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less index 2702ca163a..c1e2c72de3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less @@ -1,5 +1,6 @@ .blockelement-inlineblock-editor { + display: block; margin-bottom: 4px; margin-top: 4px; border: 1px solid @gray-9; diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less index 5ce53aece4..ce5883df6a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less @@ -1,5 +1,8 @@ .blockelement-labelblock-editor { + display: block; + margin-bottom: 4px; + margin-top: 4px; width: 100%; min-height: 48px; border: 1px solid @gray-9; @@ -11,8 +14,6 @@ text-align: left; padding-left: 20px; padding-bottom: 2px; - margin-bottom: 2px; - margin-top: 2px; user-select: none; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index 9a14f1148b..86a1f549af 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -19,9 +19,9 @@
    - + - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less index d118e6356c..35eece038e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less @@ -71,7 +71,7 @@ opacity: 0; outline: none; height: 12px; - margin-top: -7px; + margin-top: -9px; padding-top: 6px; margin-bottom: -6px; transition: opacity 240ms; From 31db52fe1be89a916a8be9f5aaed2afe7b96ffaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 23 Apr 2020 10:22:55 +0200 Subject: [PATCH 134/508] provide index for custom view --- .../propertyeditors/blocklist/blocklist.block.component.js | 5 +++-- .../views/propertyeditors/blocklist/blocklist.component.html | 4 ++-- .../blocklist/blocklist.scopedblock.component.js | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js index 72229e773f..0385460864 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.block.component.js @@ -15,11 +15,12 @@ .component("umbBlockListBlockContent", { template: '
    ', controller: BlockListBlockContentController, - controllerAs: "vm", + controllerAs: "model", bindings: { view: "@", block: "=", - api: "=" + api: "=", + index: "<" } } ); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index 86a1f549af..070ee4839b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -19,9 +19,9 @@
    - + - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.scopedblock.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.scopedblock.component.js index b4f815a7ce..0498db32c3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.scopedblock.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.scopedblock.component.js @@ -14,12 +14,13 @@ .module("umbraco") .component("umbBlockListScopedBlockContent", { controller: BlockListScopedBlockContentController, - controllerAs: "vm", + controllerAs: "model", bindings: { stylesheet: "@", view: "@", block: "=", - api: "=" + api: "=", + index: "<" } } ); From 04a77aa7c0a2ef9ef7d43407e8c590510e3a4384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 23 Apr 2020 10:23:49 +0200 Subject: [PATCH 135/508] rename contentModel to data + rename layoutModel to layout --- .../common/services/blockeditor.service.js | 54 +++++++++---------- .../blocklist/blocklist.component.js | 8 +-- .../services/block-editor-service.spec.js | 6 +-- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index 67def3c75a..c6897e63a2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -9,7 +9,7 @@ * Simple mapping from property model content entry to editing model, * needs to stay simple to avoid deep watching. */ - function mapToElementModel(elementModel, contentModel) { + function mapToElementModel(elementModel, dataModel) { var variant = elementModel.variants[0]; @@ -18,8 +18,8 @@ for (var p = 0; p < tab.properties.length; p++) { var prop = tab.properties[p]; - if (contentModel[prop.alias]) { - prop.value = contentModel[prop.alias]; + if (dataModel[prop.alias]) { + prop.value = dataModel[prop.alias]; } } } @@ -29,7 +29,7 @@ * Simple mapping from elementModel to property model content entry, * needs to stay simple to avoid deep watching. */ - function mapToPropertyModel(elementModel, contentModel) { + function mapToPropertyModel(elementModel, dataModel) { var variant = elementModel.variants[0]; @@ -39,14 +39,14 @@ for (var p = 0; p < tab.properties.length; p++) { var prop = tab.properties[p]; if (prop.value) { - contentModel[prop.alias] = prop.value; + dataModel[prop.alias] = prop.value; } } } } - function mapValueToPropertyModel(value, alias, contentModel) { - contentModel[alias] = value; + function mapValueToPropertyModel(value, alias, dataModel) { + dataModel[alias] = value; } /** @@ -88,8 +88,8 @@ function getBlockLabel(blockModel) { if(blockModel.labelInterpolator) { - // We are just using the contentModel, since its a key/value object that is live synced. (if we need to add additional vars, we could make a shallow copy and apply those.) - return blockModel.labelInterpolator(blockModel.contentModel); + // We are just using the data model, since its a key/value object that is live synced. (if we need to add additional vars, we could make a shallow copy and apply those.) + return blockModel.labelInterpolator(blockModel.data); } return blockModel.content.contentTypeName; } @@ -105,7 +105,7 @@ // Start watching each property value. var variant = model.variants[0]; var field = forSettings ? "settings" : "content"; - var watcherCreator = forSettings ? createSettingsModelPropWatcher : createContentModelPropWatcher; + var watcherCreator = forSettings ? createSettingsModelPropWatcher : createDataModelPropWatcher; for (var t = 0; t < variant.tabs.length; t++) { var tab = variant.tabs[t]; for (var p = 0; p < tab.properties.length; p++) { @@ -123,10 +123,10 @@ /** * Used to create a scoped watcher for a content property on a blockModel. */ - function createContentModelPropWatcher(blockModel, prop) { + function createDataModelPropWatcher(blockModel, prop) { return function() { // sync data: - blockModel.contentModel[prop.alias] = prop.value; + blockModel.data[prop.alias] = prop.value; // regenerate label. // TODO: could use a debounce. @@ -140,7 +140,7 @@ function createSettingsModelPropWatcher(blockModel, prop) { return function() { // sync data: - blockModel.layoutModel.settings[prop.alias] = prop.value; + blockModel.layout.settings[prop.alias] = prop.value; } } @@ -265,14 +265,14 @@ var udi = layoutEntry.udi; - var contentModel = this._getDataByUdi(udi); + var dataModel = this._getDataByUdi(udi); - if (contentModel === null) { + if (dataModel === null) { console.error("Couldnt find content model of "+udi) return null; } - var blockConfiguration = this.getBlockConfiguration(contentModel.contentTypeAlias); + var blockConfiguration = this.getBlockConfiguration(dataModel.contentTypeAlias); if (blockConfiguration === null) { console.error("The block entry of "+udi+" is not begin initialized cause its contentTypeAlias is not allowed for this PropertyEditor") @@ -296,10 +296,10 @@ blockModel.content = angular.copy(contentScaffold); blockModel.content.udi = udi; - mapToElementModel(blockModel.content, contentModel); + mapToElementModel(blockModel.content, dataModel); - blockModel.contentModel = contentModel; - blockModel.layoutModel = layoutEntry; + blockModel.data = dataModel; + blockModel.layout = layoutEntry; blockModel.watchers = []; if (blockConfiguration.settingsElementTypeAlias) { @@ -353,10 +353,10 @@ var udi = blockModel.content.key; - mapToPropertyModel(blockModel.content, blockModel.contentModel); + mapToPropertyModel(blockModel.content, blockModel.data); // TODO: implement settings, sync settings to layout entry. - // mapToPropertyModel(blockModel.settings, blockModel.layoutModel.settings) + // mapToPropertyModel(blockModel.settings, blockModel.layout.settings) }, @@ -398,23 +398,23 @@ * Insert data from ElementType Model * @return {Object} Layout entry object, to be inserted at a decired location in the layout object. */ - createFromElementType: function(elementTypeContentModel) { + createFromElementType: function(elementTypeDataModel) { - elementTypeContentModel = angular.copy(elementTypeContentModel); + elementTypeDataModel = angular.copy(elementTypeDataModel); - var contentTypeAlias = elementTypeContentModel.contentTypeAlias; + var contentTypeAlias = elementTypeDataModel.contentTypeAlias; var layoutEntry = this.create(contentTypeAlias); if(layoutEntry === null) { return null; } - var contentModel = this._getDataByUdi(layoutEntry.udi); - if(contentModel === null) { + var dataModel = this._getDataByUdi(layoutEntry.udi); + if(dataModel === null) { return null; } - mapToPropertyModel(elementTypeContentModel, contentModel); + mapToPropertyModel(elementTypeDataModel, dataModel); return layoutEntry; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index c302856807..4115523e73 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -200,17 +200,17 @@ function editBlock(blockModel, hideContent) { // make a clone to avoid editing model directly. - var blockContentModelClone = angular.copy(blockModel.content); + var blockContentClone = angular.copy(blockModel.content); var blockSettingsModelClone = null; if (blockModel.config.settingsElementTypeAlias) { - blockSettingsModelClone = angular.copy(blockModel.settings); + blockSettingsClone = angular.copy(blockModel.settings); } var blockEditorModel = { - content: blockContentModelClone, + content: blockContentClone, hideContent: hideContent, - settings: blockSettingsModelClone, + settings: blockSettingsClone, title: blockModel.label, view: "views/common/infiniteeditors/blockeditor/blockeditor.html", size: blockModel.config.editorSize || "medium", diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js index 6abf85c9b6..094fd33558 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js @@ -110,7 +110,7 @@ var blockModel = modelObject.getBlockModel(layout[0]); expect(blockModel).not.toBeUndefined(); - expect(blockModel.contentModel.udi).toBe(propertyModelMock.data[0].udi); + expect(blockModel.data.udi).toBe(propertyModelMock.data[0].udi); expect(blockModel.content.variants[0].tabs[0].properties[0].value).toBe(propertyModelMock.data[0].testproperty); done(); @@ -135,8 +135,8 @@ $rootScope.$digest();// invoke angularJS Store. - expect(blockModel.contentModel).toBe(propertyModel.data[0]); - expect(blockModel.contentModel.testproperty).toBe("anotherTestValue"); + expect(blockModel.data).toBe(propertyModel.data[0]); + expect(blockModel.data.testproperty).toBe("anotherTestValue"); expect(propertyModel.data[0].testproperty).toBe("anotherTestValue"); // From fc8fc468842abee57c3c5f9f0d0fa1ceb511097c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 23 Apr 2020 11:01:10 +0200 Subject: [PATCH 136/508] clean up --- .../views/propertyeditors/blocklist/blocklist.component.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 4115523e73..1cb0a0c2d6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -26,7 +26,7 @@ } }); - function BlockListController($scope, $interpolate, editorService, clipboardService, localizationService, overlayService, blockEditorService) { + function BlockListController($scope, editorService, clipboardService, localizationService, overlayService, blockEditorService) { var unsubscribe = []; var modelObject; @@ -201,7 +201,7 @@ // make a clone to avoid editing model directly. var blockContentClone = angular.copy(blockModel.content); - var blockSettingsModelClone = null; + var blockSettingsClone = null; if (blockModel.config.settingsElementTypeAlias) { blockSettingsClone = angular.copy(blockModel.settings); From 37e37a14cec966c511c78551115b36fb01818c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 27 Apr 2020 12:34:15 +0200 Subject: [PATCH 137/508] more localization --- ...blocklist.blockconfiguration.controller.js | 52 +++--- ...t.blockconfiguration.overlay.controller.js | 171 ++++++++++-------- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 8 + .../Umbraco/config/lang/en_us.xml | 9 + 4 files changed, 139 insertions(+), 101 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js index 494ef35137..29ea90c0f3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js @@ -92,32 +92,36 @@ var availableItems = vm.getAvailableElementTypes() - var elemTypeSelectorOverlay = { - view: "itempicker", - title: "no title jet", - availableItems: availableItems, - selectedItems: selectedItems, - createNewItem: { - action: function() { - overlayService.close(); - vm.createElementTypeAndAdd(vm.addBlockFromElementTypeAlias); + localizationService.localizeMany(["blockEditor_headlineCreateBlock", "blockEditor_labelcreateNewElementType"]).then(function(localized) { + + var elemTypeSelectorOverlay = { + view: "itempicker", + title: localizedlocalized[0], + availableItems: availableItems, + selectedItems: selectedItems, + createNewItem: { + action: function() { + overlayService.close(); + vm.createElementTypeAndAdd(vm.addBlockFromElementTypeAlias); + }, + icon: "icon-add", + name: localizedlocalized[1] }, - icon: "icon-add", - name: "Create new" - }, - position: "target", - event: $event, - size: availableItems.length < 7 ? "small" : "medium", - submit: function (overlay) { - vm.addBlockFromElementTypeAlias(overlay.selectedItem.alias); - overlayService.close(); - }, - close: function () { - overlayService.close(); - } - }; + position: "target", + event: $event, + size: availableItems.length < 7 ? "small" : "medium", + submit: function (overlay) { + vm.addBlockFromElementTypeAlias(overlay.selectedItem.alias); + overlayService.close(); + }, + close: function () { + overlayService.close(); + } + }; - overlayService.open(elemTypeSelectorOverlay); + overlayService.open(elemTypeSelectorOverlay); + + }); }; vm.createElementTypeAndAdd = function(callback) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js index 055993f164..0eb8597cc1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js @@ -46,33 +46,37 @@ vm.addSettingsForBlock = function ($event, block) { - var elemTypeSelectorOverlay = { - view: "itempicker", - title: "Pick settings (missing translation)", - availableItems: vm.elementTypes, - position: "target", - event: $event, - size: vm.elementTypes.length < 7 ? "small" : "medium", - createNewItem: { - action: function() { - overlayService.close(); - vm.createElementTypeAndAdd((alias) => { - vm.applySettingsToBlock(block, alias); - }); - }, - icon: "icon-add", - name: "Create new" - }, - submit: function (overlay) { - vm.applySettingsToBlock(block, overlay.selectedItem.alias); - overlayService.close(); - }, - close: function () { - overlayService.close(); - } - }; + localizationService.localizeMany(["blockEditor_headlineAddSettingsElementType", "blockEditor_labelcreateNewElementType"]).then(function(localized) { - overlayService.open(elemTypeSelectorOverlay); + var elemTypeSelectorOverlay = { + view: "itempicker", + title: localized[0], + availableItems: vm.elementTypes, + position: "target", + event: $event, + size: vm.elementTypes.length < 7 ? "small" : "medium", + createNewItem: { + action: function() { + overlayService.close(); + vm.createElementTypeAndAdd((alias) => { + vm.applySettingsToBlock(block, alias); + }); + }, + icon: "icon-add", + name: localized[1] + }, + submit: function (overlay) { + vm.applySettingsToBlock(block, overlay.selectedItem.alias); + overlayService.close(); + }, + close: function () { + overlayService.close(); + } + }; + + overlayService.open(elemTypeSelectorOverlay); + + }); }; vm.applySettingsToBlock = function(block, alias) { block.settingsElementTypeAlias = alias; @@ -102,23 +106,27 @@ vm.addViewForBlock = function(block) { - const filePicker = { - title: "Select view (TODO need translation)", - section: "settings", - treeAlias: "files", - entityType: "file", - isDialog: true, - select: function (node) { - console.log(node) - const filepath = decodeURIComponent(node.id.replace(/\+/g, " ")); - block.view = filepath; - editorService.close(); - }, - close: function () { - editorService.close(); - } - }; - editorService.treePicker(filePicker); + localizationService.localize("blockEditor_headlineSelectView").then(function(localizedTitle) { + + const filePicker = { + title: localizedTitle, + section: "settings", + treeAlias: "files", + entityType: "file", + isDialog: true, + select: function (node) { + console.log(node) + const filepath = decodeURIComponent(node.id.replace(/\+/g, " ")); + block.view = filepath; + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.treePicker(filePicker); + + }); } vm.requestRemoveViewForBlock = function(block) { localizationService.localizeMany(["general_remove", "defaultdialogs_confirmremoveusageof"]).then(function (data) { @@ -142,22 +150,26 @@ vm.addStylesheetForBlock = function(block) { - const filePicker = { - title: "Select Stylesheet (TODO need translation)", - section: "settings", - treeAlias: "files", - entityType: "file", - isDialog: true, - select: function (node) { - const filepath = decodeURIComponent(node.id.replace(/\+/g, " ")); - block.stylesheet = filepath; - editorService.close(); - }, - close: function () { - editorService.close(); - } - }; - editorService.treePicker(filePicker); + localizationService.localize("blockEditor_headlineAddCustomStylesheet").then(function(localizedTitle) { + + const filePicker = { + title: localizedTitle, + section: "settings", + treeAlias: "files", + entityType: "file", + isDialog: true, + select: function (node) { + const filepath = decodeURIComponent(node.id.replace(/\+/g, " ")); + block.stylesheet = filepath; + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.treePicker(filePicker); + + }); } vm.requestRemoveStylesheetForBlock = function(block) { localizationService.localizeMany(["general_remove", "defaultdialogs_confirmremoveusageof"]).then(function (data) { @@ -181,24 +193,29 @@ vm.addThumbnailForBlock = function(block) { - const thumbnailPicker = { - title: "Select thumbnail (TODO need translation)", - section: "settings", - treeAlias: "files", - entityType: "file", - isDialog: true, - filter: function (i) { - return !(i.name.indexOf(".jpg") !== -1 || i.name.indexOf(".jpeg") !== -1 || i.name.indexOf(".png") !== -1 || i.name.indexOf(".svg") !== -1 || i.name.indexOf(".webp") !== -1 || i.name.indexOf(".gif") !== -1); - }, - select: function (file) { - block.thumbnail = file.name; - editorService.close(); - }, - close: function () { - editorService.close(); - } - }; - editorService.treePicker(thumbnailPicker); + + localizationService.localize("blockEditor_headlineAddThumbnail").then(function(localizedTitle) { + + const thumbnailPicker = { + title: localizedTitle, + section: "settings", + treeAlias: "files", + entityType: "file", + isDialog: true, + filter: function (i) { + return !(i.name.indexOf(".jpg") !== -1 || i.name.indexOf(".jpeg") !== -1 || i.name.indexOf(".png") !== -1 || i.name.indexOf(".svg") !== -1 || i.name.indexOf(".webp") !== -1 || i.name.indexOf(".gif") !== -1); + }, + select: function (file) { + block.thumbnail = file.name; + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.treePicker(thumbnailPicker); + + }); } vm.removeThumbnailForBlock = function(entry) { entry.thumbnail = null; diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 1902ec4dbf..e617f9b07f 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2404,6 +2404,14 @@ To manage your website, simply open the Umbraco back office and start adding con Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it! + Create new block + Attach a settings section + Select view + Select stylesheet + Choose thumbnail + Create new + Custom stylesheet + Add stylesheet Block apperance Data models Showcase diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 5c833e84a5..df3cd8c4fe 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2415,6 +2415,14 @@ To manage your website, simply open the Umbraco back office and start adding con Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it! + Create new block + Attach a settings section + Select view + Select stylesheet + Choose thumbnail + Create new + Custom stylesheet + Add stylesheet Block apperance Data models Showcase @@ -2435,5 +2443,6 @@ To manage your website, simply open the Umbraco back office and start adding con Add thumbnail Create empty Clipboard + Settings From 223e8b122e62cb9d9df4307d2f122f28a03c9a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 27 Apr 2020 12:34:54 +0200 Subject: [PATCH 138/508] openSettings option for block editor --- .../blockeditor/blockeditor.controller.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js index 81d9bb754d..9fb32339f3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockeditor/blockeditor.controller.js @@ -23,12 +23,14 @@ angular.module("umbraco") if($scope.model.hideContent) { apps.splice(apps.indexOf(contentApp), 1); + } else if ($scope.model.openSettings !== true) { + contentApp.active = true; } // remove info app: var infoAppIndex = apps.findIndex(entry => entry.alias === "umbInfo"); apps.splice(infoAppIndex, 1); - + } if (vm.settings && vm.settings.variants) { @@ -41,15 +43,13 @@ angular.module("umbraco") "view": "views/common/infiniteeditors/elementeditor/elementeditor.settings.html" }; vm.tabs.push(settingsTab); + if ($scope.model.openSettings) { + settingsTab.active = true; + } } ); } - // activate first app: - if (vm.tabs.length > 0) { - vm.tabs[0].active = true; - } - vm.submitAndClose = function () { if ($scope.model && $scope.model.submit) { $scope.model.submit($scope.model); From 5ef98ada8c0cbcb52954c2eeea2f2432773725e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 27 Apr 2020 12:36:25 +0200 Subject: [PATCH 139/508] minor changes for a better developer experience --- .../infiniteeditors/blockpicker/blockpicker.html | 1 + .../blocklist/blocklist.block.component.js | 10 +++++----- .../blocklist/blocklist.component.html | 12 ++++++------ .../propertyeditors/blocklist/blocklist.component.js | 10 ++++++++-- .../blocklist/blocklist.component.less | 1 + .../blocklist/blocklist.scopedblock.component.js | 12 ++++++------ 6 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html index 196eec4cdb..fb7e946ee7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.html @@ -30,6 +30,7 @@
    ', + template: '
    ', controller: BlockListBlockContentController, controllerAs: "model", bindings: { @@ -26,10 +26,10 @@ ); function BlockListBlockContentController($scope) { - var vm = this; - vm.$onInit = function() { - $scope.block = vm.block; - $scope.api = vm.api; + var model = this; + model.$onInit = function() { + $scope.block = model.block; + $scope.api = model.api; }; } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index 070ee4839b..4167667af0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -19,25 +19,25 @@
    - + - +
    - - - diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less deleted file mode 100644 index 2ea03bd703..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less +++ /dev/null @@ -1,15 +0,0 @@ -.blockelement-imageblock-editor { - - width: 100%; - min-height: 42px; - padding-bottom: 10px; - padding-top: 10px; - - img { - width: 100%; - max-width: 500px; - border: none; - resize: none; - border-radius: @baseBorderRadius; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js deleted file mode 100644 index 1b074a0cb6..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js +++ /dev/null @@ -1,19 +0,0 @@ -//used for the media picker dialog -angular.module("umbraco") -.controller("Umbraco.Editors.TextAreaBlockElementEditorController", - function ($scope) { - - var vm = this; - - vm.firstProperty = $scope.block.content.variants[0].tabs[0].properties[0]; - /* - vm.onBlur = function() { - if (vm.firstProperty.value === null || vm.firstProperty.value === "") { - $scope.blockApi.deleteBlock($scope.block); - } - } - */ - // TODO: if text is empty and user hits backspace, then remove this block. - } - -); diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html deleted file mode 100644 index 06f7cf49d3..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html +++ /dev/null @@ -1,12 +0,0 @@ - -
    - - - - - -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less deleted file mode 100644 index fe11d0cd0c..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less +++ /dev/null @@ -1,27 +0,0 @@ -.blockelement-textareablock-editor { - - width: 100%; - padding-bottom: 10px; - padding-top: 10px; - - padding-left: 24px; - padding-right: 24px; - - min-height: 64px; - box-sizing: border-box; - - textarea { - display: block; - width: 100%; - max-width: 640px; - margin-left: auto; - margin-right: auto; - border: none; - resize: none; - overflow: auto; - - font-size: 18px; - font-family: Georgia,Cambria,"Times New Roman",Times,serif; - line-height: 1.25; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index b7d6cb5315..a2218ceb82 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -139,7 +139,7 @@ if (block === null) return null; // Lets apply fallback views, and make the view available directly on the blockModel. - block.view = (block.config.view ? "/" + block.config.view : (inlineEditing ? "views/blockelements/inlineblock/inlineblock.editor.html" : "views/blockelements/labelblock/labelblock.editor.html")); + block.view = (block.config.view ? "/" + block.config.view : (inlineEditing ? "views/propertyeditors/blocklist/blocklistentryeditors/inlineblock/inlineblock.editor.html" : "views/propertyeditors/blocklist/blocklistentryeditors/labelblock/labelblock.editor.html")); block.showSettings = block.config.settingsElementTypeAlias != null; diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/inlineblock/inlineblock.editor.controller.js similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.controller.js rename to src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/inlineblock/inlineblock.editor.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/inlineblock/inlineblock.editor.html similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.html rename to src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/inlineblock/inlineblock.editor.html diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/inlineblock/inlineblock.editor.less similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/blockelements/inlineblock/inlineblock.editor.less rename to src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/inlineblock/inlineblock.editor.less diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/labelblock/labelblock.editor.html similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html rename to src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/labelblock/labelblock.editor.html diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/labelblock/labelblock.editor.less similarity index 96% rename from src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less rename to src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/labelblock/labelblock.editor.less index ce5883df6a..974662b416 100644 --- a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/labelblock/labelblock.editor.less @@ -21,6 +21,7 @@ i { font-size: 22px; + margin-right: 5px; display: inline-block; vertical-align: middle; } From b6dfee3a91b72399915ce4c3799bcf51dc6f2112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 29 May 2020 10:31:30 +0200 Subject: [PATCH 150/508] limit labelinterpretator to only runs ones pr. edit. and lets make sure to have a label thought we dont have any properties. --- .../src/common/services/blockeditor.service.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index c6897e63a2..6f12db6c1e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -87,7 +87,7 @@ function getBlockLabel(blockModel) { - if(blockModel.labelInterpolator) { + if(blockModel.labelInterpolator !== undefined) { // We are just using the data model, since its a key/value object that is live synced. (if we need to add additional vars, we could make a shallow copy and apply those.) return blockModel.labelInterpolator(blockModel.data); } @@ -118,6 +118,10 @@ blockModel.watchers.push(isolatedScope.$watch("blockModels._" + blockModel.key + "." + field + ".variants[0].tabs[" + t + "].properties[" + p + "].value", watcherCreator(blockModel, prop))); } } + if (blockModel.watchers.length === 0) { + // If no watcher where created, it means we have no properties to watch. This means that nothing will activate our generate the label, since its only triggered by watchers. + blockModel.updateLabel(); + } } /** @@ -128,9 +132,7 @@ // sync data: blockModel.data[prop.alias] = prop.value; - // regenerate label. - // TODO: could use a debounce. - blockModel.label = getBlockLabel(blockModel); + blockModel.updateLabel(); } } @@ -283,9 +285,12 @@ var blockModel = {}; blockModel.key = String.CreateGuid().replace(/-/g, ""); blockModel.config = angular.copy(blockConfiguration); - if (blockModel.config.label) { + if (blockModel.config.label && blockModel.config.label !== "") { blockModel.labelInterpolator = $interpolate(blockModel.config.label); } + blockModel.updateLabel = _.debounce(function() { + this.label = getBlockLabel(this); + }, 100); var contentScaffold = this.getScaffoldFor(blockConfiguration.contentTypeAlias); if(contentScaffold === null) { From cb9f6144465031a59d27356f622305bcfbb9cc51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 29 May 2020 10:59:33 +0200 Subject: [PATCH 151/508] fixed inline views gulp watcher --- .../gulp/tasks/watchTask.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js index a94314abd6..810146d7c8 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js @@ -1,7 +1,7 @@ 'use strict'; const config = require('../config'); -const {watch, parallel, dest, src} = require('gulp'); +const {watch, series, parallel, dest, src} = require('gulp'); var _ = require('lodash'); var MergeStream = require('merge-stream'); @@ -33,16 +33,16 @@ function watchTask(cb) { var viewWatcher; _.forEach(config.sources.views, function (group) { if(group.watch !== false) { - viewWatcher = watch(group.files, { ignoreInitial: true, interval: watchInterval }); - viewWatcher.on('change', function(path, stats) { + viewWatcher = watch(group.files, { ignoreInitial: true, interval: watchInterval }, function() { console.log("copying " + group.files + " to " + config.root + config.targets.views + group.folder); - return MergeStream( - src(group.files) - .pipe( dest(config.root + config.targets.views + group.folder) ) - , js() - ); + return parallel( + function MoveViewsAndRegenerateJS() { + return src(group.files).pipe( dest(config.root + config.targets.views + group.folder) ); + }, + js + )(); }); } From 1126a2d6992c4dc185d347ea43c51524c61e96bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 29 May 2020 11:00:28 +0200 Subject: [PATCH 152/508] changed vm to a better controller instance name --- .../src/common/services/blockeditor.service.js | 2 +- .../propertyeditors/blocklist/blocklist.component.html | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index 6f12db6c1e..eaf2fc79fc 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -270,7 +270,7 @@ var dataModel = this._getDataByUdi(udi); if (dataModel === null) { - console.error("Couldnt find content model of "+udi) + console.error("Couldnt find content model of " + udi) return null; } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html index 4167667af0..8d2714382f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -11,10 +11,10 @@ type="button" class="btn-reset umb-block-list__block--create-button" ng-click="vm.showCreateDialog($index, $event)" - ng-controller="Umbraco.PropertyEditors.BlockListPropertyEditor.CreateButtonController as vm" - ng-mousemove="vm.onMouseMove($event)" + ng-controller="Umbraco.PropertyEditors.BlockListPropertyEditor.CreateButtonController as inlineCreateButtonCtrl" + ng-mousemove="inlineCreateButtonCtrl.onMouseMove($event)" > -
    +
    +
    +
    From ade450253363b2019551ed244f9f0ea5eb7dafdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 29 May 2020 11:23:02 +0200 Subject: [PATCH 153/508] make watch for views work again. --- src/Umbraco.Web.UI.Client/gulp/config.js | 12 +++++++++--- src/Umbraco.Web.UI.Client/gulp/modes.js | 12 +++++++++++- src/Umbraco.Web.UI.Client/gulp/tasks/test.js | 17 +++++++++++++++-- .../gulp/tasks/watchTask.js | 19 ++++++++++++------- .../gulp/util/processJs.js | 4 +++- src/Umbraco.Web.UI.Client/gulpfile.js | 10 +++++----- 6 files changed, 55 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/gulp/config.js b/src/Umbraco.Web.UI.Client/gulp/config.js index 39dc9bb2a4..3c32832ba0 100755 --- a/src/Umbraco.Web.UI.Client/gulp/config.js +++ b/src/Umbraco.Web.UI.Client/gulp/config.js @@ -3,10 +3,16 @@ module.exports = { compile: { build: { - sourcemaps: false + sourcemaps: false, + embedtemplates: true }, dev: { - sourcemaps: true + sourcemaps: true, + embedtemplates: true + }, + test: { + sourcemaps: false, + embedtemplates: true } }, sources: { @@ -17,7 +23,7 @@ module.exports = { installer: { files: "./src/less/installer.less", watch: "./src/less/**/*.less", out: "installer.css" }, nonodes: { files: "./src/less/pages/nonodes.less", watch: "./src/less/**/*.less", out: "nonodes.style.min.css"}, preview: { files: "./src/less/canvas-designer.less", watch: "./src/less/**/*.less", out: "canvasdesigner.css" }, - umbraco: { files: "./src/less/belle.less", watch: "./src/less/**/*.less", out: "umbraco.css" }, + umbraco: { files: "./src/less/belle.less", watch: "./src/**/*.less", out: "umbraco.css" }, rteContent: { files: "./src/less/rte-content.less", watch: "./src/less/**/*.less", out: "rte-content.css" } }, diff --git a/src/Umbraco.Web.UI.Client/gulp/modes.js b/src/Umbraco.Web.UI.Client/gulp/modes.js index dc2947f2cc..21609cdcf8 100644 --- a/src/Umbraco.Web.UI.Client/gulp/modes.js +++ b/src/Umbraco.Web.UI.Client/gulp/modes.js @@ -10,4 +10,14 @@ function setDevelopmentMode(cb) { return cb(); }; -module.exports = { setDevelopmentMode: setDevelopmentMode }; +function setTestMode(cb) { + + config.compile.current = config.compile.test; + + return cb(); +}; + +module.exports = { + setDevelopmentMode: setDevelopmentMode, + setTestMode: setTestMode + }; diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/test.js b/src/Umbraco.Web.UI.Client/gulp/tasks/test.js index 1e8d074f7e..255fe17435 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/test.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/test.js @@ -6,11 +6,24 @@ var karmaServer = require('karma').Server; * Build tests **************************/ - // Karma test +// Karma test function testUnit() { + return new karmaServer({ + configFile: __dirname + "/../../test/config/karma.conf.js" + }) + .start(); +}; + +// Run karma test server +function runUnitTestServer() { + return new karmaServer({ configFile: __dirname + "/../../test/config/karma.conf.js", + autoWatch: true, + port: 9999, + singleRun: false, + browsers: ['ChromeDebugging'], keepalive: true }) .start(); @@ -24,4 +37,4 @@ function testE2e() { .start(); }; -module.exports = { testUnit: testUnit, testE2e: testE2e }; +module.exports = { testUnit: testUnit, testE2e: testE2e, runUnitTestServer: runUnitTestServer }; diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js index 24a6e65540..810146d7c8 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js @@ -1,7 +1,7 @@ 'use strict'; const config = require('../config'); -const {watch, parallel, dest, src} = require('gulp'); +const {watch, series, parallel, dest, src} = require('gulp'); var _ = require('lodash'); var MergeStream = require('merge-stream'); @@ -9,9 +9,7 @@ var MergeStream = require('merge-stream'); var processJs = require('../util/processJs'); var processLess = require('../util/processLess'); -//const { less } = require('./less'); -//const { views } = require('./views'); - +var {js} = require('./js'); function watchTask(cb) { @@ -35,10 +33,17 @@ function watchTask(cb) { var viewWatcher; _.forEach(config.sources.views, function (group) { if(group.watch !== false) { - viewWatcher = watch(group.files, { ignoreInitial: true, interval: watchInterval }); - viewWatcher.on('change', function(path, stats) { + viewWatcher = watch(group.files, { ignoreInitial: true, interval: watchInterval }, function() { + console.log("copying " + group.files + " to " + config.root + config.targets.views + group.folder); - src(group.files).pipe( dest(config.root + config.targets.views + group.folder) ); + + return parallel( + function MoveViewsAndRegenerateJS() { + return src(group.files).pipe( dest(config.root + config.targets.views + group.folder) ); + }, + js + )(); + }); } }); diff --git a/src/Umbraco.Web.UI.Client/gulp/util/processJs.js b/src/Umbraco.Web.UI.Client/gulp/util/processJs.js index e3e393b661..67dd6dd420 100644 --- a/src/Umbraco.Web.UI.Client/gulp/util/processJs.js +++ b/src/Umbraco.Web.UI.Client/gulp/util/processJs.js @@ -25,7 +25,9 @@ module.exports = function (files, out) { .pipe(sort()); //in production, embed the templates - task = task.pipe(embedTemplates({ basePath: "./src/", minimize: { loose: true } })) + if(config.compile.current.embedtemplates === true) { + task = task.pipe(embedTemplates({ basePath: "./src/", minimize: { loose: true } })); + } task = task.pipe(concat(out)) .pipe(wrap('(function(){\n%= body %\n})();')) diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js index 705c54bf04..542d45c479 100644 --- a/src/Umbraco.Web.UI.Client/gulpfile.js +++ b/src/Umbraco.Web.UI.Client/gulpfile.js @@ -13,11 +13,11 @@ const { src, dest, series, parallel, lastRun } = require('gulp'); const config = require('./gulp/config'); -const { setDevelopmentMode } = require('./gulp/modes'); +const { setDevelopmentMode, setTestMode } = require('./gulp/modes'); const { dependencies } = require('./gulp/tasks/dependencies'); const { js } = require('./gulp/tasks/js'); const { less } = require('./gulp/tasks/less'); -const { testE2e, testUnit } = require('./gulp/tasks/test'); +const { testE2e, testUnit, runUnitTestServer } = require('./gulp/tasks/test'); const { views } = require('./gulp/tasks/views'); const { watchTask } = require('./gulp/tasks/watchTask'); @@ -31,6 +31,6 @@ exports.build = series(parallel(dependencies, js, less, views), testUnit); exports.dev = series(setDevelopmentMode, parallel(dependencies, js, less, views), watchTask); exports.watch = series(watchTask); // -exports.runTests = series(js, testUnit); -exports.testUnit = series(testUnit); -exports.testE2e = series(testE2e); +exports.runTests = series(setTestMode, parallel(js, testUnit)); +exports.runUnit = series(setTestMode, parallel(js, runUnitTestServer), watchTask); +exports.testE2e = series(setTestMode, parallel(testE2e)); From 48ad161d512c0f7f017437fc0ada711afe817538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 29 May 2020 11:31:10 +0200 Subject: [PATCH 154/508] able to re run watch --- src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js index 810146d7c8..f8e2570ff9 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js @@ -33,18 +33,14 @@ function watchTask(cb) { var viewWatcher; _.forEach(config.sources.views, function (group) { if(group.watch !== false) { - viewWatcher = watch(group.files, { ignoreInitial: true, interval: watchInterval }, function() { - - console.log("copying " + group.files + " to " + config.root + config.targets.views + group.folder); - - return parallel( + viewWatcher = watch(group.files, { ignoreInitial: true, interval: watchInterval }, + parallel( function MoveViewsAndRegenerateJS() { return src(group.files).pipe( dest(config.root + config.targets.views + group.folder) ); }, js - )(); - - }); + ) + ); } }); From 1fe7de0b5f78d3d2d7843a02f520fe65b21a2952 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Fri, 29 May 2020 15:18:46 +0000 Subject: [PATCH 155/508] Merge pull request #7166 from umbraco/v8/feature/7133-do-not-paste-keys-in-nested-content Cherry picked from SHA 49c438b55f002b02e4555bb2622c73bf1ca51239 NC keys: Do not copy keys for Nested Content (Fix for #7133) # Conflicts: # src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js --- .../src/common/services/clipboard.service.js | 64 +++++++-- .../nestedcontent/nestedcontent.controller.js | 76 ++++++++++- .../test/config/app.unit.js | 4 +- .../Compose/NestedContentPropertyComponent.cs | 126 ++++++++++++++++++ .../Compose/NestedContentPropertyComposer.cs | 9 ++ src/Umbraco.Web/Umbraco.Web.csproj | 2 + 6 files changed, 262 insertions(+), 19 deletions(-) create mode 100644 src/Umbraco.Web/Compose/NestedContentPropertyComponent.cs create mode 100644 src/Umbraco.Web/Compose/NestedContentPropertyComposer.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js b/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js index c3a1ba6432..083b4e86b7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js @@ -12,6 +12,9 @@ */ function clipboardService(notificationsService, eventsService, localStorageService, iconHelper) { + + var clearPropertyResolvers = []; + var STORAGE_KEY = "umbClipboardService"; @@ -53,13 +56,32 @@ function clipboardService(notificationsService, eventsService, localStorageServi return false; } - var prepareEntryForStorage = function(entryData) { - var shallowCloneData = Object.assign({}, entryData);// Notice only a shallow copy, since we dont need to deep copy. (that will happen when storing the data) - delete shallowCloneData.key; - delete shallowCloneData.$$hashKey; - - return shallowCloneData; + function clearPropertyForStorage(prop) { + + for (var i=0; i prepareEntryForStorage(data)); + var copiedDatas = datas.map(data => prepareEntryForStorage(data, firstLevelClearupMethod)); // remove previous copies of this entry: storage.entries = storage.entries.filter( diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js index 19547c38e4..5ebb34bb7a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js @@ -1,6 +1,58 @@ (function () { 'use strict'; + /** + * When performing a copy, we do copy the ElementType Data Model, but each inner Nested Content property is still stored as the Nested Content Model, aka. each property is just storing its value. To handle this we need to ensure we handle both scenarios. + */ + + + angular.module('umbraco').run(['clipboardService', function (clipboardService) { + + function clearNestedContentPropertiesForStorage(prop, propClearingMethod) { + + // if prop.editor is "Umbraco.NestedContent" + if ((typeof prop === 'object' && prop.editor === "Umbraco.NestedContent")) { + + var value = prop.value; + for (var i = 0; i < value.length; i++) { + var obj = value[i]; + + // remove the key + delete obj.key; + + // Loop through all inner properties: + for (var k in obj) { + propClearingMethod(obj[k]); + } + } + } + } + + clipboardService.registrerClearPropertyResolver(clearNestedContentPropertiesForStorage) + + + function clearInnerNestedContentPropertiesForStorage(prop, propClearingMethod) { + + // if we got an array, and it has a entry with ncContentTypeAlias this meants that we are dealing with a NestedContent property inside a NestedContent property. + if ((Array.isArray(prop) && prop.length > 0 && prop[0].ncContentTypeAlias !== undefined)) { + + for (var i = 0; i < prop.length; i++) { + var obj = prop[i]; + + // remove the key + delete obj.key; + + // Loop through all inner properties: + for (var k in obj) { + propClearingMethod(obj[k]); + } + } + } + } + + clipboardService.registrerClearPropertyResolver(clearInnerNestedContentPropertiesForStorage) + }]); + angular .module('umbraco') .component('nestedContentPropertyEditor', { @@ -13,7 +65,7 @@ } }); - function NestedContentController($scope, $interpolate, $filter, $timeout, contentResource, localizationService, iconHelper, clipboardService, eventsService, overlayService, $routeParams, editorState) { + function NestedContentController($scope, $interpolate, $filter, $timeout, contentResource, localizationService, iconHelper, clipboardService, eventsService, overlayService) { var vm = this; var model = $scope.$parent.$parent.model; @@ -76,7 +128,7 @@ } localizationService.localize("clipboard_labelForArrayOfItemsFrom", [model.label, nodeName]).then(function(data) { - clipboardService.copyArray("elementTypeArray", aliases, vm.nodes, data, "icon-thumbnail-list", model.id); + clipboardService.copyArray("elementTypeArray", aliases, vm.nodes, data, "icon-thumbnail-list", model.id, clearNodeForCopy); }); } @@ -208,7 +260,8 @@ }); }); - vm.overlayMenu.title = vm.overlayMenu.pasteItems.length > 0 ? labels.grid_addElement : labels.content_createEmpty; + vm.overlayMenu.title = labels.grid_addElement; + vm.overlayMenu.hideHeader = vm.overlayMenu.pasteItems.length > 0; vm.overlayMenu.clickClearPaste = function ($event) { $event.stopPropagation(); @@ -216,6 +269,7 @@ clipboardService.clearEntriesOfType("elementType", contentTypeAliases); clipboardService.clearEntriesOfType("elementTypeArray", contentTypeAliases); vm.overlayMenu.pasteItems = [];// This dialog is not connected via the clipboardService events, so we need to update manually. + vm.overlayMenu.hideHeader = false; }; if (vm.overlayMenu.availableItems.length === 1 && vm.overlayMenu.pasteItems.length === 0) { @@ -383,6 +437,11 @@ }); } + function clearNodeForCopy(clonedData) { + delete clonedData.key; + delete clonedData.$$hashKey; + } + vm.showCopy = clipboardService.isSupported(); vm.showPaste = false; @@ -390,7 +449,7 @@ syncCurrentNode(); - clipboardService.copy("elementType", node.contentTypeAlias, node); + clipboardService.copy("elementType", node.contentTypeAlias, node, null, null, null, clearNodeForCopy); $event.stopPropagation(); } @@ -488,10 +547,12 @@ } // Enforce min items if we only have one scaffold type + var modelWasChanged = false; if (vm.nodes.length < vm.minItems && vm.scaffolds.length === 1) { for (var i = vm.nodes.length; i < model.config.minItems; i++) { addNode(vm.scaffolds[0].contentTypeAlias); } + modelWasChanged = true; } // If there is only one item, set it as current node @@ -503,6 +564,9 @@ vm.inited = true; + if (modelWasChanged) { + updateModel(); + } updatePropertyActionStates(); checkAbilityToPasteContent(); } @@ -585,8 +649,8 @@ } function updatePropertyActionStates() { - copyAllEntriesAction.isDisabled = !model.value || model.value.length === 0; - removeAllEntriesAction.isDisabled = !model.value || model.value.length === 0; + copyAllEntriesAction.isDisabled = !model.value || !model.value.length; + removeAllEntriesAction.isDisabled = copyAllEntriesAction.isDisabled; } diff --git a/src/Umbraco.Web.UI.Client/test/config/app.unit.js b/src/Umbraco.Web.UI.Client/test/config/app.unit.js index 1f49d237e6..9e265215dd 100644 --- a/src/Umbraco.Web.UI.Client/test/config/app.unit.js +++ b/src/Umbraco.Web.UI.Client/test/config/app.unit.js @@ -13,8 +13,8 @@ var app = angular.module('umbraco', [ 'ngSanitize', //'ngMessages', - 'tmh.dynamicLocale' + 'tmh.dynamicLocale', //'ngFileUpload', - //'LocalStorageModule', + 'LocalStorageModule' //'chart.js' ]); diff --git a/src/Umbraco.Web/Compose/NestedContentPropertyComponent.cs b/src/Umbraco.Web/Compose/NestedContentPropertyComponent.cs new file mode 100644 index 0000000000..5794a2734e --- /dev/null +++ b/src/Umbraco.Web/Compose/NestedContentPropertyComponent.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json.Linq; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Events; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; +using Umbraco.Web.PropertyEditors; + +namespace Umbraco.Web.Compose +{ + public class NestedContentPropertyComponent : IComponent + { + public void Initialize() + { + ContentService.Copying += ContentService_Copying; + ContentService.Saving += ContentService_Saving; + } + + private void ContentService_Copying(IContentService sender, CopyEventArgs e) + { + // When a content node contains nested content property + // Check if the copied node contains a nested content + var nestedContentProps = e.Copy.Properties.Where(x => x.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.NestedContent); + UpdateNestedContentProperties(nestedContentProps, false); + } + + private void ContentService_Saving(IContentService sender, ContentSavingEventArgs e) + { + // One or more content nodes could be saved in a bulk publish + foreach (var entity in e.SavedEntities) + { + // When a content node contains nested content property + // Check if the copied node contains a nested content + var nestedContentProps = entity.Properties.Where(x => x.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.NestedContent); + UpdateNestedContentProperties(nestedContentProps, true); + } + } + + public void Terminate() + { + ContentService.Copying -= ContentService_Copying; + ContentService.Saving -= ContentService_Saving; + } + + private void UpdateNestedContentProperties(IEnumerable nestedContentProps, bool onlyMissingKeys) + { + // Each NC Property on a doctype + foreach (var nestedContentProp in nestedContentProps) + { + // A NC Prop may have one or more values due to cultures + var propVals = nestedContentProp.Values; + foreach (var cultureVal in propVals) + { + // Remove keys from published value & any nested NC's + var updatedPublishedVal = CreateNestedContentKeys(cultureVal.PublishedValue?.ToString(), onlyMissingKeys); + cultureVal.PublishedValue = updatedPublishedVal; + + // Remove keys from edited/draft value & any nested NC's + var updatedEditedVal = CreateNestedContentKeys(cultureVal.EditedValue?.ToString(), onlyMissingKeys); + cultureVal.EditedValue = updatedEditedVal; + } + } + } + + + // internal for tests + internal string CreateNestedContentKeys(string rawJson, bool onlyMissingKeys, Func createGuid = null) + { + // used so we can test nicely + if (createGuid == null) + createGuid = () => Guid.NewGuid(); + + if (string.IsNullOrWhiteSpace(rawJson) || !rawJson.DetectIsJson()) + return rawJson; + + // Parse JSON + var complexEditorValue = JToken.Parse(rawJson); + + UpdateNestedContentKeysRecursively(complexEditorValue, onlyMissingKeys, createGuid); + + return complexEditorValue.ToString(); + } + + private void UpdateNestedContentKeysRecursively(JToken json, bool onlyMissingKeys, Func createGuid) + { + // check if this is NC + var isNestedContent = json.SelectTokens($"$..['{NestedContentPropertyEditor.ContentTypeAliasPropertyKey}']", false).Any(); + + // select all values (flatten) + var allProperties = json.SelectTokens("$..*").OfType().Select(x => x.Parent as JProperty).WhereNotNull().ToList(); + foreach (var prop in allProperties) + { + if (prop.Name == NestedContentPropertyEditor.ContentTypeAliasPropertyKey) + { + // get it's sibling 'key' property + var ncKeyVal = prop.Parent["key"] as JValue; + // TODO: This bool seems odd, if the key is null, shouldn't we fill it in regardless of onlyMissingKeys? + if ((onlyMissingKeys && ncKeyVal == null) || (!onlyMissingKeys && ncKeyVal != null)) + { + // create or replace + prop.Parent["key"] = createGuid().ToString(); + } + } + else if (!isNestedContent || prop.Name != "key") + { + // this is an arbitrary property that could contain a nested complex editor + var propVal = prop.Value?.ToString(); + // check if this might contain a nested NC + if (!propVal.IsNullOrWhiteSpace() && propVal.DetectIsJson() && propVal.InvariantContains(NestedContentPropertyEditor.ContentTypeAliasPropertyKey)) + { + // recurse + var parsed = JToken.Parse(propVal); + UpdateNestedContentKeysRecursively(parsed, onlyMissingKeys, createGuid); + // set the value to the updated one + prop.Value = parsed.ToString(); + } + } + } + } + + } +} diff --git a/src/Umbraco.Web/Compose/NestedContentPropertyComposer.cs b/src/Umbraco.Web/Compose/NestedContentPropertyComposer.cs new file mode 100644 index 0000000000..4c9d9dee1c --- /dev/null +++ b/src/Umbraco.Web/Compose/NestedContentPropertyComposer.cs @@ -0,0 +1,9 @@ +using Umbraco.Core; +using Umbraco.Core.Composing; + +namespace Umbraco.Web.Compose +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public class NestedContentPropertyComposer : ComponentComposer, ICoreComposer + { } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 5173972f42..ed457b1364 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -129,6 +129,7 @@ + @@ -233,6 +234,7 @@ + From 31c9afac7614c963d9f0d5839a8f56c32cf48401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 2 Jun 2020 09:38:00 +0200 Subject: [PATCH 156/508] make js up to date --- .../src/common/services/blockeditor.service.js | 18 +++++++++--------- .../blocklist/blocklist.component.js | 4 ++-- .../blocklist.blockconfiguration.controller.js | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js index eaf2fc79fc..8adf90af74 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js @@ -284,13 +284,14 @@ var blockModel = {}; blockModel.key = String.CreateGuid().replace(/-/g, ""); - blockModel.config = angular.copy(blockConfiguration); + blockModel.config = Utilities.copy(blockConfiguration); if (blockModel.config.label && blockModel.config.label !== "") { blockModel.labelInterpolator = $interpolate(blockModel.config.label); } - blockModel.updateLabel = _.debounce(function() { + blockModel.__scope = this.isolatedScope; + blockModel.updateLabel = _.debounce(function () {this.__scope.$evalAsync(function() { this.label = getBlockLabel(this); - }, 100); + }.bind(this))}.bind(blockModel), 10); var contentScaffold = this.getScaffoldFor(blockConfiguration.contentTypeAlias); if(contentScaffold === null) { @@ -298,7 +299,7 @@ } // make basics from scaffold - blockModel.content = angular.copy(contentScaffold); + blockModel.content = Utilities.copy(contentScaffold); blockModel.content.udi = udi; mapToElementModel(blockModel.content, dataModel); @@ -314,7 +315,7 @@ } // make basics from scaffold - blockModel.settings = angular.copy(settingsScaffold); + blockModel.settings = Utilities.copy(settingsScaffold); layoutEntry.settings = layoutEntry.settings || { key: String.CreateGuid(), contentTypeAlias: blockConfiguration.settingsElementTypeAlias }; if (!layoutEntry.settings.key) { layoutEntry.settings.key = String.CreateGuid(); } if (!layoutEntry.settings.contentTypeAlias) { layoutEntry.settings.contentTypeAlias = blockConfiguration.settingsElementTypeAlias; } @@ -340,9 +341,7 @@ destroyBlockModel: function(blockModel) { // remove property value watchers: - for (const w of blockModel.watchers) { - w(); - } + blockModel.watchers.forEach(w => { w(); }); // remove model from isolatedScope. delete this.isolatedScope.blockModels[blockModel.key]; @@ -405,7 +404,7 @@ */ createFromElementType: function(elementTypeDataModel) { - elementTypeDataModel = angular.copy(elementTypeDataModel); + elementTypeDataModel = Utilities.copy(elementTypeDataModel); var contentTypeAlias = elementTypeDataModel.contentTypeAlias; @@ -457,6 +456,7 @@ delete this.blockConfigurations; delete this.scaffolds; delete this.watchers; + this.isolatedScope.$destroy(); delete this.isolatedScope; } } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index a2218ceb82..68302f2f31 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -203,11 +203,11 @@ function editBlock(blockModel, openSettings) { // make a clone to avoid editing model directly. - var blockContentClone = angular.copy(blockModel.content); + var blockContentClone = Utilities.copy(blockModel.content); var blockSettingsClone = null; if (blockModel.config.settingsElementTypeAlias) { - blockSettingsClone = angular.copy(blockModel.settings); + blockSettingsClone = Utilities.copy(blockModel.settings); } var hideContent = (openSettings === true && inlineEditing === true); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js index d24f266dca..4d41336251 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js @@ -162,7 +162,7 @@ localizationService.localize("blockEditor_blockConfigurationOverlayTitle", [vm.getElementTypeByAlias(block.contentTypeAlias).name]).then(function (data) { - var clonedBlockData = angular.copy(block); + var clonedBlockData = Utilities.copy(block); vm.openBlock = block; var overlayModel = { From f157fe56f1026822c4253ad35fdf011447551d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 2 Jun 2020 10:04:39 +0200 Subject: [PATCH 157/508] fix white background of image-picker --- src/Umbraco.Web.UI.Client/src/less/property-editors.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index b5870b8dce..83bad8cacb 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -320,6 +320,11 @@ box-shadow:none !important; } +.umb-sortable-thumbnails-container { + display: flex; + flex-wrap: wrap; + background-color: @white; +} .umb-sortable-thumbnails { list-style-type: none; From 61c0e42e5758313c9afe6cbd7af771fdcaf418f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 2 Jun 2020 10:05:00 +0200 Subject: [PATCH 158/508] media-picker container class --- .../src/views/propertyeditors/mediapicker/mediapicker.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html index c4dba4d373..59e6fcd21c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html @@ -3,7 +3,7 @@

    -
    +