diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 7459ae848b..9cd4f39c17 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -268,7 +268,7 @@ AnotherContentFinder public void GetDataEditors() { var types = _typeLoader.GetDataEditors(); - Assert.AreEqual(38, types.Count()); + Assert.AreEqual(39, types.Count()); } /// diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 3e8f053bc6..34ab3eb90d 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1222,7 +1222,7 @@ "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=", "dev": true }, "asap": { @@ -1284,7 +1284,7 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=", "dev": true }, "asynckit": { @@ -1535,7 +1535,7 @@ }, "bl": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "dev": true, "requires": { @@ -2516,7 +2516,7 @@ "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", "dev": true }, "continuable-cache": { @@ -4045,7 +4045,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -4081,7 +4081,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -4476,7 +4476,7 @@ "eventemitter3": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=", "dev": true }, "exec-buffer": { @@ -5309,8 +5309,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -5331,14 +5330,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5353,20 +5350,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -5483,8 +5477,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -5496,7 +5489,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5511,7 +5503,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5519,14 +5510,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -5545,7 +5534,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5626,8 +5614,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5639,7 +5626,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5725,8 +5711,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5762,7 +5747,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5782,7 +5766,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5826,14 +5809,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -7575,7 +7556,7 @@ "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "integrity": "sha1-d3asYn8+p3JQz8My2rfd9eT10R0=", "dev": true, "requires": { "isarray": "2.0.1" @@ -7778,7 +7759,7 @@ "http-proxy": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "integrity": "sha1-etOElGWPhGBeL220Q230EPTlvpo=", "dev": true, "requires": { "eventemitter3": "^3.0.0", @@ -13616,7 +13597,7 @@ "qjobs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=", "dev": true }, "qs": { @@ -14787,7 +14768,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -15860,7 +15841,7 @@ "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=", "dev": true, "requires": { "media-typer": "0.3.0", @@ -15902,7 +15883,7 @@ "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "integrity": "sha1-n+FTahCmZKZSZqHjzPhf02MCvJw=", "dev": true }, "unc-path-regex": { @@ -16505,7 +16486,7 @@ "ws": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "integrity": "sha1-8c+E/i1ekB686U767OeF8YeiKPI=", "dev": true, "requires": { "async-limiter": "~1.0.0", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/blockeditor/blocklist.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/blockeditor/blocklist.directive.js new file mode 100644 index 0000000000..d01d8c0bd9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/blockeditor/blocklist.directive.js @@ -0,0 +1,65 @@ +(function () { + "use strict"; + + /** + * A component used to render a list of blocks + */ + var umbBlockList = { + templateUrl: 'views/components/blockeditor/umb-block-list.html', + bindings: { + blocks: "<", + config: "<", + scaffolds: "<", + onEdit: "&", + onRemove: "&", + onSettings: "&", + onSort: "&?" + }, + controllerAs: 'vm', + controller: umbBlockListController + }; + + function umbBlockListController() { + var vm = this; + + vm.sortableOptions = { + axis: "y", + cursor: "move", + handle: ".handle", + tolerance: 'pointer', + update: function (e, ui) { + if (vm.onSort) { + vm.onSort({ e: e, ui: ui }); + } + } + }; + + vm.editContent = function (block) { + var scaffold = _.findWhere(vm.scaffolds, { + udi: block.udi + }); + var element = angular.copy(scaffold); + _.each(element.variants[0].tabs, function (tab) { + _.each(tab.properties, function (property) { + if (block.content[property.alias]) { + property.value = block.content[property.alias]; + } + }); + }); + + vm.onEdit({ element: element, block: block }); + } + + vm.remove = function (block) { + // this should be replaced by a custom dialog (pending some PRs) + if (confirm("TODO: Are you sure?")) { + vm.onRemove({ block: block }); + } + } + + } + + angular.module('umbraco').component('umbBlockList', umbBlockList); + + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index d714ea4938..2919006d0b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -509,6 +509,20 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }); }, + getScaffoldByUdi: function (parentId, udi) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetEmpty", + [{ udi: udi }, { parentId: parentId }])), + 'Failed to retrieve data for empty content item with udi ' + udi) + .then(function (result) { + return $q.when(umbDataFormatter.formatContentGetData(result)); + }); + }, + getBlueprintScaffold: function (parentId, blueprintId) { return umbRequestHelper.resourcePromise( diff --git a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less index c8f0195ea5..ea3c9335f0 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less @@ -132,6 +132,10 @@ &.umb-overlay--medium { width: 480px; } + + &.umb-overlay--large { + width:640px; + } } .umb-overlay.umb-overlay-target .umb-overlay-header { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/blockeditor/umb-block-list.html b/src/Umbraco.Web.UI.Client/src/views/components/blockeditor/umb-block-list.html new file mode 100644 index 0000000000..5a7251e2f3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/blockeditor/umb-block-list.html @@ -0,0 +1,18 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js index d04c707b25..f906369926 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js @@ -94,6 +94,11 @@ function contentCreateController($scope, navigationService.hideDialog(showMenu); }; + $scope.editContentType = function() { + $location.path("/settings/documenttypes/edit/" + $scope.contentTypeId).search("view", "permissions"); + close(); + } + $scope.createBlank = createBlank; $scope.createOrSelectBlueprintIfAny = createOrSelectBlueprintIfAny; $scope.createFromBlueprint = createFromBlueprint; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/create.html b/src/Umbraco.Web.UI.Client/src/views/content/create.html index 059b5eaf25..b8303efade 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/create.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/create.html @@ -10,7 +10,7 @@

-
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/block.settings.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/block.settings.html new file mode 100644 index 0000000000..1a86afd8a5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/block.settings.html @@ -0,0 +1,90 @@ +
+ + + + + + + + + +
+ +
+
+ +
+ +
+ +
+
+ +
+ + + + + +
+ + + + + +
+ + +
+ + + + + +
+ +
+ +
+ +
+
+
+ + + + + + + + + + +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.controller.js new file mode 100644 index 0000000000..6d59022652 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.controller.js @@ -0,0 +1,204 @@ +(function () { + "use strict"; + + function BlockEditorController($scope, contentResource, editorService, iconHelper, clipboardService) { + var vm = this; + + vm.scaffolds = []; + vm.loading = true; + + let allowedElements = []; + + // it would be awesome if we could load all scaffolds in one go... however we need to have an eye out for performance, + // oddly enough it's been shown to actually be slower to load them all at once instead of one at a time + var scaffoldsLoaded = 0; + + function init() { + + _.each($scope.model.config.blocks, function (config) { + contentResource.getScaffoldByUdi(-20, config.elementType).then(function (scaffold) { + if (scaffold.isElement) { + // the scaffold udi is not the same as the element type udi, but we need it to be for comparison + scaffold.udi = config.elementType; + vm.scaffolds.push(scaffold); + } + scaffoldsLoaded++; + initIfAllScaffoldsHaveLoaded(); + }, function (error) { + scaffoldsLoaded++; + initIfAllScaffoldsHaveLoaded(); + }); + }); + } + + function initIfAllScaffoldsHaveLoaded() { + // Initialize when all scaffolds have loaded + if ($scope.model.config.blocks.length === scaffoldsLoaded) { + vm.scaffolds = _.sortBy(vm.scaffolds, function (scaffold) { + return _.findIndex($scope.model.config.blocks, function (blockConfig) { + return blockConfig.elementType === scaffold.udi; + }); + }); + + _.each($scope.model.value, function (block) { + applyFakeSettings(block); + }); + + allowedElements = vm.scaffolds.map(x => x.contentTypeAlias); + + vm.loading = false; + } + } + + + vm.openNodeTypePicker = function ($event) { + + vm.overlayMenu = { + show: false, + style: {}, + filter: vm.scaffolds.length > 12 ? true : false, + orderBy: "$index", + view: "itempicker", + event: $event, + clickPasteItem: function (item) { + vm.pasteFromClipboard(item.data); + vm.overlayMenu.show = false; + vm.overlayMenu = null; + }, + submit: function (model) { + if (model && model.selectedItem) { + vm.add(vm.scaffolds.filter(x => x.contentTypeAlias === model.selectedItem.alias)[0]); + } + vm.overlayMenu.show = false; + vm.overlayMenu = null; + }, + close: function () { + vm.overlayMenu.show = false; + vm.overlayMenu = null; + } + }; + + // this could be used for future limiting on node types + vm.overlayMenu.availableItems = []; + vm.scaffolds.forEach(function (scaffold) { + vm.overlayMenu.availableItems.push({ + alias: scaffold.contentTypeAlias, + name: scaffold.contentTypeName, + icon: iconHelper.convertFromLegacyIcon(scaffold.icon), + description: scaffold.documentType.description + }); + }); + + if (vm.overlayMenu.availableItems.length === 0) { + return; + } + + //TODO: If we support larger thumbnails of some sort one day, this could be large + vm.overlayMenu.size = vm.overlayMenu.availableItems.length > 6 ? "medium" : "small"; + + vm.overlayMenu.pasteItems = []; + var availableNodesForPaste = clipboardService.retriveDataOfType("elementType", allowedElements); + availableNodesForPaste.forEach(function (node) { + vm.overlayMenu.pasteItems.push({ + alias: node.contentTypeAlias, + name: node.name, //contentTypeName + data: node, + icon: iconHelper.convertFromLegacyIcon(node.icon) + }); + }); + + vm.overlayMenu.clickClearPaste = function ($event) { + $event.stopPropagation(); + $event.preventDefault(); + clipboardService.clearEntriesOfType("elementType", allowedElements); + vm.overlayMenu.pasteItems = []; // This dialog is not connected via the clipboardService events, so we need to update manually. + }; + + if (vm.overlayMenu.availableItems.length === 1 && vm.overlayMenu.pasteItems.length === 0) { + // only one scaffold type - no need to display the picker + vm.add(vm.scaffolds[0]); + return; + } + + vm.overlayMenu.show = true; + } + + vm.add = function (scaffold) { + var element = angular.copy(scaffold); + var block = { + udi: element.udi, + icon: element.icon, + description: element.documentType.description, // probably don't want to persist these. + content: {}, + settings: { + view: 'views/propertyeditors/blockeditor/blockeditor.block.html' + } + }; + openContent(element, block); + } + + vm.editSettings = function (block) { + var options = { + settings: block.settings, + title: "Edit settings", + view: "views/propertyeditors/blockeditor/blockeditor.editsettings.html", + size: "small", + submit: function (model) { + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.open(options); + } + + vm.onRemove = function (block) { + $scope.model.value.splice($scope.model.value.indexOf(block), 1); + } + + vm.onEdit = function (element, block) { + openContent(element, block); + } + + function openContent(element, block) { + var options = { + element: element, + title: 'Edit block', + //fixme: This isn't really a component if its strongly tied to views in the property editor :/ + view: "views/propertyeditors/blockeditor/blockeditor.editcontent.html", + submit: function (model) { + _.each(element.variants[0].tabs, function (tab) { + _.each(tab.properties, function (property) { + block.content[property.alias] = property.value; + }); + }); + if ($scope.model.value.indexOf(block) < 0) { + $scope.model.value.push(block); + applyFakeSettings(block); + } + + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.open(options); + } + + // TODO: remove this (only for testing) + // currently pushes random values for setting a css grid location + function applyFakeSettings(block) { + block.settings["col"] = 1 + Math.floor(Math.random() * 7); + block.settings["row"] = 1 + Math.floor(Math.random() * 7); + block.settings["w"] = 1 + Math.floor(Math.random() * 3); + block.settings["h"] = 1 + Math.floor(Math.random() * 3); + } + + init(); + } + + angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditorController", BlockEditorController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.editcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.editcontent.controller.js new file mode 100644 index 0000000000..73eb392162 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.editcontent.controller.js @@ -0,0 +1,34 @@ +/** + * @ngdoc controller + * @name Umbraco.PropertyEditors.BlockEditor.EditContentController + * @function + * + * @description + * The controller for editing content with the block editor + */ + +function BlockEditorEditContentController($scope) { + var vm = this; + vm.submit = submit; + vm.close = close; + vm.content = $scope.model.element.variants[0]; + + function init() { + + } + + function submit() { + if($scope.model.submit) { + $scope.model.submit($scope.model); + } + } + + function close() { + if($scope.model.close) { + $scope.model.close(); + } + } + + init(); +} +angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditor.EditContentController", BlockEditorEditContentController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.editcontent.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.editcontent.html new file mode 100644 index 0000000000..5026de8b63 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.editcontent.html @@ -0,0 +1,38 @@ +
+ + + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.editsettings.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.editsettings.controller.js new file mode 100644 index 0000000000..bb375d558b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.editsettings.controller.js @@ -0,0 +1,26 @@ +/** + * @ngdoc controller + * @name Umbraco.PropertyEditors.BlockEditor.EditSettingsController + * @function + * + * @description + * The controller for editing the configuration of an individual block + */ + +//fixme: Need to figure out the name of a block settings, is it block config or block settings? +function BlockEditorEditSettingsController($scope) { + + this.submit = () => { + if($scope.model.submit) { + $scope.model.submit($scope.model); + } + } + + this.close = () => { + if($scope.model.close) { + $scope.model.close(); + } + } +} +angular.module("umbraco") + .controller("Umbraco.PropertyEditors.BlockEditor.EditSettingsController", ['$scope', BlockEditorEditSettingsController]); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.editsettings.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.editsettings.html new file mode 100644 index 0000000000..703661345b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.editsettings.html @@ -0,0 +1,44 @@ +
+ + + + + + + +
+ + + + + + + + + +
+
+ + + + + + + + + + +
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.html new file mode 100644 index 0000000000..c0eae888b9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.html @@ -0,0 +1,31 @@ +
+
+ +
+ +
+ + +
+ + + + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.settings.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.settings.controller.js new file mode 100644 index 0000000000..2d4f11d3cf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.settings.controller.js @@ -0,0 +1,96 @@ +/** + * @ngdoc controller + * @name Umbraco.PropertyEditors.BlockEditor.SettingsController + * @function + * + * @description + * The controller for the block editor's data type configuration + */ + +//fixme: Rename this! it shouldn't be called Settings, it should be called Configuration because that is what we call pre-values in v8 (data type config) +angular.module("umbraco") + .controller("Umbraco.PropertyEditors.BlockEditor.SettingsController", [ + "$scope", + "contentTypeResource", + "editorService", + function ($scope, contentTypeResource, editorService) { + + $scope.blocks = []; + $scope.elementTypes = []; + + if ($scope.model.value) { + $scope.blocks = $scope.model.value; + } else { + $scope.model.value = []; + } + + contentTypeResource.getAll() + .then(function (contentType) { + $scope.elementTypes.push(contentType); + }); + + $scope.$on("formSubmitting", function () { + $scope.model.value = $scope.blocks; + }); + + //fixme: This shouldn't be a method, using methods to return values in angular is a performance issue + $scope.getBlockName = function(udi) { + return 'TODO: Get block name from somewhere...'; + } + + $scope.addBlock = function () { + + var block = {}; + + var pickerOptions = { + title: "Pick an element type", + view: "views/common/infiniteeditors/treepicker/treepicker.html", + size: "small", + multiPicker: false, + section: "settings", + treeAlias: "documentTypes", + entityType: "documentType", + submit: function (model) { + _.each(model.selection, function (elementType) { + block.elementType = elementType.udi; + block.settings = { + view: 'views/propertyeditors/blockeditor/blockeditor.block.html' + } + $scope.model.value.push(block); + }); + + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + + editorService.open(pickerOptions); + } + + $scope.removeBlock = function (index) { + $scope.model.value.splice(index, 1); + }; + + //fixme: This currently doesn't work or do anything + $scope.editSettings = function (block) { + + var pickerOptions = { + title: "Block settings", + //fixme: This file needs to be renamed since it's inconsistent with other blockeditor file names, probably blockeditor.blocksettings.html + view: "views/propertyeditors/blockeditor/block.settings.html", + size: "small", + submit: function (model) { + block.settings = model; + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + + editorService.open(pickerOptions); + } + } + ]); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.settings.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.settings.html new file mode 100644 index 0000000000..29e2978724 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.settings.html @@ -0,0 +1,20 @@ +
+ + + + + + Add + + +
+
{{model.value | json}}
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.simplelist.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.simplelist.controller.js new file mode 100644 index 0000000000..b8e4d3e34e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockeditor/blockeditor.simplelist.controller.js @@ -0,0 +1,9 @@ +function BlockEditorSimpleListController($scope) { + $scope.sortableOptions = { + axis: "y", + cursor: "move", + handle: ".handle", + tolerance: 'pointer' + }; +} +angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditor.SimpleListController", ['$scope', BlockEditorSimpleListController]); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js index ccc390252b..e5ad83e450 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js @@ -307,22 +307,22 @@ angular.module("umbraco") $scope.openEditorOverlay = function (event, area, index, key) { var title = ""; localizationService.localize("grid_insertControl").then(function (value) { - title = value; - $scope.editorOverlay = { - view: "itempicker", - filter: area.$allowedEditors.length > 15, - title: title, - availableItems: area.$allowedEditors, - event: event, - show: true, - submit: function (model) { - if (model.selectedItem) { - $scope.addControl(model.selectedItem, area, index); - $scope.editorOverlay.show = false; - $scope.editorOverlay = null; + title = value; + $scope.editorOverlay = { + view: "itempicker", + filter: area.$allowedEditors.length > 15, + title: title, + availableItems: area.$allowedEditors, + event: event, + show: true, + submit: function (model) { + if (model.selectedItem) { + $scope.addControl(model.selectedItem, area, index); + $scope.editorOverlay.show = false; + $scope.editorOverlay = null; + } } - } - }; + }; }); }; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js index 7ead8974d8..2fa9244076 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js @@ -18,7 +18,7 @@ $scope.remove = function (index) { $scope.model.value.splice(index, 1); } - + $scope.sortableOptions = { axis: "y", cursor: "move", @@ -184,7 +184,8 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop $scope.overlayMenu.availableItems.push({ alias: scaffold.contentTypeAlias, name: scaffold.contentTypeName, - icon: iconHelper.convertFromLegacyIcon(scaffold.icon) + icon: iconHelper.convertFromLegacyIcon(scaffold.icon), + description: scaffold.documentType.description }); }); @@ -192,7 +193,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop return; } - $scope.overlayMenu.size = $scope.overlayMenu.availableItems.length > 6 ? "medium" : "small"; + $scope.overlayMenu.size = $scope.overlayMenu.availableItems.length > 6 ? "medium" : "small"; $scope.overlayMenu.pasteItems = []; var availableNodesForPaste = clipboardService.retriveDataOfType("elementType", contentTypeAliases); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html index 12c60c3788..9dc89a3675 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html @@ -40,7 +40,8 @@ diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index f647e1ae1e..b97d44d8cc 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -60,7 +60,7 @@ - false + true true diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 4e420206d3..32846b58f6 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -354,12 +354,34 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.NotFound); } + return EmptyContent(parentId, contentType); + } + + /// + /// Gets an empty content item for the + /// + /// + /// + [OutgoingEditorModelEvent] + public ContentItemDisplay GetEmpty(GuidUdi udi, int parentId) + { + var contentType = Services.ContentTypeService.Get(udi.Guid); + if(contentType == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + return EmptyContent(parentId, contentType); + } + + private ContentItemDisplay EmptyContent(int parentId, IContentType contentType) + { var emptyContent = Services.ContentService.Create("", parentId, contentType.Alias, Security.GetUserId().ResultOr(0)); var mapped = MapToDisplay(emptyContent); // translate the content type name if applicable mapped.ContentTypeName = Services.TextService.UmbracoDictionaryTranslate(mapped.ContentTypeName); // if your user type doesn't have access to the Settings section it would not get this property mapped - if (mapped.DocumentType != null) + if(mapped.DocumentType != null) mapped.DocumentType.Name = Services.TextService.UmbracoDictionaryTranslate(mapped.DocumentType.Name); //remove the listview app if it exists diff --git a/src/Umbraco.Web/PropertyEditors/BlockEditorConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockEditorConfiguration.cs new file mode 100644 index 0000000000..daef33eea9 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/BlockEditorConfiguration.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; +using Umbraco.Core; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + public class BlockEditorConfiguration + { + [ConfigurationField("blocks", "Blocks", "views/propertyeditors/blockeditor/blockeditor.settings.html")] + public Block[] Blocks { get; set; } + + [ConfigurationField("view", "View", "textstring", Description = "The path to a custom view for rendering the editor")] + public string View { get; set; } + + public class Block + { + [JsonProperty("elementType")] + public Udi ElementType { get; set; } + + [JsonProperty("settings")] + public BlockSetting Settings { get; set; } + } + + public class BlockSetting + { + [JsonProperty("label")] + public string Label { get; set; } + + [JsonProperty("alias")] + public string Alias { get; set; } + + [JsonProperty("dataType")] + public Udi DataType { get; set; } + } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/BlockEditorConfigurationEditor.cs b/src/Umbraco.Web/PropertyEditors/BlockEditorConfigurationEditor.cs new file mode 100644 index 0000000000..b46e52923b --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/BlockEditorConfigurationEditor.cs @@ -0,0 +1,9 @@ +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + public class BlockEditorConfigurationEditor : ConfigurationEditor + { + + } +} diff --git a/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs index dc46e25f2e..f486af4c3d 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs @@ -4,14 +4,14 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [DataEditor(Constants.PropertyEditors.Aliases.BlockEditor, "Block Editor", ValueType = ValueTypes.Json, Group="rich content")] + [DataEditor(Constants.PropertyEditors.Aliases.BlockEditor, EditorType.PropertyValue , "Block Editor", "blockeditor", ValueType = ValueTypes.Json, Group="rich content", Icon="icon-application-window-alt")] public class BlockEditorPropertyEditor : DataEditor { public BlockEditorPropertyEditor(ILogger logger) : base(logger) { } - protected override IConfigurationEditor CreateConfigurationEditor() => base.CreateConfigurationEditor(); + protected override IConfigurationEditor CreateConfigurationEditor() => new BlockEditorConfigurationEditor(); protected override IDataValueEditor CreateValueEditor() => base.CreateValueEditor(); } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 904a99dbc8..3ed07445bd 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -222,6 +222,8 @@ + +