From d580153c9ab9a40abacd23dc4be211395db437d3 Mon Sep 17 00:00:00 2001 From: elitsa Date: Wed, 15 May 2019 13:51:07 +0200 Subject: [PATCH 001/908] Removing build-docs.ps1 script since it is integrated now in the build.ps1 --- build/build-docs.ps1 | 44 -------------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 build/build-docs.ps1 diff --git a/build/build-docs.ps1 b/build/build-docs.ps1 deleted file mode 100644 index 8cd3f090c7fc..000000000000 --- a/build/build-docs.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -$uenv=build/build.ps1 -get - -$src = "$($uenv.SolutionRoot)\src" -$tmp = $uenv.BuildTemp -$out = $uenv.BuildOutput -$DocFxJson = "$src\ApiDocs\docfx.json" -$DocFxSiteOutput = "$tmp\_site\*.*" - -################ Do the UI docs -$uenv.CompileBelle() - -"Moving to Umbraco.Web.UI.Client folder" -cd .\src\Umbraco.Web.UI.Client - -"Generating the docs and waiting before executing the next commands" -& gulp docs | Out-Null - -# change baseUrl -$BaseUrl = "https://our.umbraco.com/apidocs/v8/ui/" -$IndexPath = "./docs/api/index.html" -(Get-Content $IndexPath).replace('location.href.replace(rUrl, indexFile)', "`'" + $BaseUrl + "`'") | Set-Content $IndexPath - -# zip it -& $uenv.BuildEnv.Zip a -tzip -r "$out\ui-docs.zip" "$src\Umbraco.Web.UI.Client\docs\api\*.*" - - -################ Do the c# docs - -# Build the solution in debug mode -$SolutionPath = Join-Path -Path $src -ChildPath "umbraco.sln" -#$uenv.CompileUmbraco() - -#restore nuget packages -$uenv.RestoreNuGet() - -# run DocFx -$DocFx = $uenv.BuildEnv.DocFx - -Write-Host "$DocFxJson" -& $DocFx metadata $DocFxJson -& $DocFx build $DocFxJson - -# zip it -& $uenv.BuildEnv.Zip a -tzip -r "$out\csharp-docs.zip" $DocFxSiteOutput From 1a54cdb58364355a1e7746c9810891396101e308 Mon Sep 17 00:00:00 2001 From: elitsa Date: Wed, 15 May 2019 13:54:35 +0200 Subject: [PATCH 002/908] Functions for the API docs are refactored --- build/build.ps1 | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/build/build.ps1 b/build/build.ps1 index 55b686c98edb..978d6b2c26c0 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -430,19 +430,16 @@ $this.CopyFile("$($this.SolutionRoot)\build\Azure\azuregalleryrelease.ps1", $this.BuildOutput) }) - $ubuild.DefineMethod("PrepareCSharpDocs", + $ubuild.DefineMethod("BuildCSharpDocs", { - Write-Host "Prepare C# Documentation" + Write-Host "Building C# Documentation" $src = "$($this.SolutionRoot)\src" - $tmp = $this.BuildTemp - $out = $this.BuildOutput + $tmp = $this.BuildTemp + $out = $this.BuildOutput $DocFxJson = Join-Path -Path $src "\ApiDocs\docfx.json" $DocFxSiteOutput = Join-Path -Path $tmp "\_site\*.*" - - #restore nuget packages - $this.RestoreNuGet() # run DocFx $DocFx = $this.BuildEnv.DocFx @@ -453,24 +450,26 @@ & $this.BuildEnv.Zip a -tzip -r "$out\csharp-docs.zip" $DocFxSiteOutput }) - $ubuild.DefineMethod("PrepareAngularDocs", + $ubuild.DefineMethod("BuildAngularDocs", { - Write-Host "Prepare Angular Documentation" + Write-Host "Building Angular Documentation" $src = "$($this.SolutionRoot)\src" - $out = $this.BuildOutput + $out = $this.BuildOutput - $this.CompileBelle() + # Check if the solution has been built + if (!(Test-Path "$src\Umbraco.Web.UI.Client\node_modules")) {throw "Umbraco needs to be built before generating the Angular Docs"} - "Moving to Umbraco.Web.UI.Client folder" - cd .\src\Umbraco.Web.UI.Client + Push-Location "$src\Umbraco.Web.UI.Client" "Generating the docs and waiting before executing the next commands" & gulp docs | Out-Null + Pop-Location + # change baseUrl $BaseUrl = "https://our.umbraco.com/apidocs/v8/ui/" - $IndexPath = "./docs/api/index.html" + $IndexPath = "$src\Umbraco.Web.UI.Client\docs\api\index.html" (Get-Content $IndexPath).replace('location.href.replace(rUrl, indexFile)', "`'" + $BaseUrl + "`'") | Set-Content $IndexPath # zip it From c3937d6e622ce38a66c616d1dc85d8a1dfa08f3b Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Fri, 10 Jan 2020 12:58:58 +0100 Subject: [PATCH 003/908] Disable the actions menu button until there are actions available --- .../src/less/components/buttons/umb-button.less | 5 +++++ src/Umbraco.Web.UI.Client/src/less/components/editor.less | 5 +++++ .../src/views/components/editor/umb-editor-menu.html | 1 + 3 files changed, 11 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less index 4127c2201cab..d523b24141a2 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less @@ -31,6 +31,11 @@ margin-left: 5px; } +.umb-button__button[disabled] .umb-button__caret { + border-top-color: @gray-7; + border-bottom-color: @gray-7; +} + .umb-button__progress { position: absolute; left: 50%; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor.less index bc84b0d35e20..5713e8cc4d47 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor.less @@ -73,6 +73,11 @@ height: @editorHeaderHeight; } +.umb-editor-header .umb-button__button[disabled] { + // do not dim down the background color of disabled buttons in the header + background-color: unset; +} + .umb-editor-header__back { background: transparent; border: 0; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-menu.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-menu.html index fe90fef07af2..a7efaa505a49 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-menu.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-menu.html @@ -8,6 +8,7 @@ show-caret="true" has-popup="true" is-expanded="dropdown.isOpen" + disabled="!actions || !actions.length" > 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 004/908] 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 59e8bf6c05f1..a807d63f5feb 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 005/908] 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 eb2b3525a7fb..dcd7eb9d0546 100644 --- a/src/Umbraco.Core/Constants-PropertyEditors.cs +++ b/src/Umbraco.Core/Constants-PropertyEditors.cs @@ -36,6 +36,11 @@ public static class Aliases /// 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 000000000000..da44151ec908 --- /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 006/908] 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 d8dbcc101253..dda5c51175bb 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 007/908] 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 24a6e6554022..42f25ccb23cd 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 008/908] 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 93870f8a5626..3601cb965200 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 009/908] 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 5d29e53d4a77..997b13ce8dc3 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 010/908] 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 42f25ccb23cd..3c90003e3086 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 011/908] 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 3c90003e3086..a94314abd641 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 012/908] 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 b5e032f9fbe4..78a10da78dfb 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 000000000000..c77f19ba67fa --- /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 000000000000..99b1bb53f208 --- /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 000000000000..f0678c76e981 --- /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 000000000000..610773528bae --- /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 000000000000..761fc57e0320 --- /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 000000000000..405c8634b47e --- /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 000000000000..492025274ac1 --- /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 000000000000..d42141829714 --- /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 000000000000..71aab124e630 --- /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 000000000000..e38858c8b789 --- /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 000000000000..6283bd102650 --- /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 000000000000..a26dda420245 --- /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 000000000000..198dab4f5fb8 --- /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 013/908] 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 6283bd102650..5fc17fcafd1d 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 014/908] 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 000000000000..7d00623ccf59 --- /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 000000000000..444bb7249fec --- /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 000000000000..5331ca3891da --- /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 000000000000..eeb5a73e2c39 --- /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 a8e3fc298899..8bf0b9105fb3 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 015/908] 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 eb2b3525a7fb..753cd72116a9 100644 --- a/src/Umbraco.Core/Constants-PropertyEditors.cs +++ b/src/Umbraco.Core/Constants-PropertyEditors.cs @@ -36,6 +36,11 @@ public static class Aliases /// 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 7d00623ccf59..307b69f6e027 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 444bb7249fec..163b564c9fab 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 @@ public BlockListLayoutReference(Udi udi, IPublishedElement settings) 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 5331ca3891da..36dd8af7c180 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 000000000000..16c617662409 --- /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 8bf0b9105fb3..33796a93bf80 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 000000000000..f345bf136c5d --- /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 adfb9d3b6b85..a102b9f93e44 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -119,10 +119,12 @@ public class NestedContentTests .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 ff923bb04b89..2d980461d245 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 000000000000..b9ee1b84fb60 --- /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 000000000000..95366db48614 --- /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 000000000000..f0148f7a9ad6 --- /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 0f5320746269..89190883c83b 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/NestedContentConfiguration.cs @@ -3,6 +3,7 @@ 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 000000000000..0ab9b8657288 --- /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 000000000000..dceaa9995e04 --- /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 4a25049695e9..b96104885179 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs @@ -23,8 +23,8 @@ public class NestedContentManyValueConverter : NestedContentValueConverterBase /// /// 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 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub 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 c9c99615f6ef..b3a2a9294dd4 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs @@ -22,8 +22,8 @@ public class NestedContentSingleValueConverter : NestedContentValueConverterBase /// /// 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 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub 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 7c18d8ebca2f..4295daf5fe88 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 @@ public static bool IsNestedMany(IPublishedPropertyType publishedProperty) 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 0111a369933c..e660108cade2 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 016/908] 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 | 29 ++- 5 files changed, 217 insertions(+), 38 deletions(-) diff --git a/src/Umbraco.Core/Contants-UdiEntityType.cs b/src/Umbraco.Core/Contants-UdiEntityType.cs index 75a137bd2e48..1ed862f32856 100644 --- a/src/Umbraco.Core/Contants-UdiEntityType.cs +++ b/src/Umbraco.Core/Contants-UdiEntityType.cs @@ -25,6 +25,7 @@ internal static Dictionary GetTypes() { Unknown, UdiType.Unknown }, { AnyGuid, UdiType.GuidUdi }, + { Element, UdiType.GuidUdi }, { Document, UdiType.GuidUdi }, { DocumentBlueprint, UdiType.GuidUdi }, { Media, UdiType.GuidUdi }, @@ -64,6 +65,8 @@ internal static Dictionary GetTypes() 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 163b564c9fab..a2fd6c9df96b 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs @@ -13,7 +13,7 @@ public class BlockListLayoutReference : IBlockElement 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 3b6ebc610c8a..a584ea2a9c56 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs @@ -30,18 +30,23 @@ public virtual bool HasValue(IPublishedProperty property, string culture, string 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 f345bf136c5d..114642f89d39 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -6,7 +6,9 @@ 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 @@ private BlockListPropertyValueConverter Create() 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 @@ public void Get_Value_Type_Multiple() 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 @@ public void Get_Value_Type_Multiple() [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 @@ public void Get_Value_Type_Single() 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 dceaa9995e04..ab289f048c80 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -53,6 +53,8 @@ public override object ConvertSourceToIntermediate(IPublishedElement owner, IPub /// 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,24 +81,33 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub var blockListLayouts = jsonLayout[Constants.PropertyEditors.Aliases.BlockList] as JArray; if (blockListLayouts == null) return model; - foreach(var blockListLayout in blockListLayouts) + // parse the data elements + foreach (var data in jsonData.Cast()) + { + var element = _blockConverter.ConvertToElement(data, BlockEditorPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview); + if (element == null) continue; + 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 (element == null) continue; - var layoutRef = new BlockListLayoutReference(blockListLayout.Value("udi"), element); + if (!Udi.TryParse(blockListLayout.Value("udi"), out var udi)) + continue; + + var layoutRef = new BlockListLayoutReference(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; } From 9f0becaf522eeb09ceeee271554aff5ce1a948e1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 3 Feb 2020 18:31:18 +1100 Subject: [PATCH 017/908] 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 a2fd6c9df96b..09c6c7647868 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 114642f89d39..ca687f94a633 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -207,6 +207,31 @@ public void Convert_Valid_Empty_Json() 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 @@ public void Convert_Valid_Json() 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 ab289f048c80..3878b8d60533 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -59,12 +59,13 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub { 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 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub { 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 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub // 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 1c4121da0cac..79b24175ea01 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -108,6 +108,7 @@ public override void Compose(Composition composition) 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 018/908] 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 09c6c7647868..19b30e6ea630 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs @@ -21,13 +21,13 @@ public BlockListLayoutReference(Udi udi, IPublishedElement data, IPublishedEleme /// 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 @@ public BlockListLayoutReference(Udi udi, IPublishedElement data, IPublishedEleme /// 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 36dd8af7c180..089ca7e6a3a1 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockListModel.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockListModel.cs @@ -16,15 +16,11 @@ public BlockListModel(IEnumerable data, IEnumerable /// 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 019/908] 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 695b456fd639..f264038e93a3 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 000000000000..c7cac47d8553 --- /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 000000000000..78e23b17f312 --- /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 000000000000..dc014c5daf5b --- /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 f0678c76e981..bd8f9558fdf8 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 610773528bae..5ce53aece45a 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 000000000000..09724f66194a --- /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 000000000000..6ba59f0703be --- /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 d42141829714..ebabe86617e4 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 71aab124e630..2df5dcd923b6 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 5fc17fcafd1d..f3f8c2e5fee5 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 a26dda420245..78f4138897d7 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 020/908] 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 78f4138897d7..8708fdf000f7 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 021/908] 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 7459ae848b24..9cd4f39c17e4 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -268,7 +268,7 @@ public void Resolves_Types() 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 022/908] 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 c77f19ba67fa..0b82d2202de3 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 405c8634b47e..c64d42b6ac11 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 023/908] 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 8708fdf000f7..02670e88b0b2 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 024/908] more demo content --- .../blocklist/blocklist.component.js | 275 ++++++++++++++---- 1 file changed, 220 insertions(+), 55 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 f3f8c2e5fee5..35259eaf9f8b 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,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 } ] } @@ -84,17 +124,19 @@ } }, { - alias: "contentTypeAlias", - name: "Text", - icon: "icon-info", + alias: "pageModule", + name: "Inline module", + icon: "icon-document", prototype_paste_data: { elementType: { alias: 'contentTypeAlias', icon: "icon-document", label: "Text" }, - label: "Label", - editor: "views/blockelements/textareablock/textareablock.editor.html", + label: "{{pageTitle | truncate:true:36}}", + labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), + key: 1, + editor: "views/blockelements/inlineblock/inlineblock.editor.html", overlaySize: 'medium', content: { variants: [ @@ -110,8 +152,8 @@ label: "Group 1", properties: [ { - label: "Page Title", - description: "The title of the page", + label: "Image Title", + description: "The title on top of image", view: "textbox", config: {maxChars: 500}, hideLabel: false, @@ -119,12 +161,35 @@ readonly: false, id: 441, dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "", - alias: "pageTitle", + 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 } ] } @@ -134,8 +199,8 @@ }, { alias: "contentTypeAlias", - name: "Image", - icon: "icon-picture", + name: "Text", + icon: "icon-info", prototype_paste_data: { elementType: { alias: 'contentTypeAlias', @@ -143,7 +208,7 @@ label: "Text" }, label: "Label", - editor: "views/blockelements/imageblock/imageblock.editor.html", + editor: "views/blockelements/textareablock/textareablock.editor.html", overlaySize: 'medium', content: { variants: [ @@ -168,7 +233,7 @@ readonly: false, id: 441, dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", - value: "Let's have a chat", + value: "", alias: "pageTitle", editor: "Umbraco.TextBox", isSensitive: false, @@ -177,14 +242,13 @@ } ] } - ], - temp_image: "/umbraco/assets/img/login.jpg" + ] } } }, { alias: "contentTypeAlias", - name: "Inline editing", + name: "Image", icon: "icon-picture", prototype_paste_data: { elementType: { @@ -209,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}, @@ -219,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, @@ -228,7 +315,7 @@ ] } ], - temp_image: "/umbraco/assets/img/demo.png" + temp_image: "/umbraco/assets/img/login.jpg" } } } @@ -247,7 +334,7 @@ label: "{{pageTitle | truncate:true:36}}", labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), key: 1, - editor: "views/blockelements/inlineblock/inlineblock.editor.html", + editor: "views/blockelements/labelblock/labelblock.editor.html", overlaySize: 'medium', content: { variants: [ @@ -278,48 +365,42 @@ 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: 1, - editor: "views/blockelements/labelblock/labelblock.editor.html", - overlaySize: 'medium', - content: { - variants: [ - { - language: { - isDefault: true - } - } - ], - tabs: [ - { - id: 1234, - 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}, hideLabel: false, validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, readonly: false, - id: 441, + id: 442, 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", + value: "Let's have a chat", + alias: "imageDesc", editor: "Umbraco.TextBox", isSensitive: false, culture: 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 025/908] 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 35259eaf9f8b..56a936982b3b 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 026/908] which around actions --- .../blocklist/blocklist.component.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 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 e38858c8b789..8aa40e26d969 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,16 +52,16 @@

No editor

- -
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 027/908] 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 56a936982b3b..1c7843558641 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 028/908] 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 16c617662409..000000000000 --- 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 33796a93bf80..8bf0b9105fb3 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 029/908] 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 19b30e6ea630..85d17fad24ce 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 @@ public BlockListLayoutReference(Udi udi, IPublishedElement data, IPublishedEleme [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 089ca7e6a3a1..153fe6be8a08 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 @@ public BlockListModel(IEnumerable data, 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 ca687f94a633..300c19ac1e60 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -317,12 +317,15 @@ public void Get_Data_From_Layout_Item() 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 3878b8d60533..3d8e8e2b135c 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -108,7 +108,7 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub 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 030/908] 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 f74897d565b0..01044dd7f21f 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -77,6 +77,17 @@ public virtual bool IsProtected(string path) 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 @@ public virtual bool IsProtected(string path) /// 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 @@ private bool HasAccess(string path, RoleProvider roleProvider) 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 @@ private IMember GetCurrentPersistedMember() 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 a9732cb2334868190cd19abecc7a31885591bae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 6 Feb 2020 14:57:25 +0100 Subject: [PATCH 031/908] corrected button style to match new style --- .../src/views/propertyeditors/listview/listview.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index ee1847b430aa..55adb18c6e06 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -21,7 +21,7 @@
- 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 032/908] 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 1c7843558641..287d5aba0883 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 033/908] 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 000000000000..680b75ac787a --- /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 f264038e93a3..9b9d10b9659d 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 715dc12a0716..eec87246c03e 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 @@
Create new
+
  • + +
  • 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 000000000000..13937a0625f4 --- /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 000000000000..d40ec193115d --- /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 000000000000..fb22ea8b466a --- /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 a85df5714b18..a7abf7528121 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 d14fb03727c9..e4fb330296d6 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 95366db48614..ebdc39e4452c 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 000000000000..3a4e3eae9bb7 --- /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 9c4e2f460f73..782122bccd27 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs @@ -22,7 +22,8 @@ public BlockListPropertyEditor(ILogger logger) { } #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 e660108cade2..c8812ad9ec91 --- 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 034/908] 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 a807d63f5feb..92e0b6d21dcc 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 e3e393b66116..67dd6dd42060 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 035/908] 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 ed80359833e3..04992d591f15 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 036/908] 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 fb22ea8b466a..f76e9ee1fd82 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 037/908] clean up of html --- .../prevalue/blocklist.elementtypepicker.html | 126 +++++++++--------- 1 file changed, 61 insertions(+), 65 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 d40ec193115d..830ee4124e05 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 038/908] 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 287d5aba0883..0aa6003dca59 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 039/908] 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 eb8740b3854a..bbd866a5fd28 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 a19cf40e1a76..5735f8462b0b 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 8aa40e26d969..7fcee650fc0b 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 @@

    No editor

    + + + + +
    +
    + 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 5898ee4a7078..344604325ede 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -15,8 +15,8 @@ public class BlockListConfiguration 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 051/908] 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 300c19ac1e60..a8a500d5c0e7 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -47,7 +47,7 @@ private BlockListPropertyValueConverter CreateConverter() private BlockListConfiguration ConfigForMany() => new BlockListConfiguration { - ElementTypes = new[] { + Blocks = new[] { new BlockListConfiguration.ElementType { Alias = "Test1" @@ -61,7 +61,7 @@ private BlockListPropertyValueConverter CreateConverter() private BlockListConfiguration ConfigForSingle() => new BlockListConfiguration { - ElementTypes = new[] { + Blocks = new[] { new BlockListConfiguration.ElementType { Alias = "Test1" @@ -111,7 +111,7 @@ public void Get_Value_Type_Single() 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 344604325ede..6485bd061a7f 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 @@ public class NumberRange 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 3d8e8e2b135c..94d64ee29db2 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -34,7 +34,7 @@ public override bool IsConverter(IPublishedPropertyType propertyType) /// 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 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub 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 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub } } - + } } 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 052/908] 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 000000000000..8846dc7d3afa --- /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 000000000000..b7d0cd05e8c8 --- /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 ff555fc77378..9561d28a9ed0 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 bd8f9558fdf8..419a1fd8a951 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 caad1c83a744..868573fb7246 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 34cfed2ad9d2..637df93a72c8 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 abfd738861cb..7aa03f9402e5 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,43 +858,60 @@ ]; + */ - - function setDirty() { - if (vm.propertyForm) { - vm.propertyForm.$setDirty(); - } - }; + model.value = model.value || {}; - function addNewBlock(index, type) { + var modelObject = blockEditorService.createModelObject(model.value, model.editor, model.config.blocks); - var block = angular.copy(type.prototype_paste_data); + modelObject.loadScaffolds(contentResource).then(loaded); - vm.blocks.splice(index, 0, block); - $scope.moveFocusToBlock = block; + vm.layout = []; + vm.blocks = []; + vm.availableBlockTypes = []; - } + function loaded() { - function getBlockLabel(block) { + console.log("Loading done!!!"); + console.log(modelObject); - // TODO: we should do something about this for performance. + + vm.layout = modelObject.getLayout(); + vm.layout.forEach(entry => { + vm.blocks.push(modelObject.getEditingModel(entry)); + }); - var props = new Object(); + vm.availableBlockTypes = modelObject.getAvailableBlocksForItemPicker(); + console.log(vm.availableBlockTypes); - 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); + function setDirty() { + if (vm.propertyForm) { + vm.propertyForm.$setDirty(); } + }; + + function addNewBlock(index, contentTypeAlias) { + + // Create layout entry. + 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); + + $scope.moveFocusToBlock = blockEditingObject; - return "block.label"; } + + vm.deleteBlock = function(block) { var index = vm.blocks.indexOf(block); if(index !== -1) { @@ -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 970b42d2cbae..77751579c9fa 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 9ca35778be30..8184761a1a31 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 6485bd061a7f..f06980434852 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -29,7 +29,7 @@ public class NumberRange 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 @@ public class BlockConfiguration [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 053/908] 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 8846dc7d3afa..6ed5b27211b9 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 7aa03f9402e5..1cb7a8d2590b 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 054/908] 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 1e8d074f7ece..b5239d35e7a7 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 705c54bf04db..f6964df7c5ee 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 42a89c5d1376..be8174739add 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 c150af79de85..a956b5d803ba 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 4e3a78144b93..8a120b316579 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 055/908] 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 f6964df7c5ee..ec9b7bc50831 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 be8174739add..5bab6a2e224d 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 a956b5d803ba..e7a6cce6a612 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 6e6eb00da706..cf73e6a8cec5 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 000000000000..1ff2d3a0a77d --- /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 8a120b316579..501e43b0433b 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 1e9ea2ea4669..64a984b499b1 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 000000000000..5c48b4a8dcec --- /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 056/908] 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 6ed5b27211b9..a4f7ca9e7bb7 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 057/908] 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 1cb7a8d2590b..5e450eb1331a 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 058/908] 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 5e450eb1331a..eefe339569e8 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 77751579c9fa..1f76afef8ea5 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 8184761a1a31..75a59a0f2d5c 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 f06980434852..f76612db5905 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -35,8 +35,8 @@ public class BlockConfiguration [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 059/908] 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 a4f7ca9e7bb7..2e761fa9e3ba 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 060/908] 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 eefe339569e8..5e450eb1331a 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 1f76afef8ea5..77751579c9fa 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 75a59a0f2d5c..8184761a1a31 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 f76612db5905..f06980434852 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -35,8 +35,8 @@ public class BlockConfiguration [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 061/908] block editor implementation --- .../common/services/blockeditor.service.js | 78 +- .../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 | 1055 +++-------------- .../propertyeditors/blocklist/blocklist.html | 2 +- .../PropertyEditors/BlockListConfiguration.cs | 4 +- 15 files changed, 317 insertions(+), 1007 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 2e761fa9e3ba..bcea8efc5e85 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) { + function mapToEditingModel(editingModel, contentModel) { + + var variant = editingModel.variants[0]; - scaffold.key = contentModel.key; + 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]; + } + } + } + } - var variant = scaffold.variants[0]; + 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 b7d0cd05e8c8..dd289c96a6ab 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 0b82d2202de3..e5e68dde9ca2 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 9561d28a9ed0..31754466ab45 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 dc014c5daf5b..ab6b21d898c8 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 419a1fd8a951..78595fce2933 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 09724f66194a..88a2b306ef23 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 637df93a72c8..347d8f1d7b6e 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 ebabe86617e4..a054e6478402 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 000000000000..4231e5c83e5c --- /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 000000000000..a1b38e509f75 --- /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 5e450eb1331a..5546fdeb8b69 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; - - 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", - 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" - } - } + vm.moveFocusToBlock = null; + + 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 = { + labelKey: "clipboard_labelForCopyAllEntries", + labelTokens: [vm.model.label], + icon: "documents", + method: function () {}, + isDisabled: true } - ]; + + var propertyActions = [ + copyAllEntriesAction + ]; + + if (this.umbProperty) { + this.umbProperty.setPropertyActions(propertyActions); + } + }; - // 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 - } - ] - } - ] - } - } - ]; + + function loaded() { - */ + vm.layout = modelObject.getLayout(); + mapToBlocks(); + vm.availableBlockTypes = modelObject.getAvailableBlocksForItemPicker(); - model.value = model.value || {}; + } - var modelObject = blockEditorService.createModelObject(model.value, model.editor, model.config.blocks); - modelObject.loadScaffolding(contentResource).then(loaded); + function getEditingModel(entry) { + var block = modelObject.getEditingModel(entry); - vm.layout = []; - vm.blocks = []; - vm.availableBlockTypes = []; + if (block === null) return null; - function loaded() { + block.view = block.config.view || vm.model.config.useInlineEditingAsDefault ? "views/blockelements/inlineblock/inlineblock.editor.html" : "views/blockelements/labelblock/labelblock.editor.html"; - console.log("Loading done!!!"); - console.log(modelObject); + return block; + } - - vm.layout = modelObject.getLayout(); + /** + * 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 - } - - - var copyAllEntriesAction = { - labelKey: "clipboard_labelForCopyAllEntries", - labelTokens: [model.label], - icon: "documents", - method: function () {}, - isDisabled: true + vm.blockEditorApi = { + editBlock: editBlock, + requestCopyBlock: requestCopyBlock, + requestDeleteBlock: requestDeleteBlock, + deleteBlock: deleteBlock, + syncBlockData: syncBlockData } - 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); - } - else { - vm.propertyForm.minCount.$setValidity("minCount", true); + 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); + } } - - 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 198dab4f5fb8..eea9f01ff359 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 f06980434852..20b6c00d682a 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -42,8 +42,8 @@ public class BlockConfiguration 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 062/908] 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 bcea8efc5e85..3ac974217111 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 a1b38e509f75..8fe08888a9b6 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 5546fdeb8b69..204988927d3d 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 063/908] 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 | 262 ++++++++++++++---- 10 files changed, 302 insertions(+), 111 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 3ac974217111..00308ae4ff2d 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 e5e68dde9ca2..62cd4384b8e7 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 31754466ab45..56fbe13e2b3c 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 78595fce2933..2036e3ef3abf 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 868573fb7246..9fc213cd4aa4 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 c64d42b6ac11..80aba51a8489 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 4231e5c83e5c..36905978d584 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 8fe08888a9b6..330d8f7b695d 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 204988927d3d..157d31b9cffc 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) { + if (blockEditingObject === null) { + return false; + } - // add layout entry at the decired location in layout. - vm.layout.splice(index, 0, layoutEntry); + // 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; + // 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); + } + + 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 requestDeleteBlock (block) { + 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 064/908] 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 157d31b9cffc..3e04e62b45aa 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 065/908] 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 3e04e62b45aa..fcb2bc926c7c 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 066/908] 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 00308ae4ff2d..675746dca4e7 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 fcb2bc926c7c..821a9362fa1e 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 067/908] 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 675746dca4e7..f8c2c4789ad7 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 068/908] clean up --- .../common/services/blockeditor.service.js | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 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 f8c2c4789ad7..e076930b586f 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,11 +224,31 @@ return entry; }, - getContentByUdi: function(udi) { - return this.value.data.find(entry => entry.udi === udi); + /** + * 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); + + return layoutEntry; + }, + // private - createContent: function(elementTypeAlias) { + _createContent: function(elementTypeAlias) { var content = { contentTypeAlias: elementTypeAlias, udi: udiService.create("element") @@ -237,6 +257,10 @@ 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) { @@ -249,29 +273,6 @@ 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); - - return layoutEntry; - } } 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 069/908] 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 92e0b6d21dcc..a397462fd433 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 dc2947f2cc3b..21609cdcf801 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 b5239d35e7a7..255fe17435d0 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 ec9b7bc50831..542d45c479e6 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 501e43b0433b..f6fc5ca1be9f 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 070/908] 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 000000000000..cd07033f8fe6 --- /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 071/908] 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 cf73e6a8cec5..49a90f273e50 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 1ff2d3a0a77d..9b5dcf5ad605 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 = ""; + }); + }); + + return response; + } - function returnVariantNodebyId(status, data, headers) { + 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 e076930b586f..9d0a695d0259 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 821a9362fa1e..617f92350d7e 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 5c48b4a8dcec..b656d5e56e49 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 072/908] 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 9b5dcf5ad605..3a434bdadc15 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 073/908] 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 b656d5e56e49..6bfc7aea3495 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 074/908] 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 49a90f273e50..fc28567ea3ea 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 330d8f7b695d..c3397e94334f 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 3761b42561e6..2004becba78d 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 617f92350d7e..9904a0132b88 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 02670e88b0b2..2ab2c37d3b53 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 6bfc7aea3495..b51ba1f9ee5b 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 075/908] 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 9d0a695d0259..0f87634aa6e1 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 2004becba78d..05f7fff551a4 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 56fbe13e2b3c..cd7468af8d1f 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 2036e3ef3abf..fecf1ea51e38 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 80aba51a8489..bbcef1643b67 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 c3397e94334f..5945ea9b6906 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 05f7fff551a4..07a07ee52922 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 93773b1598b1..b5c42f5cf664 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 078/908] 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 b51ba1f9ee5b..6abf85c9b6c8 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 079/908] 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 a8a500d5c0e7..7f98ef5f18de 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -48,11 +48,11 @@ private BlockListPropertyValueConverter CreateConverter() 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 @@ private BlockListPropertyValueConverter CreateConverter() 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 080/908] 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 36905978d584..000000000000 --- 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 5945ea9b6906..000000000000 --- 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 081/908] 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 2ab2c37d3b53..df19af462c76 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 082/908] 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 df19af462c76..474657e4174f 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 083/908] 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 c6613e0c6007..8df95bb68443 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 a054e6478402..fea337f31ff7 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 e5c43468af41..b52480c2a71d 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 07a07ee52922..7f002846d7bf 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 7380c9b0c80f..74c61a419a70 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 - }; - vm.tabs.push(vm.contentTab); + if (vm.content && vm.content.variants) { + + 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 51750756a87b..4480648da24a 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 88a2b306ef23..5308173c7217 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 347d8f1d7b6e..5056576ca30f 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 d30b343c4da4..eb8c72c57939 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 000000000000..df69e2e648e0 --- /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 7f002846d7bf..4b54b85fe8d9 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 ab6b21d898c8..30d1e6bd2d69 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 4b54b85fe8d9..4aa3b0f6b165 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 474657e4174f..d470ee97cfaa 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 091/908] 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 8703aeb385d5..a05535d3b476 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 e0e679924b46..14a62ae79021 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 000000000000..17e9f47e19e5 --- /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 000000000000..81b6da1996bf --- /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 000000000000..9bf3ffc2dced --- /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 779e97b0359f..4c74c45d888a 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 1dbfd85cd53c..a09088efa198 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 348880965e34..89fbceaea872 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 092/908] 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 17e9f47e19e5..000000000000 --- 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 81b6da1996bf..a9d86de217a1 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 093/908] 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 5a83a8023465..028a10d434ea 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 30d1e6bd2d69..7de7707896c2 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 + .umb-group-panel { + margin-top: 20px; } - .umb-group-panel__header { - display:none; + &.--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 094/908] 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 a9d86de217a1..a2fbb0e907ef 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 9bf3ffc2dced..5eaec3f67b99 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 095/908] 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 15f85e1c77b2..76c0c55fca86 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 096/908] 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 ca57679f51fc..c7a7530181ba 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 097/908] 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 7de7707896c2..a1e5b54c3510 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 098/908] 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 a1e5b54c3510..2702ca163a08 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 000000000000..1a0b4f6241da --- /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 4aa3b0f6b165..fc957ce00330 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 d470ee97cfaa..4194b5d89e9b 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 099/908] 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 4194b5d89e9b..d08c862f889e 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 100/908] 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 0714c4b63245..076b4f205ca0 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 @@ private bool UpdatePropertyDataDto(PropertyDataDto dto, BlockListConfiguration c 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 000000000000..32f8431e6504 --- /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 eeb5a73e2c39..38b4e96aaec7 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 148aa0052d22..73bd8a11261f 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 b9ee1b84fb60..f8105c565108 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 782122bccd27..3f8288b0aca9 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 @@ public BlockListPropertyEditor(ILogger logger) #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 101/908] 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 85d17fad24ce..19b30e6ea630 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 @@ public BlockListLayoutReference(Udi udi, IPublishedElement settings) [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 153fe6be8a08..089ca7e6a3a1 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 @@ public BlockListModel(IEnumerable data, 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 7f98ef5f18de..f63485f4bfb2 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -317,15 +317,12 @@ public void Get_Data_From_Layout_Item() 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 1e013e851c70..d4e130cc0da7 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -114,7 +114,7 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub 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 102/908] 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 a9f66d74198d..d8a304826f15 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 103/908] 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 f63485f4bfb2..6a7ec33a5a10 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -24,11 +24,15 @@ public class BlockListPropertyValueConverterTests /// 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 @@ public void Convert_Valid_Json() }, data: [ { - 'contentTypeAlias': 'home', - 'key': '1304E1DD-AC87-4396-84FE-8A399231CB3D' + 'contentTypeAlias': 'Test1', + 'udi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D' } ] }"; @@ -265,7 +269,7 @@ public void Convert_Valid_Json() 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 @@ public void Get_Data_From_Layout_Item() }, 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 @@ public void Get_Data_From_Layout_Item() 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 104/908] 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 d8a304826f15..e8a03a7dcd39 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 105/908] 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 000000000000..8ebf7ec8c45c --- /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 a1977cec5592..32db64b16b48 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 99b1bb53f208..2ea03bd7031d 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 9fc213cd4aa4..1b074a0cb68a 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 bbcef1643b67..ca49a3f5b8a3 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 492025274ac1..fe11d0cd0c3a 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 c7ac475d4606..2b25a8b01335 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 106/908] 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 f0fcdc3d52ca..f65a60197f26 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -194,9 +194,6 @@ protected void DefinePlan() 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 73bd8a11261f..18c51fd90549 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 107/908] 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 f65a60197f26..1d30efa573d6 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -194,6 +194,11 @@ protected void DefinePlan() 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 000000000000..6e959e86d777 --- /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 000000000000..e42453a3fe7b --- /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 076b4f205ca0..43ee85c2ea10 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 @@ public StackedContentToBlockList(IMigrationContext context) : base(context) 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 @@ private List GetDataTypes(string alias) return Database.Fetch(sql); } - private Dictionary GetKnownDocumentTypes() + private Dictionary GetKnownDocumentTypes() { var sql = Sql() .Select(r => r.Select(x => x.NodeDto)) @@ -47,12 +48,40 @@ private Dictionary GetKnownDocumentTypes() .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 @@ private bool Migrate(IEnumerable dataTypesToMigrate, 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 @@ private BlockListConfiguration UpdateConfiguration(DataTypeDto dataType, Diction 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 @@ private void UpdatePropertyData(DataTypeDto dataType, BlockListConfiguration con } - 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 @@ private class SimpleModel [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 @@ public class SimpleLayoutRef } } } + + 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 18c51fd90549..1ae5267b6a33 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 108/908] 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 e8a03a7dcd39..3bc6308b556a 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 109/908] 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 3bc6308b556a..22fd4283de3b 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 110/908] 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 43ee85c2ea10..a3a8bd62c4b7 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 @@ private Dictionary GetKnownDocumentTypes() .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 @@ public void AddDataItem(JObject obj, Dictionary knownDoc 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 @@ public class SimpleLayoutRef 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 111/908] 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 680b75ac787a..a235bcda3e13 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 14a62ae79021..6c021203a5dd 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 b645e3113b9b..c320a31807f8 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 000000000000..c66879e864b7 --- /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 000000000000..98e730321929 --- /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 000000000000..5adbded9bbbc --- /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 000000000000..e4953999fdcf --- /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 d08c862f889e..d118e6356cd4 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 000000000000..a058f465e023 --- /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 000000000000..06e9714b7790 --- /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 000000000000..f4d9caa73b38 --- /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 000000000000..015d25e9fbe4 --- /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 000000000000..255d27cb8d54 --- /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 216856dee9c6..c4b6349747af 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 77751579c9fa..000000000000 --- 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 8184761a1a31..000000000000 --- 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 a09088efa198..409ba0cefed5 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 89fbceaea872..404993a20163 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 000000000000..64dfbbb4ab81 --- /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 20b6c00d682a..584ed5f1f88f 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 @@ public class NumberRange 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 @@ public class BlockConfiguration [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 975f242deec6..c657a52dcc1c 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 112/908] 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 255d27cb8d54..806930ffb2ad 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 c4b6349747af..8d063c77fb2d 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 409ba0cefed5..a1de35c26609 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 404993a20163..cb00bae3d137 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 113/908] 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 98e730321929..3e7728c58363 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 5adbded9bbbc..703c1bca264f 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 114/908] 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 015d25e9fbe4..a752448e2c58 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 806930ffb2ad..ae147d488f73 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 584ed5f1f88f..e0157ca36cc6 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -54,8 +54,11 @@ public class BlockConfiguration 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 115/908] 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 3e7728c58363..8f1cb00c6d60 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 116/908] 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 a05535d3b476..3524982a9011 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 000000000000..5d020104856b --- /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 000000000000..3eb04257514d --- /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 2b25a8b01335..f97ab1e19a9c 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 117/908] 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 fc957ce00330..360201c27dcb 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 f97ab1e19a9c..d175e422411f 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 e0157ca36cc6..fba80bd57de3 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -57,8 +57,8 @@ public class BlockConfiguration [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 118/908] 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 a3a8bd62c4b7..c75998a66d1a 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 @@ private void UpdateDataType(DataTypeDto dataType) 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 @@ public class NumberRange 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 @@ public class BlockConfiguration [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 119/908] 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 d4e130cc0da7..21105b531e54 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -32,13 +32,7 @@ public override bool IsConverter(IPublishedPropertyType propertyType) => 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 120/908] 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 c75998a66d1a..1c27f1c48aa1 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 @@ private BlockListConfiguration UpdateConfiguration(DataTypeDto dataType, Diction { 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 @@ private class SimpleModel 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 121/908] 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 1d30efa573d6..1d4cef45d4de 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -197,7 +197,6 @@ protected void DefinePlan() // 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 6e959e86d777..000000000000 --- 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 e42453a3fe7b..2bc017b64326 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 @@ public override void Migrate() 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 @@ private IEnumerable GetDocTypeIds(string configuration, Dictionary(); 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 @@ private IEnumerable GetDocTypeIds(string configuration, Dictionary(); } - 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 1ae5267b6a33..c0d14a580ef9 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 122/908] 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 3524982a9011..75f3efe3225f 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 d175e422411f..21af10b792c6 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 123/908] 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 a058f465e023..494ef351374c 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 06e9714b7790..bb5326e76988 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 124/908] use the right wrapper, for correct spacing --- .../blockpicker/blockpicker.html | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 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 3eb04257514d..35f7f38d970f 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 125/908] 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 5d020104856b..d5525f8dd9a0 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 126/908] 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 75f3efe3225f..67def3c75a00 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 127/908] 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 301db23abde3..85ce9298c6ab 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 128/908] paste feature for block-picker --- .../blockpicker/blockpicker.controller.js | 29 ++++++++++ .../blockpicker/blockpicker.html | 41 +++++++++++++- .../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, 102 insertions(+), 26 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 d5525f8dd9a0..4957e13fad59 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 35f7f38d970f..5206c7e66d34 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 @@
    -
    + +
    + + +
    +
    +
    + + +
    + +
    + + + +
    +
    +
    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 21af10b792c6..69bfb1a71a68 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 4c74c45d888a..f0625ad767fa 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 a1de35c26609..acd63dea4f95 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 cb00bae3d137..d370de60bfda 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 129/908] localize block-picker tabs --- .../blockpicker/blockpicker.controller.js | 41 +++++++++++-------- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 2 + .../Umbraco/config/lang/en_us.xml | 2 + 3 files changed, 29 insertions(+), 16 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 4957e13fad59..1deb0100739d 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.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.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 acd63dea4f95..d3e410e39c75 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 d370de60bfda..5c833e84a507 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 130/908] 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 703c1bca264f..d269a87b48cb 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 131/908] 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 c5e3776e918e..8db36220bdad 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 d3e410e39c75..1902ec4dbf5d 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 132/908] 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 8db36220bdad..81d9bb754d44 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 133/908] 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 69bfb1a71a68..185b22d76470 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 134/908] 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 1deb0100739d..77be5b741a98 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 5206c7e66d34..196eec4cdbe1 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 185b22d76470..efc0e49d215c 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 135/908] 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 2bc017b64326..fbcd55ec92f1 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 @@ public override void Migrate() 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 136/908] 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 32db64b16b48..7f09aab5257b 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 028a10d434ea..d61d343a644e 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 ca49a3f5b8a3..06f7cf49d3c0 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 000000000000..72229e773fc4 --- /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 360201c27dcb..9a14f1148bbe 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 fba80bd57de3..5fc016f694ff 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -47,6 +47,9 @@ public class BlockConfiguration [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 137/908] 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 2702ca163a08..c1e2c72de323 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 5ce53aece45a..ce5883df6abf 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 9a14f1148bbe..86a1f549af2e 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 d118e6356cd4..35eece038e78 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 138/908] 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 72229e773fc4..03854608649c 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 86a1f549af2e..070ee4839b4a 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 b4f815a7cec5..0498db32c371 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 139/908] 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 67def3c75a00..c6897e63a22b 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 c302856807b3..4115523e731e 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 6abf85c9b6c8..094fd335588b 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 140/908] 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 4115523e731e..1cb0a0c2d637 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 141/908] more localization --- ...blocklist.blockconfiguration.controller.js | 50 +++--- ...t.blockconfiguration.overlay.controller.js | 167 ++++++++++-------- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 8 + .../Umbraco/config/lang/en_us.xml | 9 + 4 files changed, 136 insertions(+), 98 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 494ef351374c..29ea90c0f3d0 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() { + 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] + }, + position: "target", + event: $event, + size: availableItems.length < 7 ? "small" : "medium", + submit: function (overlay) { + vm.addBlockFromElementTypeAlias(overlay.selectedItem.alias); 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(); - } - }; + 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 055993f164b7..0eb8597cc1ed 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() { + localizationService.localizeMany(["blockEditor_headlineAddSettingsElementType", "blockEditor_labelcreateNewElementType"]).then(function(localized) { + + 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(); - 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(); - } - }; + close: function () { + overlayService.close(); + } + }; - overlayService.open(elemTypeSelectorOverlay); + 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 1902ec4dbf5d..e617f9b07f6f 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 5c833e84a507..df3cd8c4febe 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 142/908] 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 81d9bb754d44..9fb32339f3ef 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 143/908] 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 196eec4cdbe1..fb7e946ee78c 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 070ee4839b4a..4167667af06d 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 @@
    - + - +
    - - - + + + - - - +
    +
    @@ -86,7 +90,7 @@
    -
    +
    Change password diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html index 2e81395643d4..b77236d84f2d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html @@ -8,7 +8,7 @@
    -