diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec
index a223704617..db34b5e984 100644
--- a/build/NuSpecs/UmbracoCms.nuspec
+++ b/build/NuSpecs/UmbracoCms.nuspec
@@ -26,8 +26,8 @@
not want this to happen as the alpha of the next major is, really, the next major already.
-->
-
-
+
+
diff --git a/build/NuSpecs/tools/Web.config.cloud.xdt b/build/NuSpecs/tools/Web.config.cloud.xdt
new file mode 100644
index 0000000000..988c741126
--- /dev/null
+++ b/build/NuSpecs/tools/Web.config.cloud.xdt
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/build.ps1 b/build/build.ps1
index 15d455b976..55ceb774ee 100644
--- a/build/build.ps1
+++ b/build/build.ps1
@@ -91,6 +91,7 @@
{
$src = "$($this.SolutionRoot)\src"
$log = "$($this.BuildTemp)\belle.log"
+
Write-Host "Compile Belle"
Write-Host "Logging to $log"
@@ -352,6 +353,14 @@
"-x!dotless.Core.*" "-x!Content_Types.xml" "-x!*.pdb" `
> $null
if (-not $?) { throw "Failed to zip UmbracoCms." }
+
+ Write-Host "Zip cms cloud"
+ $this.CopyFile("$($this.SolutionRoot)\build\NuSpecs\tools\Web.config.cloud.xdt", "$tmp\WebApp\Web.config.install.xdt")
+ &$this.BuildEnv.Zip a -r "$out\UmbracoCms.$($this.Version.Semver).Cloud.zip" `
+ "$tmp\WebApp\*" `
+ "-x!dotless.Core.*" "-x!Content_Types.xml" "-x!*.pdb" `
+ > $null
+ if (-not $?) { throw "Failed to zip UmbracoCms." }
})
$ubuild.DefineMethod("PrepareBuild",
@@ -553,7 +562,6 @@
# run
if (-not $get)
{
-cd
if ($command.Length -eq 0)
{
$command = @( "Build" )
diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs
index 2dac2d8791..56e0376196 100644
--- a/src/SolutionInfo.cs
+++ b/src/SolutionInfo.cs
@@ -18,5 +18,5 @@ using System.Resources;
[assembly: AssemblyVersion("8.0.0")]
// these are FYI and changed automatically
-[assembly: AssemblyFileVersion("8.15.1")]
-[assembly: AssemblyInformationalVersion("8.15.1")]
+[assembly: AssemblyFileVersion("8.16.0")]
+[assembly: AssemblyInformationalVersion("8.16.0-rc")]
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.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
index 011b7e019f..024ce07c5f 100644
--- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
+++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
@@ -348,9 +348,9 @@
False
True
- 8151
+ 8160
/
- http://localhost:8151
+ http://localhost:8160
False
False
diff --git a/src/Umbraco.Web.UI/config/BackOfficeTours/getting-started.json b/src/Umbraco.Web.UI/config/BackOfficeTours/getting-started.json
index 75797f78e7..01defd7e10 100644
--- a/src/Umbraco.Web.UI/config/BackOfficeTours/getting-started.json
+++ b/src/Umbraco.Web.UI/config/BackOfficeTours/getting-started.json
@@ -272,9 +272,9 @@
"view": "nodename"
},
{
- "element": "[data-element='editor-content'] [data-element='property-welcomeText']",
+ "element": "[data-element='editor-content'] [data-element='property-welcomeText'] > div",
"title": "Add a welcome text",
- "content": "Add content to the Welcome Text field.
If you don't have any ideas here is a start:
I am learning Umbraco. High Five I Rock #H5IR
.
"
+ "content": "Add content to the Welcome Text field.
If you don't have any ideas here is a start:
I am learning Umbraco. High Five I Rock #H5IR
"
},
{
"element": "[data-element='editor-content'] [data-element='button-saveAndPublish']",
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 @@
+
diff --git a/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs b/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs
index e2a6f155d0..25585ca0cb 100644
--- a/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs
+++ b/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs
@@ -1,10 +1,8 @@
-using System;
+using System.Collections;
using System.Net.Http;
using System.Web.Http.Filters;
-using Umbraco.Core;
using Umbraco.Web.Composing;
using Umbraco.Web.Editors;
-using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.WebApi.Filters
{
@@ -23,18 +21,37 @@ namespace Umbraco.Web.WebApi.Filters
if (actionExecutedContext.Response.Content is ObjectContent objectContent)
{
var model = objectContent.Value;
-
if (model != null)
{
- var args = new EditorModelEventArgs(
- model,
- Current.UmbracoContext);
- EditorModelEventManager.EmitEvent(actionExecutedContext, args);
- objectContent.Value = args.Model;
+ if (model is IDictionary modelDict)
+ {
+ foreach (var entity in modelDict)
+ {
+ if (entity is DictionaryEntry entry)
+ {
+ var args = CreateArgs(entry.Value);
+ EditorModelEventManager.EmitEvent(actionExecutedContext, args);
+ entry.Value = args.Model;
+ }
+ }
+ }
+ else
+ {
+ var args = CreateArgs(model);
+ EditorModelEventManager.EmitEvent(actionExecutedContext, args);
+ objectContent.Value = args.Model;
+ }
}
}
base.OnActionExecuted(actionExecutedContext);
}
+
+ private EditorModelEventArgs CreateArgs(object model)
+ {
+ return new EditorModelEventArgs(
+ model,
+ Current.UmbracoContext);
+ }
}
}