From 47986ec0f59a6f73544ac312739c4a79aa1fd66d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Thu, 30 Jul 2020 15:01:29 +0200
Subject: [PATCH 01/76] limit control-label effect to the first control-label,
these 3 new lines covers all cases that i could find in CMS.
---
src/Umbraco.Web.UI.Client/src/less/mixins.less | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/Umbraco.Web.UI.Client/src/less/mixins.less b/src/Umbraco.Web.UI.Client/src/less/mixins.less
index a87080a326cb..9739a90dae7a 100644
--- a/src/Umbraco.Web.UI.Client/src/less/mixins.less
+++ b/src/Umbraco.Web.UI.Client/src/less/mixins.less
@@ -138,7 +138,9 @@
// additional targetting of the ng-invalid class.
.formFieldState(@textColor: @gray-4, @borderColor: @gray-7, @backgroundColor: @gray-10) {
// Set the text color
- .control-label,
+ > .control-label,
+ > .umb-el-wrap > .control-label,
+ > .umb-el-wrap > .control-header > .control-label,
.help-block,
.help-inline {
color: @textColor;
From 55805eabc3ab87571f23b9f40e60cc8fa4b7ea6a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Thu, 30 Jul 2020 15:26:40 +0200
Subject: [PATCH 02/76] make the required-star as bold as possible.
---
src/Umbraco.Web.UI.Client/src/less/main.less | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less
index ce20b8dc88f5..2354e96d38c7 100644
--- a/src/Umbraco.Web.UI.Client/src/less/main.less
+++ b/src/Umbraco.Web.UI.Client/src/less/main.less
@@ -272,6 +272,7 @@ label:not([for]) {
/* CONTROL VALIDATION */
.umb-control-required {
color: @controlRequiredColor;
+ font-weight: 900;
}
.controls-row {
From 33afbb8af6c4cb9127d46a36d447adb3e99b69b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Thu, 30 Jul 2020 16:13:22 +0200
Subject: [PATCH 03/76] added validation-error badge to navigation item
---
.../src/less/components/umb-editor-navigation-item.less | 8 ++++++++
.../components/editor/umb-editor-navigation-item.html | 1 +
2 files changed, 9 insertions(+)
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less
index cb673e3c6f79..c2f3e0f04d15 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less
@@ -101,6 +101,10 @@
height: 12px;
min-width: 12px;
}
+ &.--error-badge {
+ display: none;
+ font-weight: 900;
+ }
}
&-text {
@@ -191,4 +195,8 @@
&::before {
background-color: @red;
}
+ .badge.--error-badge {
+ display: block;
+ }
+
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html
index 484e0175c560..9e5669f443e4 100644
--- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html
+++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html
@@ -9,6 +9,7 @@
{{ vm.item.name }}
{{vm.item.badge.count}}
+
!
From 0e82ec9995c3af78d6dcffd6bc1efd9854150a34 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Fri, 31 Jul 2020 09:45:52 +0200
Subject: [PATCH 04/76] moved and changed validation css
---
.../umb-editor-navigation-item.less | 30 ++++++++++---------
1 file changed, 16 insertions(+), 14 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less
index c2f3e0f04d15..b3720feb3d39 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less
@@ -53,6 +53,22 @@
height: 4px;
}
}
+
+ // Validation
+ .show-validation &.-has-error {
+ color: @red;
+
+ &:hover {
+ color: @red !important;
+ }
+
+ &::before {
+ background-color: @red;
+ }
+ .badge.--error-badge {
+ display: block;
+ }
+ }
}
&__action:active,
@@ -186,17 +202,3 @@
}
}
}
-
-// Validation
-.show-validation .umb-sub-views-nav-item__action.-has-error,
-.show-validation .umb-sub-views-nav-item > a.-has-error {
- color: @red;
-
- &::before {
- background-color: @red;
- }
- .badge.--error-badge {
- display: block;
- }
-
-}
From 20779dff017610c95acfb373bbde5a3f586c8c0f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Fri, 31 Jul 2020 10:47:01 +0200
Subject: [PATCH 05/76] speach bubble look for property-errors
---
.../lib/bootstrap/less/alerts.less | 17 ++++++++++++
.../src/less/alerts.less | 27 +++++++++++++++++++
2 files changed, 44 insertions(+)
diff --git a/src/Umbraco.Web.UI.Client/lib/bootstrap/less/alerts.less b/src/Umbraco.Web.UI.Client/lib/bootstrap/less/alerts.less
index 0116b191b3b7..c99948103268 100644
--- a/src/Umbraco.Web.UI.Client/lib/bootstrap/less/alerts.less
+++ b/src/Umbraco.Web.UI.Client/lib/bootstrap/less/alerts.less
@@ -77,3 +77,20 @@
.alert-block p + p {
margin-top: 5px;
}
+
+
+// Property error alerts
+// -------------------------
+.alert.property-error {
+ &::after {
+ content:'';
+ position: absolute;
+ bottom:0;
+ left: 32px;
+ width: 0;
+ height: 0;
+ border-left: 8px solid transparent;
+ border-right: 8px solid transparent;
+ border-top: 8px solid @warningBackground;
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/less/alerts.less b/src/Umbraco.Web.UI.Client/src/less/alerts.less
index 3907b59f58de..3539e2106413 100644
--- a/src/Umbraco.Web.UI.Client/src/less/alerts.less
+++ b/src/Umbraco.Web.UI.Client/src/less/alerts.less
@@ -7,6 +7,7 @@
// -------------------------
.alert {
+ position: relative;
padding: 8px 35px 8px 14px;
margin-bottom: @baseLineHeight;
background-color: @warningBackground;
@@ -98,3 +99,29 @@
.alert-block p + p {
margin-top: 5px;
}
+
+
+// Property error alerts
+// -------------------------
+.alert.property-error {
+
+ display: inline-block;
+ font-size: 14px;
+ padding: 6px 16px 6px 12px;
+ margin-bottom: 6px;
+
+ &::after {
+ content:'';
+ position: absolute;
+ bottom:-6px;
+ left: 6px;
+ width: 0;
+ height: 0;
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-top: 6px solid;
+ }
+ &.alert-error::after {
+ border-top-color: @errorBackground;
+ }
+}
From 8d6cafa45635f3d8bebb0d7cce64741dc1827cc5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Fri, 31 Jul 2020 10:57:51 +0200
Subject: [PATCH 06/76] remove again, this is not begin used.
---
.../lib/bootstrap/less/alerts.less | 17 -----------------
1 file changed, 17 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/lib/bootstrap/less/alerts.less b/src/Umbraco.Web.UI.Client/lib/bootstrap/less/alerts.less
index c99948103268..0116b191b3b7 100644
--- a/src/Umbraco.Web.UI.Client/lib/bootstrap/less/alerts.less
+++ b/src/Umbraco.Web.UI.Client/lib/bootstrap/less/alerts.less
@@ -77,20 +77,3 @@
.alert-block p + p {
margin-top: 5px;
}
-
-
-// Property error alerts
-// -------------------------
-.alert.property-error {
- &::after {
- content:'';
- position: absolute;
- bottom:0;
- left: 32px;
- width: 0;
- height: 0;
- border-left: 8px solid transparent;
- border-right: 8px solid transparent;
- border-top: 8px solid @warningBackground;
- }
-}
From bbe2d3a9b69b586ed40455dc3c87d2b57d988bf1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Fri, 31 Jul 2020 11:25:40 +0200
Subject: [PATCH 07/76] fixing create from element type, now that udi is
changed to contentUdi
---
.../src/common/services/blockeditormodelobject.service.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js
index fe57534ffbc4..d744f8277f12 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js
@@ -730,7 +730,7 @@
return null;
}
- var dataModel = getDataByUdi(layoutEntry.udi, this.value.contentData);
+ var dataModel = getDataByUdi(layoutEntry.contentUdi, this.value.contentData);
if (dataModel === null) {
return null;
}
From 16c66b5141bd30b6fc2c71a540372940f206f8df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Fri, 31 Jul 2020 11:30:21 +0200
Subject: [PATCH 08/76] deleteAllBlocks fixed (dont know what up with
whitespace now)
---
.../umbBlockListPropertyEditor.component.js | 62 +++++++++----------
1 file changed, 31 insertions(+), 31 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js
index 327677c6b91b..5d6b1dcd1464 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js
@@ -29,7 +29,7 @@
});
function BlockListController($scope, editorService, clipboardService, localizationService, overlayService, blockEditorService, udiService, serverValidationManager, angularHelper) {
-
+
var unsubscribe = [];
var modelObject;
@@ -84,7 +84,7 @@
vm.validationLimit = vm.model.config.validationLimit;
vm.listWrapperStyles = {};
-
+
if (vm.model.config.maxPropertyWidth) {
vm.listWrapperStyles['max-width'] = vm.model.config.maxPropertyWidth;
}
@@ -100,7 +100,7 @@
} else if(vm.umbElementEditorContent && vm.umbElementEditorContent.getScope) {
scopeOfExistence = vm.umbElementEditorContent.getScope();
}
-
+
// Create Model Object, to manage our data for this Block Editor.
modelObject = blockEditorService.createModelObject(vm.model.value, vm.model.editor, vm.model.config.blocks, scopeOfExistence, $scope);
modelObject.load().then(onLoaded);
@@ -124,7 +124,7 @@
copyAllBlocksAction,
deleteAllBlocksAction
];
-
+
if (vm.umbProperty) {
vm.umbProperty.setPropertyActions(propertyActions);
}
@@ -132,7 +132,7 @@
// Called when we save the value, the server may return an updated data and our value is re-synced
// we need to deal with that here so that our model values are all in sync so we basically re-initialize.
- function onServerValueChanged(newVal, oldVal) {
+ function onServerValueChanged(newVal, oldVal) {
// 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 newVal !== 'object' || newVal === null) {// testing if we have null or undefined value or if the value is set to another type than Object.
@@ -142,13 +142,13 @@
modelObject.update(newVal, $scope);
onLoaded();
}
-
+
function setDirty() {
if (vm.propertyForm) {
vm.propertyForm.$setDirty();
}
}
-
+
function onLoaded() {
// Store a reference to the layout model, because we need to maintain this model.
@@ -161,7 +161,7 @@
// $block must have the data property to be a valid BlockObject, if not its considered as a destroyed blockObject.
if (entry.$block === undefined || entry.$block === null || entry.$block.data === undefined) {
var block = getBlockObject(entry);
-
+
// If this entry was not supported by our property-editor it would return 'null'.
if (block !== null) {
entry.$block = block;
@@ -192,7 +192,7 @@
}
function getDefaultViewForBlock(block) {
-
+
if (block.config.unsupported === true)
return "views/propertyeditors/blocklist/blocklistentryeditors/unsupportedblock/unsupportedblock.editor.html";
@@ -207,8 +207,8 @@
if (block === null) return null;
// ensure that the containing content variant language/culture is transfered along
- // to the scaffolded content object representing this block. This is required for validation
- // along with ensuring that the umb-property inheritance is constently maintained.
+ // to the scaffolded content object representing this block. This is required for validation
+ // along with ensuring that the umb-property inheritance is constently maintained.
if (vm.umbVariantContent.editor.content.language) {
block.content.language = vm.umbVariantContent.editor.content.language;
// currently we only ever deal with invariant content for blocks so there's only one
@@ -243,7 +243,7 @@
if (blockObject === null) {
return false;
}
-
+
// If we reach this line, we are good to add the layoutEntry and blockObject to our models.
// Add the Block Object to our layout entry.
@@ -251,7 +251,7 @@
// add layout entry at the decired location in layout.
vm.layout.splice(index, 0, layoutEntry);
-
+
// lets move focus to this new block.
vm.setBlockFocus(blockObject);
@@ -271,7 +271,7 @@
var removed = vm.layout.splice(layoutIndex, 1);
removed.forEach(x => {
// remove any server validation errors associated
- var guid = udiService.getKey(x.contentUdi);
+ var guid = udiService.getKey(x.contentUdi);
serverValidationManager.removePropertyError(guid, vm.umbProperty.property.culture, vm.umbProperty.property.segment, "", { matchType: "contains" });
});
@@ -280,11 +280,11 @@
}
function deleteAllBlocks() {
- vm.layout.forEach(entry => {
- deleteBlock(entry.$block);
- });
+ while(vm.layout.length) {
+ deleteBlock(vm.layout[0].$block);
+ };
}
-
+
function activateBlock(blockObject) {
blockObject.active = true;
}
@@ -317,7 +317,7 @@
if (blockObject.config.settingsElementTypeKey) {
blockSettingsClone = Utilities.copy(blockObject.settings);
}
-
+
var blockEditorModel = {
$parentScope: $scope, // pass in a $parentScope, this maintains the scope inheritance in infinite editing
$parentForm: parentForm || vm.propertyForm, // pass in a $parentForm, this maintains the FormController hierarchy with the infinite editing view (if it contains a form)
@@ -366,11 +366,11 @@
vm.showCreateDialog = showCreateDialog;
function showCreateDialog(createIndex, $event) {
-
+
if (vm.blockTypePicker) {
return;
}
-
+
if (vm.availableBlockTypes.length === 0) {
return;
}
@@ -405,7 +405,7 @@
if (blockPickerModel && blockPickerModel.selectedItem) {
added = addNewBlock(createIndex, blockPickerModel.selectedItem.blockConfigModel.contentTypeKey);
}
-
+
if(!(mouseEvent.ctrlKey || mouseEvent.metaKey)) {
editorService.close();
if (added && vm.layout.length > createIndex) {
@@ -448,7 +448,7 @@
}
);
});
-
+
var arrayEntriesForPaste = clipboardService.retriveEntriesOfType("elementTypeArray", vm.availableContentTypesAliases);
arrayEntriesForPaste.forEach(function (entry) {
blockPickerModel.clipboardItems.push(
@@ -472,13 +472,13 @@
var requestCopyAllBlocks = function() {
var elementTypesToCopy = vm.layout.filter(entry => entry.$block.config.unsupported !== true).map(entry => entry.$block.content);
-
+
// list aliases
var aliases = elementTypesToCopy.map(content => content.contentTypeAlias);
// remove dublicates
aliases = aliases.filter((item, index) => aliases.indexOf(item) === index);
-
+
var contentNodeName = "";
if(vm.umbVariantContent) {
contentNodeName = vm.umbVariantContent.editor.content.name;
@@ -494,7 +494,7 @@
clipboardService.copy("elementType", block.content.contentTypeAlias, block.content, block.label);
}
function requestPasteFromClipboard(index, pasteEntry) {
-
+
if (pasteEntry === undefined) {
return false;
}
@@ -512,7 +512,7 @@
// set the BlockObject on our layout entry.
layoutEntry.$block = blockObject;
-
+
// insert layout entry at the decired location in layout.
vm.layout.splice(index, 0, layoutEntry);
@@ -565,7 +565,7 @@
copyBlock: copyBlock,
requestDeleteBlock: requestDeleteBlock,
deleteBlock: deleteBlock,
- openSettingsForBlock: openSettingsForBlock
+ openSettingsForBlock: openSettingsForBlock
}
vm.sortableOptions = {
@@ -594,15 +594,15 @@
var isMinRequirementGood = vm.validationLimit.min === null || vm.layout.length >= vm.validationLimit.min;
vm.propertyForm.minCount.$setValidity("minCount", isMinRequirementGood);
-
+
var isMaxRequirementGood = vm.validationLimit.max === null || vm.layout.length <= vm.validationLimit.max;
vm.propertyForm.maxCount.$setValidity("maxCount", isMaxRequirementGood);
-
+
}
}
unsubscribe.push($scope.$watch(() => vm.layout.length, onAmountOfBlocksChanged));
-
+
$scope.$on("$destroy", function () {
for (const subscription of unsubscribe) {
subscription();
From 47203f71117f1572031d4fe97977b8684a5f0cdb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Fri, 31 Jul 2020 14:05:07 +0200
Subject: [PATCH 09/76] discard changes dialog
---
.../blockeditor/blockeditor.controller.js | 25 ++++++++++++++++++-
src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 2 ++
src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 2 ++
.../Umbraco/config/lang/en_us.xml | 2 ++
4 files changed, 30 insertions(+), 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 f515cbb4bab8..4d79ecb7b410 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,6 +1,6 @@
angular.module("umbraco")
.controller("Umbraco.Editors.BlockEditorController",
- function ($scope, localizationService, formHelper) {
+ function ($scope, localizationService, formHelper, overlayService) {
var vm = this;
vm.model = $scope.model;
@@ -67,6 +67,29 @@ angular.module("umbraco")
// * It would have a 'commit' method to commit the removed errors - which we would call in the formHelper.submitForm when it's successful
// * It would have a 'rollback' method to reset the removed errors - which we would call here
+
+ if (vm.blockForm.$dirty === true) {
+ localizationService.localizeMany(["prompt_discardChanges", "blockEditor_blockHasChanges"]).then(function (localizations) {
+ const confirm = {
+ title: localizations[0],
+ view: "default",
+ content: localizations[1],
+ submitButtonLabelKey: "general_discard",
+ submitButtonStyle: "danger",
+ closeButtonLabelKey: "general_cancel",
+ submit: function () {
+ overlayService.close();
+ vm.model.close(vm.model);
+ },
+ close: function () {
+ overlayService.close();
+ }
+ };
+ overlayService.open(confirm);
+ });
+
+ return;
+ }
// TODO: check if content/settings has changed and ask user if they are sure.
vm.model.close(vm.model);
}
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
index e3d456dea700..a7957c5afd2b 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
@@ -647,6 +647,7 @@
DesignOrdbogDimensioner
+ DiscardNedHentRediger
@@ -1851,6 +1852,7 @@ Mange hilsner fra Umbraco robotten
IndstillingerAvanceretSkjuld indholds editoren
+ Du har lavet ændringer til dette indhold. Er du sikker på at du vil kassere dem?
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
index 8355c36bd1b2..d72ee33d6cf3 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
@@ -675,6 +675,7 @@
DesignDictionaryDimensions
+ DiscardDownDownloadEdit
@@ -2467,6 +2468,7 @@ To manage your website, simply open the Umbraco back office and start adding con
SettingsAdvancedForce hide content editor
+ You have made changes to this content. Are you sure you want to discard them?What are Content Templates?
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 fccd11a5aa0c..cb3169526858 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
@@ -682,6 +682,7 @@
DesignDictionaryDimensions
+ DiscardDownDownloadEdit
@@ -2487,6 +2488,7 @@ To manage your website, simply open the Umbraco back office and start adding con
SettingsAdvancedForce hide content editor
+ You have made changes to this content. Are you sure you want to discard them?What are Content Templates?
From d06c1e01a7e36eedfdd2bb34119b90afa7ab9f9c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Mon, 3 Aug 2020 11:27:32 +0200
Subject: [PATCH 10/76] Update da.xml
translate to danish
---
src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
index a7957c5afd2b..997fa577b7f4 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
@@ -647,7 +647,7 @@
DesignOrdbogDimensioner
- Discard
+ KassérNedHentRediger
From c7d42d94d0c112b0b2650c3c22a6860aebee0739 Mon Sep 17 00:00:00 2001
From: Shannon
Date: Mon, 3 Aug 2020 22:02:10 +1000
Subject: [PATCH 11/76] Changes c# model for block list editor to be much
simpler.
---
.../Models/Blocks/BlockEditorModel.cs | 36 --------------
...istLayoutReference.cs => BlockListItem.cs} | 16 ++----
.../Models/Blocks/BlockListModel.cs | 49 +++++++++++++++----
src/Umbraco.Core/Umbraco.Core.csproj | 3 +-
.../BlockListPropertyValueConverterTests.cs | 45 ++++++-----------
.../BlockListTemplateExtensions.cs | 2 +-
.../BlockListPropertyValueConverter.cs | 6 +--
7 files changed, 66 insertions(+), 91 deletions(-)
delete mode 100644 src/Umbraco.Core/Models/Blocks/BlockEditorModel.cs
rename src/Umbraco.Core/Models/Blocks/{BlockListLayoutReference.cs => BlockListItem.cs} (65%)
diff --git a/src/Umbraco.Core/Models/Blocks/BlockEditorModel.cs b/src/Umbraco.Core/Models/Blocks/BlockEditorModel.cs
deleted file mode 100644
index fa5a29fece24..000000000000
--- a/src/Umbraco.Core/Models/Blocks/BlockEditorModel.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.Serialization;
-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
- {
- protected BlockEditorModel(IEnumerable contentData, IEnumerable settingsData)
- {
- ContentData = contentData ?? throw new ArgumentNullException(nameof(contentData));
- SettingsData = settingsData ?? new List();
- }
-
- public BlockEditorModel()
- {
- }
-
-
- ///
- /// The content data items of the Block List editor
- ///
- [DataMember(Name = "contentData")]
- public IEnumerable ContentData { get; set; } = new List();
-
- ///
- /// The settings data items of the Block List editor
- ///
- [DataMember(Name = "settingsData")]
- public IEnumerable SettingsData { get; set; } = new List();
- }
-}
diff --git a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs b/src/Umbraco.Core/Models/Blocks/BlockListItem.cs
similarity index 65%
rename from src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs
rename to src/Umbraco.Core/Models/Blocks/BlockListItem.cs
index f576bd927fc3..f4b5c489e7f0 100644
--- a/src/Umbraco.Core/Models/Blocks/BlockListLayoutReference.cs
+++ b/src/Umbraco.Core/Models/Blocks/BlockListItem.cs
@@ -7,10 +7,10 @@ namespace Umbraco.Core.Models.Blocks
///
/// Represents a layout item for the Block List editor
///
- [DataContract(Name = "blockListLayout", Namespace = "")]
- public class BlockListLayoutReference : IBlockReference
+ [DataContract(Name = "block", Namespace = "")]
+ public class BlockListItem : IBlockReference
{
- public BlockListLayoutReference(Udi contentUdi, IPublishedElement content, Udi settingsUdi, IPublishedElement settings)
+ public BlockListItem(Udi contentUdi, IPublishedElement content, Udi settingsUdi, IPublishedElement settings)
{
ContentUdi = contentUdi ?? throw new ArgumentNullException(nameof(contentUdi));
Content = content ?? throw new ArgumentNullException(nameof(content));
@@ -33,19 +33,13 @@ public BlockListLayoutReference(Udi contentUdi, IPublishedElement content, Udi s
///
/// The content data item referenced
///
- ///
- /// This is ignored from serialization since it is just a reference to the actual data element
- ///
- [IgnoreDataMember]
+ [DataMember(Name = "content")]
public IPublishedElement Content { get; }
///
/// The settings data item referenced
///
- ///
- /// This is ignored from serialization since it is just a reference to the actual data element
- ///
- [IgnoreDataMember]
+ [DataMember(Name = "settings")]
public IPublishedElement Settings { get; }
}
}
diff --git a/src/Umbraco.Core/Models/Blocks/BlockListModel.cs b/src/Umbraco.Core/Models/Blocks/BlockListModel.cs
index 0492cf0d7328..9a5a3af22aad 100644
--- a/src/Umbraco.Core/Models/Blocks/BlockListModel.cs
+++ b/src/Umbraco.Core/Models/Blocks/BlockListModel.cs
@@ -1,4 +1,7 @@
-using System.Collections.Generic;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
using System.Runtime.Serialization;
using Umbraco.Core.Models.PublishedContent;
@@ -8,26 +11,54 @@ namespace Umbraco.Core.Models.Blocks
/// The strongly typed model for the Block List editor
///
[DataContract(Name = "blockList", Namespace = "")]
- public class BlockListModel : BlockEditorModel
+ public class BlockListModel : IReadOnlyList
{
+ private readonly IReadOnlyList _layout = new List();
+
public static BlockListModel Empty { get; } = new BlockListModel();
private BlockListModel()
{
}
- public BlockListModel(IEnumerable contentData, IEnumerable settingsData, IEnumerable layout)
- : base(contentData, settingsData)
+ public BlockListModel(IEnumerable layout)
{
- Layout = layout;
+ _layout = layout.ToList();
}
+ public int Count => _layout.Count;
+
+ ///
+ /// Get the block by index
+ ///
+ ///
+ ///
+ public BlockListItem this[int index] => _layout[index];
+
+ ///
+ /// Get the block by content Guid
+ ///
+ ///
+ ///
+ public BlockListItem this[Guid contentKey] => _layout.FirstOrDefault(x => x.Content.Key == contentKey);
+
///
- /// The layout items of the Block List editor
+ /// Get the block by content element Udi
///
- [DataMember(Name = "layout")]
- public IEnumerable Layout { get; } = new List();
+ ///
+ ///
+ public BlockListItem this[Udi contentUdi]
+ {
+ get
+ {
+ if (!(contentUdi is GuidUdi guidUdi)) return null;
+ return _layout.FirstOrDefault(x => x.Content.Key == guidUdi.Guid);
+ }
+ }
+
+ public IEnumerator GetEnumerator() => _layout.GetEnumerator();
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
}
}
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 73af567cbca7..1b1ee4fb2836 100755
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -150,8 +150,7 @@
-
-
+
diff --git a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs
index 23cc78210680..959e059d59d0 100644
--- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs
+++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs
@@ -154,15 +154,13 @@ public void Convert_Null_Empty()
var converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel;
Assert.IsNotNull(converted);
- Assert.AreEqual(0, converted.ContentData.Count());
- Assert.AreEqual(0, converted.Layout.Count());
+ Assert.AreEqual(0, converted.Count);
json = string.Empty;
converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel;
Assert.IsNotNull(converted);
- Assert.AreEqual(0, converted.ContentData.Count());
- Assert.AreEqual(0, converted.Layout.Count());
+ Assert.AreEqual(0, converted.Count);
}
[Test]
@@ -177,8 +175,7 @@ public void Convert_Valid_Empty_Json()
var converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel;
Assert.IsNotNull(converted);
- Assert.AreEqual(0, converted.ContentData.Count());
- Assert.AreEqual(0, converted.Layout.Count());
+ Assert.AreEqual(0, converted.Count);
json = @"{
layout: {},
@@ -186,8 +183,7 @@ public void Convert_Valid_Empty_Json()
converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel;
Assert.IsNotNull(converted);
- Assert.AreEqual(0, converted.ContentData.Count());
- Assert.AreEqual(0, converted.Layout.Count());
+ Assert.AreEqual(0, converted.Count);
// Even though there is a layout, there is no data, so the conversion will result in zero elements in total
json = @"
@@ -205,8 +201,7 @@ public void Convert_Valid_Empty_Json()
converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel;
Assert.IsNotNull(converted);
- Assert.AreEqual(0, converted.ContentData.Count());
- Assert.AreEqual(0, converted.Layout.Count());
+ Assert.AreEqual(0, converted.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 = @"
@@ -228,8 +223,7 @@ public void Convert_Valid_Empty_Json()
converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel;
Assert.IsNotNull(converted);
- Assert.AreEqual(0, converted.ContentData.Count());
- Assert.AreEqual(0, converted.Layout.Count());
+ Assert.AreEqual(0, converted.Count);
// Everthing is ok except the udi reference in the layout doesn't match the data so it will be empty
json = @"
@@ -252,8 +246,7 @@ public void Convert_Valid_Empty_Json()
converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel;
Assert.IsNotNull(converted);
- Assert.AreEqual(1, converted.ContentData.Count());
- Assert.AreEqual(0, converted.Layout.Count());
+ Assert.AreEqual(0, converted.Count);
}
[Test]
@@ -283,14 +276,12 @@ public void Convert_Valid_Json()
var converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel;
Assert.IsNotNull(converted);
- Assert.AreEqual(1, converted.ContentData.Count());
- var item0 = converted.ContentData.ElementAt(0);
+ Assert.AreEqual(1, converted.Count);
+ var item0 = converted[0].Content;
Assert.AreEqual(Guid.Parse("1304E1DD-AC87-4396-84FE-8A399231CB3D"), item0.Key);
Assert.AreEqual("Test1", 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.ContentUdi);
+ Assert.IsNull(converted[0].Settings);
+ Assert.AreEqual(Udi.Parse("umb://element/1304E1DDAC87439684FE8A399231CB3D"), converted[0].ContentUdi);
}
[Test]
@@ -348,17 +339,15 @@ public void Get_Data_From_Layout_Item()
var converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel;
Assert.IsNotNull(converted);
- Assert.AreEqual(3, converted.ContentData.Count());
- Assert.AreEqual(3, converted.SettingsData.Count());
- Assert.AreEqual(2, converted.Layout.Count());
+ Assert.AreEqual(2, converted.Count);
- var item0 = converted.Layout.ElementAt(0);
+ var item0 = converted[0];
Assert.AreEqual(Guid.Parse("1304E1DD-AC87-4396-84FE-8A399231CB3D"), item0.Content.Key);
Assert.AreEqual("Test1", item0.Content.ContentType.Alias);
Assert.AreEqual(Guid.Parse("1F613E26CE274898908A561437AF5100"), item0.Settings.Key);
Assert.AreEqual("Setting2", item0.Settings.ContentType.Alias);
- var item1 = converted.Layout.ElementAt(1);
+ var item1 = converted[1];
Assert.AreEqual(Guid.Parse("0A4A416E-547D-464F-ABCC-6F345C17809A"), item1.Content.Key);
Assert.AreEqual("Test2", item1.Content.ContentType.Alias);
Assert.AreEqual(Guid.Parse("63027539B0DB45E7B70459762D4E83DD"), item1.Settings.Key);
@@ -434,11 +423,9 @@ public void Data_Item_Removed_If_Removed_From_Config()
var converted = editor.ConvertIntermediateToObject(publishedElement, propertyType, PropertyCacheLevel.None, json, false) as BlockListModel;
Assert.IsNotNull(converted);
- Assert.AreEqual(2, converted.ContentData.Count());
- Assert.AreEqual(0, converted.SettingsData.Count());
- Assert.AreEqual(1, converted.Layout.Count());
+ Assert.AreEqual(1, converted.Count);
- var item0 = converted.Layout.ElementAt(0);
+ var item0 = converted[0];
Assert.AreEqual(Guid.Parse("0A4A416E-547D-464F-ABCC-6F345C17809A"), item0.Content.Key);
Assert.AreEqual("Test2", item0.Content.ContentType.Alias);
Assert.IsNull(item0.Settings);
diff --git a/src/Umbraco.Web/BlockListTemplateExtensions.cs b/src/Umbraco.Web/BlockListTemplateExtensions.cs
index 1754eb4fc4d4..413584bc8e0f 100644
--- a/src/Umbraco.Web/BlockListTemplateExtensions.cs
+++ b/src/Umbraco.Web/BlockListTemplateExtensions.cs
@@ -14,7 +14,7 @@ public static class BlockListTemplateExtensions
public static MvcHtmlString GetBlockListHtml(this HtmlHelper html, BlockListModel model, string template = DefaultTemplate)
{
- if (model?.Layout == null || !model.Layout.Any()) return new MvcHtmlString(string.Empty);
+ if (model?.Count == 0) return new MvcHtmlString(string.Empty);
var view = DefaultFolder + template;
return html.Partial(view, model);
diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs
index 25b22e1a9ccf..0c90a41fbd80 100644
--- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs
@@ -58,7 +58,7 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub
var contentPublishedElements = new Dictionary();
var settingsPublishedElements = new Dictionary();
- var layout = new List();
+ var layout = new List();
var value = (string)inter;
if (string.IsNullOrWhiteSpace(value)) return BlockListModel.Empty;
@@ -120,11 +120,11 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub
settingsData = null;
}
- var layoutRef = new BlockListLayoutReference(contentGuidUdi, contentData, settingGuidUdi, settingsData);
+ var layoutRef = new BlockListItem(contentGuidUdi, contentData, settingGuidUdi, settingsData);
layout.Add(layoutRef);
}
- var model = new BlockListModel(contentPublishedElements.Values, settingsPublishedElements.Values, layout);
+ var model = new BlockListModel(layout);
return model;
}
}
From b0ff73fd7573fdb76e22790a5ac4589ed192ed9d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Mon, 3 Aug 2020 14:12:06 +0200
Subject: [PATCH 12/76] make badge bounce
---
.../editor/umb-variant-switcher.less | 26 +++++++++++++++++++
.../umb-editor-navigation-item.less | 21 +++++++++++++--
2 files changed, 45 insertions(+), 2 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-variant-switcher.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-variant-switcher.less
index 8dbc070856f1..eae25b273c8c 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-variant-switcher.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-variant-switcher.less
@@ -50,6 +50,19 @@ button.umb-variant-switcher__toggle {
font-weight: bold;
background-color: @errorBackground;
color: @errorText;
+
+ animation-duration: 1.4s;
+ animation-iteration-count: infinite;
+ animation-name: umb-variant-switcher__toggle--badge-bounce;
+ animation-timing-function: ease;
+ @keyframes umb-variant-switcher__toggle--badge-bounce {
+ 0% { transform: translateY(0); }
+ 20% { transform: translateY(-6px); }
+ 40% { transform: translateY(0); }
+ 55% { transform: translateY(-3px); }
+ 70% { transform: translateY(0); }
+ 100% { transform: translateY(0); }
+ }
}
}
}
@@ -226,6 +239,19 @@ button.umb-variant-switcher__toggle {
font-weight: bold;
background-color: @errorBackground;
color: @errorText;
+
+ animation-duration: 1.4s;
+ animation-iteration-count: infinite;
+ animation-name: umb-variant-switcher__name--badge-bounce;
+ animation-timing-function: ease;
+ @keyframes umb-variant-switcher__name--badge-bounce {
+ 0% { transform: translateY(0); }
+ 20% { transform: translateY(-6px); }
+ 40% { transform: translateY(0); }
+ 55% { transform: translateY(-3px); }
+ 70% { transform: translateY(0); }
+ 100% { transform: translateY(0); }
+ }
}
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less
index b3720feb3d39..2fc705b11bd7 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less
@@ -65,8 +65,25 @@
&::before {
background-color: @red;
}
- .badge.--error-badge {
- display: block;
+
+ &:not(.is-active) {
+ .badge {
+ animation-duration: 1.4s;
+ animation-iteration-count: infinite;
+ animation-name: umb-sub-views-nav-item--badge-bounce;
+ animation-timing-function: ease;
+ @keyframes umb-sub-views-nav-item--badge-bounce {
+ 0% { transform: translateY(0); }
+ 20% { transform: translateY(-6px); }
+ 40% { transform: translateY(0); }
+ 55% { transform: translateY(-3px); }
+ 70% { transform: translateY(0); }
+ 100% { transform: translateY(0); }
+ }
+ }
+ .badge.--error-badge {
+ display: block;
+ }
}
}
}
From c9b0f20fa32cb067bd4d58a7ec47307d9e390803 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Mon, 3 Aug 2020 14:13:38 +0200
Subject: [PATCH 13/76] move load to the buttom of init()
---
.../umbBlockListPropertyEditor.component.js | 63 ++++++++++---------
1 file changed, 32 insertions(+), 31 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js
index 327677c6b91b..3b54bca48ab6 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js
@@ -29,7 +29,7 @@
});
function BlockListController($scope, editorService, clipboardService, localizationService, overlayService, blockEditorService, udiService, serverValidationManager, angularHelper) {
-
+
var unsubscribe = [];
var modelObject;
@@ -84,7 +84,7 @@
vm.validationLimit = vm.model.config.validationLimit;
vm.listWrapperStyles = {};
-
+
if (vm.model.config.maxPropertyWidth) {
vm.listWrapperStyles['max-width'] = vm.model.config.maxPropertyWidth;
}
@@ -100,10 +100,6 @@
} else if(vm.umbElementEditorContent && vm.umbElementEditorContent.getScope) {
scopeOfExistence = vm.umbElementEditorContent.getScope();
}
-
- // Create Model Object, to manage our data for this Block Editor.
- modelObject = blockEditorService.createModelObject(vm.model.value, vm.model.editor, vm.model.config.blocks, scopeOfExistence, $scope);
- modelObject.load().then(onLoaded);
copyAllBlocksAction = {
labelKey: "clipboard_labelForCopyAllEntries",
@@ -124,15 +120,20 @@
copyAllBlocksAction,
deleteAllBlocksAction
];
-
+
if (vm.umbProperty) {
vm.umbProperty.setPropertyActions(propertyActions);
}
+
+ // Create Model Object, to manage our data for this Block Editor.
+ modelObject = blockEditorService.createModelObject(vm.model.value, vm.model.editor, vm.model.config.blocks, scopeOfExistence, $scope);
+ modelObject.load().then(onLoaded);
+
};
// Called when we save the value, the server may return an updated data and our value is re-synced
// we need to deal with that here so that our model values are all in sync so we basically re-initialize.
- function onServerValueChanged(newVal, oldVal) {
+ function onServerValueChanged(newVal, oldVal) {
// 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 newVal !== 'object' || newVal === null) {// testing if we have null or undefined value or if the value is set to another type than Object.
@@ -142,13 +143,13 @@
modelObject.update(newVal, $scope);
onLoaded();
}
-
+
function setDirty() {
if (vm.propertyForm) {
vm.propertyForm.$setDirty();
}
}
-
+
function onLoaded() {
// Store a reference to the layout model, because we need to maintain this model.
@@ -161,7 +162,7 @@
// $block must have the data property to be a valid BlockObject, if not its considered as a destroyed blockObject.
if (entry.$block === undefined || entry.$block === null || entry.$block.data === undefined) {
var block = getBlockObject(entry);
-
+
// If this entry was not supported by our property-editor it would return 'null'.
if (block !== null) {
entry.$block = block;
@@ -192,7 +193,7 @@
}
function getDefaultViewForBlock(block) {
-
+
if (block.config.unsupported === true)
return "views/propertyeditors/blocklist/blocklistentryeditors/unsupportedblock/unsupportedblock.editor.html";
@@ -207,8 +208,8 @@
if (block === null) return null;
// ensure that the containing content variant language/culture is transfered along
- // to the scaffolded content object representing this block. This is required for validation
- // along with ensuring that the umb-property inheritance is constently maintained.
+ // to the scaffolded content object representing this block. This is required for validation
+ // along with ensuring that the umb-property inheritance is constently maintained.
if (vm.umbVariantContent.editor.content.language) {
block.content.language = vm.umbVariantContent.editor.content.language;
// currently we only ever deal with invariant content for blocks so there's only one
@@ -243,7 +244,7 @@
if (blockObject === null) {
return false;
}
-
+
// If we reach this line, we are good to add the layoutEntry and blockObject to our models.
// Add the Block Object to our layout entry.
@@ -251,7 +252,7 @@
// add layout entry at the decired location in layout.
vm.layout.splice(index, 0, layoutEntry);
-
+
// lets move focus to this new block.
vm.setBlockFocus(blockObject);
@@ -271,7 +272,7 @@
var removed = vm.layout.splice(layoutIndex, 1);
removed.forEach(x => {
// remove any server validation errors associated
- var guid = udiService.getKey(x.contentUdi);
+ var guid = udiService.getKey(x.contentUdi);
serverValidationManager.removePropertyError(guid, vm.umbProperty.property.culture, vm.umbProperty.property.segment, "", { matchType: "contains" });
});
@@ -284,7 +285,7 @@
deleteBlock(entry.$block);
});
}
-
+
function activateBlock(blockObject) {
blockObject.active = true;
}
@@ -317,7 +318,7 @@
if (blockObject.config.settingsElementTypeKey) {
blockSettingsClone = Utilities.copy(blockObject.settings);
}
-
+
var blockEditorModel = {
$parentScope: $scope, // pass in a $parentScope, this maintains the scope inheritance in infinite editing
$parentForm: parentForm || vm.propertyForm, // pass in a $parentForm, this maintains the FormController hierarchy with the infinite editing view (if it contains a form)
@@ -366,11 +367,11 @@
vm.showCreateDialog = showCreateDialog;
function showCreateDialog(createIndex, $event) {
-
+
if (vm.blockTypePicker) {
return;
}
-
+
if (vm.availableBlockTypes.length === 0) {
return;
}
@@ -405,7 +406,7 @@
if (blockPickerModel && blockPickerModel.selectedItem) {
added = addNewBlock(createIndex, blockPickerModel.selectedItem.blockConfigModel.contentTypeKey);
}
-
+
if(!(mouseEvent.ctrlKey || mouseEvent.metaKey)) {
editorService.close();
if (added && vm.layout.length > createIndex) {
@@ -448,7 +449,7 @@
}
);
});
-
+
var arrayEntriesForPaste = clipboardService.retriveEntriesOfType("elementTypeArray", vm.availableContentTypesAliases);
arrayEntriesForPaste.forEach(function (entry) {
blockPickerModel.clipboardItems.push(
@@ -472,13 +473,13 @@
var requestCopyAllBlocks = function() {
var elementTypesToCopy = vm.layout.filter(entry => entry.$block.config.unsupported !== true).map(entry => entry.$block.content);
-
+
// list aliases
var aliases = elementTypesToCopy.map(content => content.contentTypeAlias);
// remove dublicates
aliases = aliases.filter((item, index) => aliases.indexOf(item) === index);
-
+
var contentNodeName = "";
if(vm.umbVariantContent) {
contentNodeName = vm.umbVariantContent.editor.content.name;
@@ -494,7 +495,7 @@
clipboardService.copy("elementType", block.content.contentTypeAlias, block.content, block.label);
}
function requestPasteFromClipboard(index, pasteEntry) {
-
+
if (pasteEntry === undefined) {
return false;
}
@@ -512,7 +513,7 @@
// set the BlockObject on our layout entry.
layoutEntry.$block = blockObject;
-
+
// insert layout entry at the decired location in layout.
vm.layout.splice(index, 0, layoutEntry);
@@ -565,7 +566,7 @@
copyBlock: copyBlock,
requestDeleteBlock: requestDeleteBlock,
deleteBlock: deleteBlock,
- openSettingsForBlock: openSettingsForBlock
+ openSettingsForBlock: openSettingsForBlock
}
vm.sortableOptions = {
@@ -594,15 +595,15 @@
var isMinRequirementGood = vm.validationLimit.min === null || vm.layout.length >= vm.validationLimit.min;
vm.propertyForm.minCount.$setValidity("minCount", isMinRequirementGood);
-
+
var isMaxRequirementGood = vm.validationLimit.max === null || vm.layout.length <= vm.validationLimit.max;
vm.propertyForm.maxCount.$setValidity("maxCount", isMaxRequirementGood);
-
+
}
}
unsubscribe.push($scope.$watch(() => vm.layout.length, onAmountOfBlocksChanged));
-
+
$scope.$on("$destroy", function () {
for (const subscription of unsubscribe) {
subscription();
From 52fa96c82474c91d111f68a55e71dfbdfb86cd7d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Tue, 4 Aug 2020 10:00:39 +0200
Subject: [PATCH 14/76] append the drag class if a stylesheet is present.
---
.../src/views/propertyeditors/blocklist/umb-block-list-row.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-row.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-row.html
index 55f849e4d029..bcbede231cfc 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-row.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-row.html
@@ -3,6 +3,7 @@
Date: Tue, 4 Aug 2020 11:09:45 +0200
Subject: [PATCH 15/76] remove unused index from blockEditorModel for infinite
editor
---
.../umbBlockListPropertyEditor.component.js | 57 +++++++++----------
1 file changed, 28 insertions(+), 29 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js
index 327677c6b91b..c702206393f3 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js
@@ -29,7 +29,7 @@
});
function BlockListController($scope, editorService, clipboardService, localizationService, overlayService, blockEditorService, udiService, serverValidationManager, angularHelper) {
-
+
var unsubscribe = [];
var modelObject;
@@ -84,7 +84,7 @@
vm.validationLimit = vm.model.config.validationLimit;
vm.listWrapperStyles = {};
-
+
if (vm.model.config.maxPropertyWidth) {
vm.listWrapperStyles['max-width'] = vm.model.config.maxPropertyWidth;
}
@@ -100,7 +100,7 @@
} else if(vm.umbElementEditorContent && vm.umbElementEditorContent.getScope) {
scopeOfExistence = vm.umbElementEditorContent.getScope();
}
-
+
// Create Model Object, to manage our data for this Block Editor.
modelObject = blockEditorService.createModelObject(vm.model.value, vm.model.editor, vm.model.config.blocks, scopeOfExistence, $scope);
modelObject.load().then(onLoaded);
@@ -124,7 +124,7 @@
copyAllBlocksAction,
deleteAllBlocksAction
];
-
+
if (vm.umbProperty) {
vm.umbProperty.setPropertyActions(propertyActions);
}
@@ -132,7 +132,7 @@
// Called when we save the value, the server may return an updated data and our value is re-synced
// we need to deal with that here so that our model values are all in sync so we basically re-initialize.
- function onServerValueChanged(newVal, oldVal) {
+ function onServerValueChanged(newVal, oldVal) {
// 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 newVal !== 'object' || newVal === null) {// testing if we have null or undefined value or if the value is set to another type than Object.
@@ -142,13 +142,13 @@
modelObject.update(newVal, $scope);
onLoaded();
}
-
+
function setDirty() {
if (vm.propertyForm) {
vm.propertyForm.$setDirty();
}
}
-
+
function onLoaded() {
// Store a reference to the layout model, because we need to maintain this model.
@@ -161,7 +161,7 @@
// $block must have the data property to be a valid BlockObject, if not its considered as a destroyed blockObject.
if (entry.$block === undefined || entry.$block === null || entry.$block.data === undefined) {
var block = getBlockObject(entry);
-
+
// If this entry was not supported by our property-editor it would return 'null'.
if (block !== null) {
entry.$block = block;
@@ -192,7 +192,7 @@
}
function getDefaultViewForBlock(block) {
-
+
if (block.config.unsupported === true)
return "views/propertyeditors/blocklist/blocklistentryeditors/unsupportedblock/unsupportedblock.editor.html";
@@ -207,8 +207,8 @@
if (block === null) return null;
// ensure that the containing content variant language/culture is transfered along
- // to the scaffolded content object representing this block. This is required for validation
- // along with ensuring that the umb-property inheritance is constently maintained.
+ // to the scaffolded content object representing this block. This is required for validation
+ // along with ensuring that the umb-property inheritance is constently maintained.
if (vm.umbVariantContent.editor.content.language) {
block.content.language = vm.umbVariantContent.editor.content.language;
// currently we only ever deal with invariant content for blocks so there's only one
@@ -243,7 +243,7 @@
if (blockObject === null) {
return false;
}
-
+
// If we reach this line, we are good to add the layoutEntry and blockObject to our models.
// Add the Block Object to our layout entry.
@@ -251,7 +251,7 @@
// add layout entry at the decired location in layout.
vm.layout.splice(index, 0, layoutEntry);
-
+
// lets move focus to this new block.
vm.setBlockFocus(blockObject);
@@ -271,7 +271,7 @@
var removed = vm.layout.splice(layoutIndex, 1);
removed.forEach(x => {
// remove any server validation errors associated
- var guid = udiService.getKey(x.contentUdi);
+ var guid = udiService.getKey(x.contentUdi);
serverValidationManager.removePropertyError(guid, vm.umbProperty.property.culture, vm.umbProperty.property.segment, "", { matchType: "contains" });
});
@@ -284,7 +284,7 @@
deleteBlock(entry.$block);
});
}
-
+
function activateBlock(blockObject) {
blockObject.active = true;
}
@@ -317,7 +317,7 @@
if (blockObject.config.settingsElementTypeKey) {
blockSettingsClone = Utilities.copy(blockObject.settings);
}
-
+
var blockEditorModel = {
$parentScope: $scope, // pass in a $parentScope, this maintains the scope inheritance in infinite editing
$parentForm: parentForm || vm.propertyForm, // pass in a $parentForm, this maintains the FormController hierarchy with the infinite editing view (if it contains a form)
@@ -325,7 +325,6 @@
openSettings: openSettings === true,
liveEditing: liveEditing,
title: blockObject.label,
- index: blockIndex,
view: "views/common/infiniteeditors/blockeditor/blockeditor.html",
size: blockObject.config.editorSize || "medium",
submit: function(blockEditorModel) {
@@ -366,11 +365,11 @@
vm.showCreateDialog = showCreateDialog;
function showCreateDialog(createIndex, $event) {
-
+
if (vm.blockTypePicker) {
return;
}
-
+
if (vm.availableBlockTypes.length === 0) {
return;
}
@@ -405,7 +404,7 @@
if (blockPickerModel && blockPickerModel.selectedItem) {
added = addNewBlock(createIndex, blockPickerModel.selectedItem.blockConfigModel.contentTypeKey);
}
-
+
if(!(mouseEvent.ctrlKey || mouseEvent.metaKey)) {
editorService.close();
if (added && vm.layout.length > createIndex) {
@@ -448,7 +447,7 @@
}
);
});
-
+
var arrayEntriesForPaste = clipboardService.retriveEntriesOfType("elementTypeArray", vm.availableContentTypesAliases);
arrayEntriesForPaste.forEach(function (entry) {
blockPickerModel.clipboardItems.push(
@@ -472,13 +471,13 @@
var requestCopyAllBlocks = function() {
var elementTypesToCopy = vm.layout.filter(entry => entry.$block.config.unsupported !== true).map(entry => entry.$block.content);
-
+
// list aliases
var aliases = elementTypesToCopy.map(content => content.contentTypeAlias);
// remove dublicates
aliases = aliases.filter((item, index) => aliases.indexOf(item) === index);
-
+
var contentNodeName = "";
if(vm.umbVariantContent) {
contentNodeName = vm.umbVariantContent.editor.content.name;
@@ -494,7 +493,7 @@
clipboardService.copy("elementType", block.content.contentTypeAlias, block.content, block.label);
}
function requestPasteFromClipboard(index, pasteEntry) {
-
+
if (pasteEntry === undefined) {
return false;
}
@@ -512,7 +511,7 @@
// set the BlockObject on our layout entry.
layoutEntry.$block = blockObject;
-
+
// insert layout entry at the decired location in layout.
vm.layout.splice(index, 0, layoutEntry);
@@ -565,7 +564,7 @@
copyBlock: copyBlock,
requestDeleteBlock: requestDeleteBlock,
deleteBlock: deleteBlock,
- openSettingsForBlock: openSettingsForBlock
+ openSettingsForBlock: openSettingsForBlock
}
vm.sortableOptions = {
@@ -594,15 +593,15 @@
var isMinRequirementGood = vm.validationLimit.min === null || vm.layout.length >= vm.validationLimit.min;
vm.propertyForm.minCount.$setValidity("minCount", isMinRequirementGood);
-
+
var isMaxRequirementGood = vm.validationLimit.max === null || vm.layout.length <= vm.validationLimit.max;
vm.propertyForm.maxCount.$setValidity("maxCount", isMaxRequirementGood);
-
+
}
}
unsubscribe.push($scope.$watch(() => vm.layout.length, onAmountOfBlocksChanged));
-
+
$scope.$on("$destroy", function () {
for (const subscription of unsubscribe) {
subscription();
From 7cbe50b2f156178d1432acc475a5f9b0e09ca261 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Tue, 4 Aug 2020 11:23:13 +0200
Subject: [PATCH 16/76] need to define active always, in order for it to become
false.
---
.../blockeditor/blockeditor.controller.js | 9 +++------
1 file changed, 3 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 f515cbb4bab8..b8c6f1f27b7d 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,17 +23,14 @@ angular.module("umbraco")
if (contentApp) {
if (vm.model.hideContent) {
apps.splice(apps.indexOf(contentApp), 1);
- } else if (vm.model.openSettings !== true) {
- contentApp.active = true;
}
+ contentApp.active = (vm.model.openSettings !== true);
}
if (vm.model.settings && vm.model.settings.variants) {
var settingsApp = apps.find(entry => entry.alias === "settings");
if (settingsApp) {
- if (vm.model.openSettings) {
- settingsApp.active = true;
- }
+ settingsApp.active = (vm.model.openSettings === true);
}
}
@@ -55,7 +52,7 @@ angular.module("umbraco")
vm.close = function () {
if (vm.model && vm.model.close) {
- // TODO: At this stage there could very well have been server errors that have been cleared
+ // TODO: At this stage there could very well have been server errors that have been cleared
// but if we 'close' we are basically cancelling the value changes which means we'd want to cancel
// all of the server errors just cleared. It would be possible to do that but also quite annoying.
// The rudimentary way would be to:
From 74f3b8073f3f84a6bbe884ce8fed5b094a379c43 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Tue, 4 Aug 2020 11:32:02 +0200
Subject: [PATCH 17/76] show state in submit button
---
.../infiniteeditors/blockeditor/blockeditor.controller.js | 6 +++++-
.../common/infiniteeditors/blockeditor/blockeditor.html | 2 +-
2 files changed, 6 insertions(+), 2 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 f515cbb4bab8..6dbf74f83046 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
@@ -42,6 +42,7 @@ angular.module("umbraco")
vm.submitAndClose = function () {
if (vm.model && vm.model.submit) {
+
// always keep server validations since this will be a nested editor and server validations are global
if (formHelper.submitForm({
scope: $scope,
@@ -49,13 +50,16 @@ angular.module("umbraco")
keepServerValidation: true
})) {
vm.model.submit(vm.model);
+ vm.saveButtonState = "success";
+ } else {
+ vm.saveButtonState = "error";
}
}
}
vm.close = function () {
if (vm.model && vm.model.close) {
- // TODO: At this stage there could very well have been server errors that have been cleared
+ // TODO: At this stage there could very well have been server errors that have been cleared
// but if we 'close' we are basically cancelling the value changes which means we'd want to cancel
// all of the server errors just cleared. It would be possible to do that but also quite annoying.
// The rudimentary way would be to:
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 de18f13d2c2e..dfcfd48887de 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
@@ -44,7 +44,7 @@
From 8981fd9c6fd3cf5da771a759498e56e5fba55d9d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Tue, 4 Aug 2020 11:34:07 +0200
Subject: [PATCH 18/76] should check for value before checking the length or
the value of first entry.
---
.../dropdownFlexible/dropdownFlexible.controller.js | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js
index afbb4feb2007..4064df6a2426 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js
@@ -15,14 +15,14 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleCo
//ensure this is a bool, old data could store zeros/ones or string versions
$scope.model.config.multiple = Object.toBoolean($scope.model.config.multiple);
-
+
//ensure when form is saved that we don't store [] or [null] as string values in the database when no items are selected
$scope.$on("formSubmitting", function () {
- if ($scope.model.value.length === 0 || $scope.model.value[0] === null) {
+ if ($scope.model.value && ($scope.model.value.length === 0 || $scope.model.value[0] === null)) {
$scope.model.value = null;
}
});
-
+
function convertArrayToDictionaryArray(model){
//now we need to format the items in the dictionary because we always want to have an array
var newItems = [];
@@ -41,7 +41,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleCo
var keys = _.keys($scope.model.config.items);
for (var i = 0; i < vals.length; i++) {
- var label = vals[i].value ? vals[i].value : vals[i];
+ var label = vals[i].value ? vals[i].value : vals[i];
newItems.push({ id: keys[i], sortOrder: vals[i].sortOrder, value: label });
}
@@ -65,7 +65,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleCo
else {
throw "The items property must be either an array or a dictionary";
}
-
+
//sort the values
$scope.model.config.items.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); });
@@ -80,7 +80,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleCo
$scope.model.value = "";
}
}
-
+
// if we run in single mode we'll store the value in a local variable
// so we can pass an array as the model as our PropertyValueEditor expects that
$scope.model.singleDropdownValue = "";
From 041d6a91a8824f6d35ce69a2b9b4ecbc3b521fa5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Tue, 4 Aug 2020 12:57:05 +0200
Subject: [PATCH 19/76] use the right red for error borders, no need for it to
be darker.
---
src/Umbraco.Web.UI.Client/src/less/variables.less | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less
index 2f627f3ab3cb..840c6d529f23 100644
--- a/src/Umbraco.Web.UI.Client/src/less/variables.less
+++ b/src/Umbraco.Web.UI.Client/src/less/variables.less
@@ -481,7 +481,7 @@
@formErrorText: @errorBackground;
@formErrorBackground: lighten(@errorBackground, 55%);
-@formErrorBorder: darken(spin(@errorBackground, -10), 3%);
+@formErrorBorder: @red;
@formSuccessText: @successBackground;
@formSuccessBackground: lighten(@successBackground, 48%);
From ff3d0b177001b135c5452d2258a47926e1fd129c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Tue, 4 Aug 2020 12:57:21 +0200
Subject: [PATCH 20/76] append a badge for settings, and make sure it visible.
---
.../umb-block-list-property-editor.less | 37 +++++++++++++++++++
.../blocklist/umb-block-list-row.html | 1 +
2 files changed, 38 insertions(+)
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.less
index b6f3ace44c59..0c92aef44bb8 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.less
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.less
@@ -39,6 +39,9 @@
}
}
}
+ng-form.ng-invalid-val-server-match-settings > .umb-block-list__block > .umb-block-list__block--actions {
+ opacity: 1;
+}
.umb-block-list__block--actions {
position: absolute;
z-index:999999999;// We always want to be on top of custom view, but we need to make sure we still are behind relevant Umbraco CMS UI. ToDo: Needs further testing.
@@ -58,6 +61,40 @@
&:hover {
color: @ui-action-discreet-type-hover;
}
+ > .__error-badge {
+ position: absolute;
+ top: -2px;
+ right: -2px;
+ min-width: 8px;
+ color: @white;
+ background-color: @ui-active-type;
+ border: 2px solid @white;
+ border-radius: 50%;
+ font-size: 8px;
+ font-weight: bold;
+ padding: 2px;
+ line-height: 8px;
+ background-color: @red;
+ display: none;
+ font-weight: 900;
+ }
+ &.--error > .__error-badge {
+ display: block;
+
+ animation-duration: 1.4s;
+ animation-iteration-count: infinite;
+ animation-name: umb-block-list__action--badge-bounce;
+ animation-timing-function: ease;
+ @keyframes umb-block-list__action--badge-bounce {
+ 0% { transform: translateY(0); }
+ 20% { transform: translateY(-4px); }
+ 40% { transform: translateY(0); }
+ 55% { transform: translateY(-2px); }
+ 70% { transform: translateY(0); }
+ 100% { transform: translateY(0); }
+ }
+
+ }
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-row.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-row.html
index 55f849e4d029..a2c4b5c5b478 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-row.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-row.html
@@ -19,6 +19,7 @@
Settings
+
-
+
+
+
+
From a2dd9ad0c471c86f13e18205bd6eb827d02d9b68 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Wed, 12 Aug 2020 10:34:15 +0200
Subject: [PATCH 38/76] dont render input if not shown.
---
.../src/views/components/editor/umb-editor-header.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html
index 7929f92371ca..26f03c97ca55 100644
--- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html
+++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html
@@ -42,7 +42,7 @@
localize="placeholder"
placeholder="@placeholders_entername"
name="headerName"
- ng-show="!nameLocked"
+ ng-if="!nameLocked"
ng-model="name"
ng-class="{'name-is-empty': $parent.name===null || $parent.name===''}"
umb-auto-focus
From 244d441dd45fc03ba5001e71dce0fb1904854da5 Mon Sep 17 00:00:00 2001
From: Warren Buckley
Date: Wed, 12 Aug 2020 12:04:19 +0100
Subject: [PATCH 39/76] Remove the if security check if access to settings
section - seems we do not need to do this
We check on the content node itself in the info tab for access to the doctype & template based on the section rather than the null presence of this property
---
src/Umbraco.Web/Models/Mapping/CommonMapper.cs | 15 +++------------
1 file changed, 3 insertions(+), 12 deletions(-)
diff --git a/src/Umbraco.Web/Models/Mapping/CommonMapper.cs b/src/Umbraco.Web/Models/Mapping/CommonMapper.cs
index 1276a2a405cf..f7db17ff7498 100644
--- a/src/Umbraco.Web/Models/Mapping/CommonMapper.cs
+++ b/src/Umbraco.Web/Models/Mapping/CommonMapper.cs
@@ -49,18 +49,9 @@ public UserProfile GetCreator(IContent source, MapperContext context)
public ContentTypeBasic GetContentType(IContentBase source, MapperContext context)
{
- // TODO: We can resolve the UmbracoContext from the IValueResolver options!
- // OMG
- if (HttpContext.Current != null && Composing.Current.UmbracoContext != null && Composing.Current.UmbracoContext.Security.CurrentUser != null
- && Composing.Current.UmbracoContext.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings)))
- {
- var contentType = _contentTypeBaseServiceProvider.GetContentTypeOf(source);
- var contentTypeBasic = context.Map(contentType);
-
- return contentTypeBasic;
- }
- //no access
- return null;
+ var contentType = _contentTypeBaseServiceProvider.GetContentTypeOf(source);
+ var contentTypeBasic = context.Map(contentType);
+ return contentTypeBasic;
}
public string GetTreeNodeUrl(IContentBase source)
From 2896e260af489bcc230a1942cfd1acd0ef381994 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Thu, 13 Aug 2020 07:37:53 +0200
Subject: [PATCH 40/76] Revert "dont render input if not shown."
This reverts commit a2dd9ad0c471c86f13e18205bd6eb827d02d9b68.
---
.../src/views/components/editor/umb-editor-header.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html
index 26f03c97ca55..7929f92371ca 100644
--- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html
+++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html
@@ -42,7 +42,7 @@
localize="placeholder"
placeholder="@placeholders_entername"
name="headerName"
- ng-if="!nameLocked"
+ ng-show="!nameLocked"
ng-model="name"
ng-class="{'name-is-empty': $parent.name===null || $parent.name===''}"
umb-auto-focus
From 00edd197f82e5a768772fb8d6e567183466abaaf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Thu, 13 Aug 2020 08:13:32 +0200
Subject: [PATCH 41/76] added attribute name-required, which can be used to
make name not required. Since the value defaults to true.
---
.../components/editor/umbeditorheader.directive.js | 12 +++++++-----
.../infiniteeditors/blockeditor/blockeditor.html | 1 +
.../views/components/editor/umb-editor-header.html | 4 ++--
3 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js
index 58f799e5af71..76687dc0d650 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js
@@ -188,6 +188,7 @@ Use this directive to construct a header inside the main editor window.
@param {string} name The content name.
+@param {boolean=} nameRequired Require name to be defined. (True by default)
@param {array=} tabs Array of tabs. See example above.
@param {array=} navigation Array of sub views. See example above.
@param {boolean=} nameLocked Set to true to lock the name.
@@ -199,7 +200,7 @@ Use this directive to construct a header inside the main editor window.
@param {boolean=} hideAlias Set to true to hide alias.
@param {string=} description Add a description to the content.
@param {boolean=} hideDescription Set to true to hide description.
-@param {boolean=} setpagetitle If true the page title will be set to reflect the type of data the header is working with
+@param {boolean=} setpagetitle If true the page title will be set to reflect the type of data the header is working with
@param {string=} editorfor The localization to use to aid accessibility on the edit and create screen
**/
@@ -207,7 +208,7 @@ Use this directive to construct a header inside the main editor window.
'use strict';
function EditorHeaderDirective(editorService, localizationService, editorState, $rootScope) {
-
+
function link(scope, $injector) {
scope.vm = {};
@@ -329,11 +330,11 @@ Use this directive to construct a header inside the main editor window.
}
scope.accessibility.a11yMessageVisible = !isEmptyOrSpaces(scope.accessibility.a11yMessage);
scope.accessibility.a11yNameVisible = !isEmptyOrSpaces(scope.accessibility.a11yName);
-
+
});
}
-
+
function isEmptyOrSpaces(str) {
return str === null || str===undefined || str.trim ==='';
@@ -348,7 +349,7 @@ Use this directive to construct a header inside the main editor window.
});
}
-
+
var directive = {
transclude: true,
@@ -358,6 +359,7 @@ Use this directive to construct a header inside the main editor window.
scope: {
name: "=",
nameLocked: "=",
+ nameRequired: "=?",
menu: "=",
hideActionsMenu: "",
icon: "=",
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 dfcfd48887de..285e554c6862 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
@@ -6,6 +6,7 @@
From 2b73b3ddadcb13520e2b6e9858d68b8360ffe70e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Thu, 13 Aug 2020 13:12:07 +0200
Subject: [PATCH 42/76] include PublishedPendingChanges as Published state.
---
.../content/overlays/publish.controller.js | 21 ++++++++++---------
.../src/views/content/overlays/publish.html | 15 ++++++-------
2 files changed, 17 insertions(+), 19 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js
index decf05be5f94..37f005cd4778 100644
--- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js
@@ -11,13 +11,13 @@
/** Returns true if publish meets the requirements of mandatory languages */
function canPublish() {
-
+
var hasSomethingToPublish = false;
for (var i = 0; i < vm.variants.length; i++) {
var variant = vm.variants[i];
- // if varaint is mandatory and not already published:
+ // if varaint is mandatory and not already published, then we require it to be set to publish:
if (variant.publish === false && notPublishedMandatoryFilter(variant)) {
return false;
}
@@ -39,8 +39,9 @@
function hasAnyDataFilter(variant) {
- if (variant.name == null || variant.name.length === 0) {
- return false;
+ // if we have a name, then we have data.
+ if (variant.name != null && variant.name.length > 0) {
+ return true;
}
if(variant.isDirty === true) {
@@ -77,7 +78,7 @@
}
function notPublishedMandatoryFilter(variant) {
- return variant.state !== "Published" && isMandatoryFilter(variant);
+ return variant.state !== "Published" && variant.state !== "PublishedPendingChanges" && isMandatoryFilter(variant);
}
function isMandatoryFilter(variant) {
//determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
@@ -98,11 +99,11 @@
vm.variants = $scope.model.variants;
_.each(vm.variants, (variant) => {
-
+
// reset to not be published
variant.publish = false;
variant.save = false;
-
+
variant.isMandatory = isMandatoryFilter(variant);
// If we have a variant thats not in the state of NotCreated, then we know we have adata and its not a new content node.
@@ -112,12 +113,12 @@
});
_.each(vm.variants, (variant) => {
-
+
// if this is a new node and we have data on this variant.
if(vm.isNew === true && hasAnyDataFilter(variant)) {
variant.save = true;
}
-
+
});
vm.availableVariants = vm.variants.filter(publishableVariantFilter);
@@ -139,7 +140,7 @@
if (a.language.name < b.language.name) {
return 1;
}
- }
+ }
if (a.segment && b.segment) {
if (a.segment > b.segment) {
return -1;
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html
index 71e19651d601..c3e0b90f5f09 100644
--- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html
+++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html
@@ -21,19 +21,16 @@
on-change="vm.changeSelection(variant)"
server-validation-field="{{variant.htmlId}}">
-
-
- *
-
-
-
- — {{variant.language.name}}
- *
+
+
+
+ — {{variant.language.name}}
+ * -
-
+ {{publishVariantSelectorForm.publishVariantSelector.errorMsg}}
From dde735f75cc96979f3707743fa5c1f1468906e79 Mon Sep 17 00:00:00 2001
From: Sebastiaan Janssen
Date: Thu, 13 Aug 2020 14:33:19 +0200
Subject: [PATCH 43/76] Sync some dependencies that have been updated in
v8/contrib
---
src/Umbraco.Web.UI.Client/package.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json
index 21445e3cb331..454d081fe793 100644
--- a/src/Umbraco.Web.UI.Client/package.json
+++ b/src/Umbraco.Web.UI.Client/package.json
@@ -38,11 +38,11 @@
"lazyload-js": "1.0.0",
"moment": "2.22.2",
"ng-file-upload": "12.2.13",
- "nouislider": "14.4.0",
+ "nouislider": "14.6.0",
"npm": "^6.14.7",
"signalr": "2.4.0",
"spectrum-colorpicker": "1.8.0",
- "tinymce": "4.9.10",
+ "tinymce": "4.9.11",
"typeahead.js": "0.11.1",
"underscore": "1.9.1"
},
@@ -78,7 +78,7 @@
"karma-phantomjs-launcher": "1.0.4",
"karma-spec-reporter": "0.0.32",
"less": "3.10.3",
- "lodash": "4.17.15",
+ "lodash": "4.17.19",
"marked": "^0.7.0",
"merge-stream": "2.0.0",
"run-sequence": "2.2.1"
From 6d6f525343c9f3794063cd96e78a36b39893540c Mon Sep 17 00:00:00 2001
From: Warren Buckley
Date: Tue, 18 Aug 2020 12:43:29 +0100
Subject: [PATCH 44/76] Fix to ensure we use virtual paths for any blocks with
custom views (#8597)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Niels Lyngsø
Co-authored-by: Shannon
---
.../services/umbrequesthelper.service.js | 31 ++++++++++---------
...t.blockconfiguration.overlay.controller.js | 7 +++--
.../blocklist/umbblocklistblock.component.js | 23 +++++++-------
.../services/umb-request-helper.spec.js | 28 +++++++++++++++++
4 files changed, 59 insertions(+), 30 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js
index 7b43b239eadf..90125e7de642 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js
@@ -15,7 +15,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* This will convert a virtual path (i.e. ~/App_Plugins/Blah/Test.html ) to an absolute path
- *
+ *
* @param {string} a virtual path, if this is already an absolute path it will just be returned, if this is a relative path an exception will be thrown
*/
convertVirtualToAbsolutePath: function(virtualPath) {
@@ -31,6 +31,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
return Umbraco.Sys.ServerVariables.application.applicationPath + virtualPath.trimStart("~/");
},
+
/**
* @ngdoc method
* @name umbraco.services.umbRequestHelper#dictionaryToQueryString
@@ -39,7 +40,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* This will turn an array of key/value pairs or a standard dictionary into a query string
- *
+ *
* @param {Array} queryStrings An array of key/value pairs
*/
dictionaryToQueryString: function (queryStrings) {
@@ -76,9 +77,9 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* This will return the webapi Url for the requested key based on the servervariables collection
- *
+ *
* @param {string} apiName The webapi name that is found in the servervariables["umbracoUrls"] dictionary
- * @param {string} actionName The webapi action name
+ * @param {string} actionName The webapi action name
* @param {object} queryStrings Can be either a string or an array containing key/value pairs
*/
getApiUrl: function (apiName, actionName, queryStrings) {
@@ -103,7 +104,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* This returns a promise with an underlying http call, it is a helper method to reduce
- * the amount of duplicate code needed to query http resources and automatically handle any
+ * the amount of duplicate code needed to query http resources and automatically handle any
* Http errors. See /docs/source/using-promises-resources.md
*
* @param {object} opts A mixed object which can either be a string representing the error message to be
@@ -117,7 +118,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
* The error callback must return an object containing: {errorMsg: errorMessage, data: originalData, status: status }
*/
resourcePromise: function (httpPromise, opts) {
-
+
/** The default success callback used if one is not supplied in the opts */
function defaultSuccess(data, status, headers, config) {
//when it's successful, just return the data
@@ -151,7 +152,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
return httpPromise.then(function (response) {
- //invoke the callback
+ //invoke the callback
var result = callbacks.success.apply(this, [response.data, response.status, response.headers, response.config]);
formHelper.showNotifications(response.data);
@@ -183,7 +184,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
overlayService.ysod(error);
}
else {
- //show a simple error notification
+ //show a simple error notification
notificationsService.error("Server error", "Contact administrator, see log for full details. " + result.errorMsg + "");
}
@@ -209,7 +210,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* Used for saving content/media/members specifically
- *
+ *
* @param {Object} args arguments object
* @returns {Promise} http promise object.
*/
@@ -233,7 +234,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
if (args.showNotifications === null || args.showNotifications === undefined) {
args.showNotifications = true;
}
-
+
//save the active tab id so we can set it when the data is returned.
var activeTab = _.find(args.content.tabs, function (item) {
return item.active;
@@ -300,7 +301,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
overlayService.ysod(error);
}
else {
- //show a simple error notification
+ //show a simple error notification
notificationsService.error("Server error", "Contact administrator, see log for full details. " + response.data.ExceptionMessage + "");
}
@@ -331,7 +332,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
});
}
else if (!jsonData.key || !jsonData.value) { throw "jsonData object must have both a key and a value property"; }
-
+
return $http({
method: 'POST',
url: url,
@@ -366,7 +367,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
return $q.reject(response);
});
},
-
+
/**
* @ngdoc method
* @name umbraco.resources.contentResource#downloadFile
@@ -374,7 +375,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* Downloads a file to the client using AJAX/XHR
- *
+ *
* @param {string} httpPath the path (url) to the resource being downloaded
* @returns {Promise} http promise object.
*/
@@ -388,7 +389,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
// Use an arraybuffer
return $http.get(httpPath, { responseType: 'arraybuffer' })
.then(function (response) {
-
+
var octetStreamMime = 'application/octet-stream';
var success = false;
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 b1937e718d76..48c21b4009ab 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
@@ -160,7 +160,7 @@
},
select: function (node) {
const filepath = decodeURIComponent(node.id.replace(/\+/g, " "));
- block.view = filepath;
+ block.view = "~/" + filepath;
editorService.close();
},
close: function () {
@@ -206,7 +206,7 @@
},
select: function (node) {
const filepath = decodeURIComponent(node.id.replace(/\+/g, " "));
- block.stylesheet = filepath;
+ block.stylesheet = "~/" + filepath;
editorService.close();
},
close: function () {
@@ -252,7 +252,8 @@
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;
+ const id = decodeURIComponent(file.id.replace(/\+/g, " "));
+ block.thumbnail = "~/" + id;
editorService.close();
},
close: function () {
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbblocklistblock.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbblocklistblock.component.js
index 4474dbd55c42..64fd40459dea 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbblocklistblock.component.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbblocklistblock.component.js
@@ -5,11 +5,11 @@
* @ngdoc directive
* @name umbraco.directives.directive:umbBlockListBlock
* @description
- * The component to render the view for a block.
+ * The component to render the view for a block.
* If a stylesheet is used then this uses a ShadowDom to make a scoped element.
* This way the backoffice styling does not collide with the block style.
*/
-
+
angular
.module("umbraco")
.component("umbBlockListBlock", {
@@ -29,16 +29,16 @@
}
);
- function BlockListBlockController($scope, $compile, $element) {
+ function BlockListBlockController($scope, $compile, $element, umbRequestHelper) {
var model = this;
model.$onInit = function () {
- // This is ugly and is only necessary because we are not using components and instead
+ // This is ugly and is only necessary because we are not using components and instead
// relying on ng-include. It is definitely possible to compile the contents
- // of the view into the DOM using $templateCache and $http instead of using
- // ng - include which means that the controllerAs flows directly to the view.
- // This would mean that any custom components would need to be updated instead of relying on $scope.
- // Guess we'll leave it for now but means all things need to be copied to the $scope and then all
+ // of the view into the DOM using $templateCache and $http instead of using
+ // ng - include which means that the controllerAs flows directly to the view.
+ // This would mean that any custom components would need to be updated instead of relying on $scope.
+ // Guess we'll leave it for now but means all things need to be copied to the $scope and then all
// primitives need to be watched.
$scope.block = model.block;
@@ -48,8 +48,7 @@
$scope.valFormManager = model.valFormManager;
if (model.stylesheet) {
- // TODO: Not sure why this needs a prefixed /? this means it will never work in a virtual directory
- model.stylesheet = "/" + model.stylesheet;
+ model.stylesheet = umbRequestHelper.convertVirtualToAbsolutePath(model.stylesheet);
var shadowRoot = $element[0].attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `