Merge pull request #8736 from umbraco/v8/bugfix/block-list-recreate-udis-on-paste

V8.7RC: Recreate UDIs for pasted Block Editor entries
This commit is contained in:
Mole
2020-09-03 11:33:44 +02:00
committed by GitHub
4 changed files with 139 additions and 20 deletions

View File

@@ -4,39 +4,119 @@
*
* @description
* <b>Added in Umbraco 8.7</b>. Service for dealing with Block Editors.
*
*
* Block Editor Service provides the basic features for a block editor.
* The main feature is the ability to create a Model Object which takes care of your data for your Block Editor.
*
*
*
*
* ##Samples
*
* ####Instantiate a Model Object for your property editor:
*
*
* <pre>
* modelObject = blockEditorService.createModelObject(vm.model.value, vm.model.editor, vm.model.config.blocks, $scope);
* modelObject.load().then(onLoaded);
* </pre>
*
*
*
* See {@link umbraco.services.blockEditorModelObject BlockEditorModelObject} for more samples.
*
*
*/
(function () {
'use strict';
/**
* When performing a runtime copy of Block Editors entries, we copy the ElementType Data Model and inner IDs are kept identical, to ensure new IDs are changed on paste we need to provide a resolver for the ClipboardService.
*/
angular.module('umbraco').run(['clipboardService', 'udiService', function (clipboardService, udiService) {
function replaceUdi(obj, key, dataObject) {
var udi = obj[key];
var newUdi = udiService.create("element");
obj[key] = newUdi;
dataObject.forEach((data) => {
if (data.udi === udi) {
data.udi = newUdi;
}
});
}
function replaceUdisOfObject(obj, propValue) {
for (var k in obj) {
if(k === "contentUdi") {
replaceUdi(obj, k, propValue.contentData);
} else if(k === "settingsUdi") {
replaceUdi(obj, k, propValue.settingsData);
} else {
var propType = typeof obj[k];
if(propType === "object" || propType === "array") {
replaceUdisOfObject(obj[k], propValue)
}
}
}
}
function replaceBlockListUDIsResolver(obj, propClearingMethod) {
if (typeof obj === "object") {
// 'obj' can both be a property object or the raw value of a inner property.
var value = obj;
// if we got a property object from a ContentTypeModel we need to look at the value. We check for value and editor to, sort of, ensure this is the case.
if(obj.value !== undefined && obj.editor !== undefined) {
value = obj.value;
// If value isnt a object, lets break out.
if(typeof obj.value !== "object") {
return;
}
}
// we got an object, and it has these three props then we are most likely dealing with a Block Editor.
if ((value.layout !== undefined && value.contentData !== undefined && value.settingsData !== undefined)) {
replaceUdisOfObject(value.layout, value);
// replace UDIs for inner properties of this Block Editors content data.
if(value.contentData.length > 0) {
value.contentData.forEach((item) => {
for (var k in item) {
propClearingMethod(item[k]);
}
});
}
// replace UDIs for inner properties of this Block Editors settings data.
if(value.settingsData.length > 0) {
value.settingsData.forEach((item) => {
for (var k in item) {
propClearingMethod(item[k]);
}
});
}
}
}
}
clipboardService.registerPastePropertyResolver(replaceBlockListUDIsResolver)
}]);
function blockEditorService(blockEditorModelObject) {
/**
* @ngdocs function
* @name createModelObject
* @methodOf umbraco.services.blockEditorService
*
*
* @description
* Create a new Block Editor Model Object.
* See {@link umbraco.services.blockEditorModelObject blockEditorModelObject}
*
*
* @see umbraco.services.blockEditorModelObject
* @param {object} propertyModelValue data object of the property editor, usually model.value.
* @param {string} propertyEditorAlias alias of the property.

View File

@@ -13,8 +13,7 @@
(function () {
'use strict';
function blockEditorModelObjectFactory($interpolate, $q, udiService, contentResource, localizationService, umbRequestHelper) {
function blockEditorModelObjectFactory($interpolate, $q, udiService, contentResource, localizationService, umbRequestHelper, clipboardService) {
/**
* Simple mapping from property model content entry to editing model,
@@ -746,7 +745,7 @@
*/
createFromElementType: function (elementTypeDataModel) {
elementTypeDataModel = Utilities.copy(elementTypeDataModel);
elementTypeDataModel = clipboardService.parseContentForPaste(elementTypeDataModel);
var contentElementTypeKey = elementTypeDataModel.contentTypeKey;

View File

@@ -14,6 +14,7 @@ function clipboardService(notificationsService, eventsService, localStorageServi
var clearPropertyResolvers = [];
var pastePropertyResolvers = [];
var STORAGE_KEY = "umbClipboardService";
@@ -95,8 +96,46 @@ function clipboardService(notificationsService, eventsService, localStorageServi
}
function resolvePropertyForPaste(prop) {
for (var i=0; i<pastePropertyResolvers.length; i++) {
pastePropertyResolvers[i](prop, resolvePropertyForPaste);
}
}
var service = {};
/**
* @ngdoc method
* @name umbraco.services.clipboardService#parseContentForPaste
* @methodOf umbraco.services.clipboardService
*
* @param {object} function The content entry to be cloned and resolved.
*
* @description
* Executed registered property resolvers for inner properties, to be done on pasting a clipbaord content entry.
*
*/
service.parseContentForPaste = function(contentEntryData) {
var cloneData = Utilities.copy(contentEntryData);
// remove keys from sub-entries
for (var t = 0; t < cloneData.variants[0].tabs.length; t++) {
var tab = cloneData.variants[0].tabs[t];
for (var p = 0; p < tab.properties.length; p++) {
var prop = tab.properties[p];
resolvePropertyForPaste(prop);
}
}
return cloneData;
}
/**
* @ngdoc method
@@ -119,7 +158,7 @@ function clipboardService(notificationsService, eventsService, localStorageServi
* @name umbraco.services.clipboardService#registerClearPropertyResolver
* @methodOf umbraco.services.clipboardService
*
* @param {string} function A method executed for every property and inner properties copied.
* @param {string} function A method executed for every property and inner properties copied. Notice the method both needs to deal with retriving a property object {alias:..editor:..value:... ,...} and the raw property value as if the property is an inner property of a nested property.
*
* @description
* Executed for all properties including inner properties when performing a copy action.
@@ -128,19 +167,18 @@ function clipboardService(notificationsService, eventsService, localStorageServi
clearPropertyResolvers.push(resolver);
};
/**
* @ngdoc method
* @name umbraco.services.clipboardService#registrerPropertyClearingResolver
* @name umbraco.services.clipboardService#registerPastePropertyResolver
* @methodOf umbraco.services.clipboardService
*
* @param {string} function A method executed for every property and inner properties copied.
* @param {string} function A method executed for every property and inner properties pasted. Notice the method both needs to deal with retriving a property object {alias:..editor:..value:... ,...} and the raw property value as if the property is an inner property of a nested property.
*
* @description
* Executed for all properties including inner properties when performing a copy action.
* Executed for all properties including inner properties when performing a paste action.
*/
service.registrerClearPropertyResolver = function(resolver) {
clearPropertyResolvers.push(resolver);
service.registerPastePropertyResolver = function(resolver) {
pastePropertyResolvers.push(resolver);
};

View File

@@ -28,7 +28,7 @@
}
}
clipboardService.registrerClearPropertyResolver(clearNestedContentPropertiesForStorage)
clipboardService.registerClearPropertyResolver(clearNestedContentPropertiesForStorage)
function clearInnerNestedContentPropertiesForStorage(prop, propClearingMethod) {
@@ -50,7 +50,7 @@
}
}
clipboardService.registrerClearPropertyResolver(clearInnerNestedContentPropertiesForStorage)
clipboardService.registerClearPropertyResolver(clearInnerNestedContentPropertiesForStorage)
}]);
angular
@@ -474,6 +474,8 @@
return;
}
newNode = clipboardService.parseContentForPaste(newNode);
// generate a new key.
newNode.key = String.CreateGuid();