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 a33e85c9a2..e996ef4111 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 @@ -642,6 +642,24 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { return $q.when(umbDataFormatter.formatContentGetData(result)); }); }, + + getScaffolds: function(parentId, aliases){ + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetEmptyByAliases"), + { parentId: parentId, contentTypeAliases: aliases } + ), + 'Failed to retrieve data for empty content item aliases ' + aliases.join(", ") + ).then(function(result) { + Object.keys(result).map(function(key){ + result[key] = umbDataFormatter.formatContentGetData(result[key]); + }); + + return $q.when(result); + }); + }, /** * @ngdoc method * @name umbraco.resources.contentResource#getScaffoldByKey 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 446fb8c076..64fc40d84d 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 @@ -522,10 +522,14 @@ ]; // Initialize - var scaffoldsLoaded = 0; vm.scaffolds = []; - _.each(model.config.contentTypes, function (contentType) { - contentResource.getScaffold(-20, contentType.ncAlias).then(function (scaffold) { + + contentResource.getScaffolds(-20, contentTypeAliases).then(function (scaffolds){ + // Loop through all the content types + _.each(model.config.contentTypes, function (contentType){ + // Get the scaffold from the result + var scaffold = scaffolds[contentType.ncAlias]; + // make sure it's an element type before allowing the user to create new ones if (scaffold.isElement) { // remove all tabs except the specified tab @@ -554,13 +558,10 @@ // Store the scaffold object vm.scaffolds.push(scaffold); } - - scaffoldsLoaded++; - initIfAllScaffoldsHaveLoaded(); - }, function (error) { - scaffoldsLoaded++; - initIfAllScaffoldsHaveLoaded(); }); + + // Initialize once all scaffolds have been loaded + initNestedContent(); }); /** @@ -586,57 +587,50 @@ }); } - var initIfAllScaffoldsHaveLoaded = function () { + var initNestedContent = function () { // Initialize when all scaffolds have loaded - if (model.config.contentTypes.length === scaffoldsLoaded) { - // Because we're loading the scaffolds async one at a time, we need to - // sort them explicitly according to the sort order defined by the data type. - contentTypeAliases = []; - _.each(model.config.contentTypes, function (contentType) { - contentTypeAliases.push(contentType.ncAlias); - }); - vm.scaffolds = $filter("orderBy")(vm.scaffolds, function (s) { - return contentTypeAliases.indexOf(s.contentTypeAlias); - }); + // Sort the scaffold explicitly according to the sort order defined by the data type. + vm.scaffolds = $filter("orderBy")(vm.scaffolds, function (s) { + return contentTypeAliases.indexOf(s.contentTypeAlias); + }); - // Convert stored nodes - if (model.value) { - for (var i = 0; i < model.value.length; i++) { - var item = model.value[i]; - var scaffold = getScaffold(item.ncContentTypeAlias); - if (scaffold == null) { - // No such scaffold - the content type might have been deleted. We need to skip it. - continue; - } - createNode(scaffold, item); + // Convert stored nodes + if (model.value) { + for (var i = 0; i < model.value.length; i++) { + var item = model.value[i]; + var scaffold = getScaffold(item.ncContentTypeAlias); + if (scaffold == null) { + // No such scaffold - the content type might have been deleted. We need to skip it. + continue; } + createNode(scaffold, item); } - - // Enforce min items if we only have one scaffold type - var modelWasChanged = false; - if (vm.nodes.length < vm.minItems && vm.scaffolds.length === 1) { - for (var i = vm.nodes.length; i < model.config.minItems; i++) { - addNode(vm.scaffolds[0].contentTypeAlias); - } - modelWasChanged = true; - } - - // If there is only one item, set it as current node - if (vm.singleMode || (vm.nodes.length === 1 && vm.maxItems === 1)) { - setCurrentNode(vm.nodes[0], false); - } - - validate(); - - vm.inited = true; - - if (modelWasChanged) { - updateModel(); - } - - updatePropertyActionStates(); - checkAbilityToPasteContent(); } + + // Enforce min items if we only have one scaffold type + var modelWasChanged = false; + if (vm.nodes.length < vm.minItems && vm.scaffolds.length === 1) { + for (var i = vm.nodes.length; i < model.config.minItems; i++) { + addNode(vm.scaffolds[0].contentTypeAlias); + } + modelWasChanged = true; + } + + // If there is only one item, set it as current node + if (vm.singleMode || (vm.nodes.length === 1 && vm.maxItems === 1)) { + setCurrentNode(vm.nodes[0], false); + } + + validate(); + + vm.inited = true; + + if (modelWasChanged) { + updateModel(); + } + + updatePropertyActionStates(); + checkAbilityToPasteContent(); } function extendPropertyWithNCData(prop) { diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 8293d1274f..d7f1d81c77 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -364,6 +364,24 @@ namespace Umbraco.Web.Editors return GetEmpty(contentType, parentId); } + /// + /// Gets a dictionary containing empty content items for every alias specified in the contentTypeAliases array in the body of the request. + /// + /// + /// This is a post request in order to support a large amount of aliases without hitting the URL length limit. + /// + /// + /// + [OutgoingEditorModelEvent] + [HttpPost] + public IDictionary GetEmptyByAliases(ContentTypesByAliases contentTypesByAliases) + { + // It's important to do this operation within a scope to reduce the amount of readlock queries. + using var scope = _scopeProvider.CreateScope(autoComplete: true); + var contentTypes = contentTypesByAliases.ContentTypeAliases.Select(alias => Services.ContentTypeService.Get(alias)); + return GetEmpties(contentTypes, contentTypesByAliases.ParentId).ToDictionary(x => x.ContentTypeAlias); + } + /// /// Gets an empty content item for the document type. diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypesByAliases.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypesByAliases.cs new file mode 100644 index 0000000000..dd1185b304 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypesByAliases.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + /// + /// A model for retrieving multiple content types based on their aliases. + /// + [DataContract(Name = "contentTypes", Namespace = "")] + public class ContentTypesByAliases + { + /// + /// Id of the parent of the content type. + /// + [DataMember(Name = "parentId")] + [Required] + public int ParentId { get; set; } + + /// + /// The alias of every content type to get. + /// + [DataMember(Name = "contentTypeAliases")] + [Required] + public string[] ContentTypeAliases { get; set; } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 68bcade20c..2340228eae 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -253,6 +253,7 @@ +