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 @@
+