From c5ba23a15c360eada010553c39809d8dbf26ba1e Mon Sep 17 00:00:00 2001
From: Ronald Barendse
Date: Wed, 13 Oct 2021 13:32:30 +0200
Subject: [PATCH 01/51] Add Constants.Sql.MaxParameterCount
---
src/Umbraco.Core/Constants-Sql.cs | 17 +++++++++++++++++
src/Umbraco.Core/Umbraco.Core.csproj | 3 ++-
2 files changed, 19 insertions(+), 1 deletion(-)
create mode 100644 src/Umbraco.Core/Constants-Sql.cs
diff --git a/src/Umbraco.Core/Constants-Sql.cs b/src/Umbraco.Core/Constants-Sql.cs
new file mode 100644
index 000000000000..366dfbe44ab4
--- /dev/null
+++ b/src/Umbraco.Core/Constants-Sql.cs
@@ -0,0 +1,17 @@
+namespace Umbraco.Core
+{
+ public static partial class Constants
+ {
+ public static class Sql
+ {
+ ///
+ /// The maximum amount of parameters that can be used in a query.
+ ///
+ ///
+ /// The actual limit is 2100 (https://docs.microsoft.com/en-us/sql/sql-server/maximum-capacity-specifications-for-sql-server),
+ /// but we want to ensure there's room for additional parameters if this value is used to create groups/batches.
+ ///
+ public const int MaxParameterCount = 2000;
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 77eeaaa85329..e2679c222351 100755
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -132,6 +132,7 @@
+
@@ -1665,4 +1666,4 @@
-
+
\ No newline at end of file
From a6b04a941ced30381230c3d37e0c667960a2bfda Mon Sep 17 00:00:00 2001
From: Ronald Barendse
Date: Wed, 13 Oct 2021 13:42:18 +0200
Subject: [PATCH 02/51] Replace arbitrary group size with constant
---
.../Persistence/NPocoDatabaseExtensions-Bulk.cs | 2 +-
.../Repositories/Implement/AuditEntryRepository.cs | 2 +-
.../Repositories/Implement/ContentRepositoryBase.cs | 4 ++--
.../Repositories/Implement/ContentTypeRepositoryBase.cs | 8 ++++----
.../Repositories/Implement/DictionaryRepository.cs | 3 +--
.../Repositories/Implement/DocumentRepository.cs | 6 +++---
.../Repositories/Implement/EntityContainerRepository.cs | 2 +-
.../Repositories/Implement/EntityRepository.cs | 2 +-
.../Repositories/Implement/PermissionRepository.cs | 9 ++++-----
.../Repositories/Implement/RedirectUrlRepository.cs | 5 +++--
.../Repositories/Implement/RepositoryBaseOfTIdTEntity.cs | 5 ++---
.../Persistence/Repositories/Implement/TagRepository.cs | 2 +-
src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs | 2 +-
13 files changed, 25 insertions(+), 27 deletions(-)
diff --git a/src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs b/src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs
index 77cc0d66019b..a50e3c2aaa9f 100644
--- a/src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs
+++ b/src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs
@@ -137,7 +137,7 @@ internal static IDbCommand[] GenerateBulkInsertCommands(this IUmbracoDatabase
// Math.Floor(2100 / 8) = 262 record per command
// 4168 / 262 = 15.908... = there will be 16 command in total
// (if we have disabled db parameters, then all records will be included, in only one command)
- var recordsPerCommand = paramsPerRecord == 0 ? int.MaxValue : Convert.ToInt32(Math.Floor(2000.00 / paramsPerRecord));
+ var recordsPerCommand = paramsPerRecord == 0 ? int.MaxValue : Convert.ToInt32(Math.Floor((double)Constants.Sql.MaxParameterCount / paramsPerRecord));
var commandsCount = Convert.ToInt32(Math.Ceiling((double)records.Length / recordsPerCommand));
var commands = new IDbCommand[commandsCount];
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/AuditEntryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/AuditEntryRepository.cs
index c3d34cc3e9f8..d921bb6d5153 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/AuditEntryRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/AuditEntryRepository.cs
@@ -54,7 +54,7 @@ protected override IEnumerable PerformGetAll(params int[] ids)
var entries = new List();
- foreach (var group in ids.InGroupsOf(2000))
+ foreach (var group in ids.InGroupsOf(Constants.Sql.MaxParameterCount))
{
var sql = Sql()
.Select()
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs
index 85814ef681cf..a6b08c2bfd2b 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs
@@ -638,7 +638,7 @@ protected IDictionary GetPropertyCollections(List(versions, 2000, batch =>
+ var allPropertyDataDtos = Database.FetchByGroups(versions, Constants.Sql.MaxParameterCount, batch =>
SqlContext.Sql()
.Select()
.From()
@@ -647,7 +647,7 @@ protected IDictionary GetPropertyCollections(List x.PropertyTypeId).Distinct().ToList();
- var allPropertyTypeDtos = Database.FetchByGroups(allPropertyTypeIds, 2000, batch =>
+ var allPropertyTypeDtos = Database.FetchByGroups(allPropertyTypeIds, Constants.Sql.MaxParameterCount, batch =>
SqlContext.Sql()
.Select(r => r.Select(x => x.DataTypeDto))
.From()
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
index aff53e44b0af..8835f8c647a4 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
@@ -768,7 +768,7 @@ private void CopyTagData(int? sourceLanguageId, int? targetLanguageId, IReadOnly
// note: important to use SqlNullableEquals for nullable types, cannot directly compare language identifiers
var whereInArgsCount = propertyTypeIds.Count + (contentTypeIds?.Count ?? 0);
- if (whereInArgsCount > 2000)
+ if (whereInArgsCount > Constants.Sql.MaxParameterCount)
throw new NotSupportedException("Too many property/content types.");
// delete existing relations (for target language)
@@ -906,7 +906,7 @@ private void CopyPropertyData(int? sourceLanguageId, int? targetLanguageId, IRea
// note: important to use SqlNullableEquals for nullable types, cannot directly compare language identifiers
//
var whereInArgsCount = propertyTypeIds.Count + (contentTypeIds?.Count ?? 0);
- if (whereInArgsCount > 2000)
+ if (whereInArgsCount > Constants.Sql.MaxParameterCount)
throw new NotSupportedException("Too many property/content types.");
//first clear out any existing property data that might already exists under the target language
@@ -1005,7 +1005,7 @@ private void RenormalizeDocumentEditedFlags(IReadOnlyCollection propertyTyp
//based on the current variance of each item to see if it's 'edited' value should be true/false.
var whereInArgsCount = propertyTypeIds.Count + (contentTypeIds?.Count ?? 0);
- if (whereInArgsCount > 2000)
+ if (whereInArgsCount > Constants.Sql.MaxParameterCount)
throw new NotSupportedException("Too many property/content types.");
var propertySql = Sql()
@@ -1095,7 +1095,7 @@ private void RenormalizeDocumentEditedFlags(IReadOnlyCollection propertyTyp
}
//lookup all matching rows in umbracoDocumentCultureVariation
- var docCultureVariationsToUpdate = editedLanguageVersions.InGroupsOf(2000)
+ var docCultureVariationsToUpdate = editedLanguageVersions.InGroupsOf(Constants.Sql.MaxParameterCount)
.SelectMany(_ => Database.Fetch(
Sql().Select().From()
.WhereIn(x => x.LanguageId, editedLanguageVersions.Keys.Select(x => x.langId).ToList())
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DictionaryRepository.cs
index ac1f7c3f2a94..7895b7aa8fc2 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/DictionaryRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DictionaryRepository.cs
@@ -260,8 +260,7 @@ public IEnumerable GetDictionaryItemDescendants(Guid? parentId)
Func>> getItemsFromParents = guids =>
{
- //needs to be in groups of 2000 because we are doing an IN clause and there's a max parameter count that can be used.
- return guids.InGroupsOf(2000)
+ return guids.InGroupsOf(Constants.Sql.MaxParameterCount)
.Select(@group =>
{
var sqlClause = GetBaseQuery(false)
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs
index 5716fbe129c4..753630d186d0 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs
@@ -1342,7 +1342,7 @@ private IDictionary GetContentSchedule(params in
{
var result = new Dictionary();
- var scheduleDtos = Database.FetchByGroups(contentIds, 2000, batch => Sql()
+ var scheduleDtos = Database.FetchByGroups(contentIds, Constants.Sql.MaxParameterCount, batch => Sql()
.Select()
.From()
.WhereIn(x => x.NodeId, batch));
@@ -1391,7 +1391,7 @@ private IDictionary> GetContentVariations(List>();
- var dtos = Database.FetchByGroups(versions, 2000, batch
+ var dtos = Database.FetchByGroups(versions, Constants.Sql.MaxParameterCount, batch
=> Sql()
.Select()
.From()
@@ -1420,7 +1420,7 @@ private IDictionary> GetDocumentVariations(List<
{
var ids = temps.Select(x => x.Id);
- var dtos = Database.FetchByGroups(ids, 2000, batch =>
+ var dtos = Database.FetchByGroups(ids, Constants.Sql.MaxParameterCount, batch =>
Sql()
.Select()
.From()
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityContainerRepository.cs
index 505cbfc816ea..07b82190f5d4 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityContainerRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityContainerRepository.cs
@@ -60,7 +60,7 @@ protected override IEnumerable PerformGetAll(params int[] ids)
{
if (ids.Any())
{
- return Database.FetchByGroups(ids, 2000, batch =>
+ return Database.FetchByGroups(ids, Constants.Sql.MaxParameterCount, batch =>
GetBaseQuery(false)
.Where(x => x.NodeObjectType == NodeObjectTypeId)
.WhereIn(x => x.NodeId, batch))
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs
index a7502a338af8..7bcc5f6b2db4 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs
@@ -281,7 +281,7 @@ private IEnumerable BuildVariants(IEnumerable(v.Select(x => x.Id), 2000, GetVariantInfos);
+ var dtos = Database.FetchByGroups(v.Select(x => x.Id), Constants.Sql.MaxParameterCount, GetVariantInfos);
// group by node id (each group contains all languages)
var xdtos = dtos.GroupBy(x => x.NodeId).ToDictionary(x => x.Key, x => x);
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/PermissionRepository.cs
index 259f0b89c0ab..ce324df6856b 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/PermissionRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/PermissionRepository.cs
@@ -44,7 +44,7 @@ public EntityPermissionCollection GetPermissionsForEntities(int[] groupIds, para
{
var result = new EntityPermissionCollection();
- foreach (var groupOfGroupIds in groupIds.InGroupsOf(2000))
+ foreach (var groupOfGroupIds in groupIds.InGroupsOf(Constants.Sql.MaxParameterCount))
{
//copy local
var localIds = groupOfGroupIds.ToArray();
@@ -64,7 +64,7 @@ public EntityPermissionCollection GetPermissionsForEntities(int[] groupIds, para
else
{
//iterate in groups of 2000 since we don't want to exceed the max SQL param count
- foreach (var groupOfEntityIds in entityIds.InGroupsOf(2000))
+ foreach (var groupOfEntityIds in entityIds.InGroupsOf(Constants.Sql.MaxParameterCount))
{
var ids = groupOfEntityIds;
var sql = Sql()
@@ -133,11 +133,10 @@ public void ReplacePermissions(int groupId, IEnumerable permissions, param
var db = AmbientScope.Database;
- //we need to batch these in groups of 2000 so we don't exceed the max 2100 limit
var sql = "DELETE FROM umbracoUserGroup2NodePermission WHERE userGroupId = @groupId AND nodeId in (@nodeIds)";
- foreach (var idGroup in entityIds.InGroupsOf(2000))
+ foreach (var group in entityIds.InGroupsOf(Constants.Sql.MaxParameterCount))
{
- db.Execute(sql, new { groupId, nodeIds = idGroup });
+ db.Execute(sql, new { groupId, nodeIds = group });
}
var toInsert = new List();
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/RedirectUrlRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/RedirectUrlRepository.cs
index 24c1e31c2015..099d49fbf8ac 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/RedirectUrlRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/RedirectUrlRepository.cs
@@ -37,8 +37,9 @@ protected override IRedirectUrl PerformGet(Guid id)
protected override IEnumerable PerformGetAll(params Guid[] ids)
{
- if (ids.Length > 2000)
- throw new NotSupportedException("This repository does not support more than 2000 ids.");
+ if (ids.Length > Constants.Sql.MaxParameterCount)
+ throw new NotSupportedException($"This repository does not support more than {Constants.Sql.MaxParameterCount} ids.");
+
var sql = GetBaseQuery(false).WhereIn(x => x.Id, ids);
var dtos = Database.Fetch(sql);
return dtos.WhereNotNull().Select(Map);
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs
index 69e4db5940a2..c2aec05fa7e1 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs
@@ -186,14 +186,13 @@ public IEnumerable GetMany(params TId[] ids)
// can't query more than 2000 ids at a time... but if someone is really querying 2000+ entities,
// the additional overhead of fetching them in groups is minimal compared to the lookup time of each group
- const int maxParams = 2000;
- if (ids.Length <= maxParams)
+ if (ids.Length <= Constants.Sql.MaxParameterCount)
{
return CachePolicy.GetAll(ids, PerformGetAll);
}
var entities = new List();
- foreach (var groupOfIds in ids.InGroupsOf(maxParams))
+ foreach (var groupOfIds in ids.InGroupsOf(Constants.Sql.MaxParameterCount))
{
entities.AddRange(CachePolicy.GetAll(groupOfIds.ToArray(), PerformGetAll));
}
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/TagRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/TagRepository.cs
index 279e03b19e37..83170d9dbcb4 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/TagRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/TagRepository.cs
@@ -39,7 +39,7 @@ protected override IEnumerable PerformGetAll(params int[] ids)
{
var dtos = ids.Length == 0
? Database.Fetch(Sql().Select().From())
- : Database.FetchByGroups(ids, 2000, batch => Sql().Select().From().WhereIn(x => x.Id, batch));
+ : Database.FetchByGroups(ids, Constants.Sql.MaxParameterCount, batch => Sql().Select().From().WhereIn(x => x.Id, batch));
return dtos.Select(TagFactory.BuildEntity).ToList();
}
diff --git a/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs
index 619863089847..f66a1ee934d9 100644
--- a/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs
+++ b/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs
@@ -240,7 +240,7 @@ private void Map(IUserGroup source, UserGroupDisplay target, MapperContext conte
// the entity service due to too many Sql parameters.
var list = new List();
- foreach (var idGroup in allContentPermissions.Keys.InGroupsOf(2000))
+ foreach (var idGroup in allContentPermissions.Keys.InGroupsOf(Constants.Sql.MaxParameterCount))
list.AddRange(_entityService.GetAll(UmbracoObjectTypes.Document, idGroup.ToArray()));
contentEntities = list.ToArray();
}
From 88a20cb13cbde845829cbe00efa581f9737713e4 Mon Sep 17 00:00:00 2001
From: Ronald Barendse
Date: Wed, 13 Oct 2021 13:46:51 +0200
Subject: [PATCH 03/51] Minor query cleanup
---
.../Repositories/Implement/DictionaryRepository.cs | 4 ++--
.../Repositories/Implement/MemberRepository.cs | 9 +++------
.../Repositories/Implement/RepositoryBaseOfTIdTEntity.cs | 6 ++++--
3 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DictionaryRepository.cs
index 7895b7aa8fc2..da957a728858 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/DictionaryRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DictionaryRepository.cs
@@ -261,11 +261,11 @@ public IEnumerable GetDictionaryItemDescendants(Guid? parentId)
Func>> getItemsFromParents = guids =>
{
return guids.InGroupsOf(Constants.Sql.MaxParameterCount)
- .Select(@group =>
+ .Select(group =>
{
var sqlClause = GetBaseQuery(false)
.Where(x => x.Parent != null)
- .Where($"{SqlSyntax.GetQuotedColumnName("parent")} IN (@parentIds)", new { parentIds = @group });
+ .WhereIn(x => x.Parent, group);
var translator = new SqlTranslator(sqlClause, Query());
var sql = translator.Translate();
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs
index 62f41aa72759..1982d6ebf921 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs
@@ -431,16 +431,13 @@ public IEnumerable FindMembersInRole(string roleName, string usernameTo
var matchedMembers = Get(query).ToArray();
var membersInGroup = new List();
+
//then we need to filter the matched members that are in the role
- //since the max sql params are 2100 on sql server, we'll reduce that to be safe for potentially other servers and run the queries in batches
- var inGroups = matchedMembers.InGroupsOf(1000);
- foreach (var batch in inGroups)
+ foreach (var group in matchedMembers.Select(x => x.Id).InGroupsOf(Constants.Sql.MaxParameterCount))
{
- var memberIdBatch = batch.Select(x => x.Id);
-
var sql = Sql().SelectAll().From()
.Where(dto => dto.MemberGroup == memberGroup.Id)
- .WhereIn(dto => dto.Member, memberIdBatch);
+ .WhereIn(dto => dto.Member, group);
var memberIdsInGroup = Database.Fetch(sql)
.Select(x => x.Member).ToArray();
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs
index c2aec05fa7e1..5892f2bf4993 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs
@@ -188,14 +188,16 @@ public IEnumerable GetMany(params TId[] ids)
// the additional overhead of fetching them in groups is minimal compared to the lookup time of each group
if (ids.Length <= Constants.Sql.MaxParameterCount)
{
+ // the additional overhead of fetching them in groups is minimal compared to the lookup time of each group
return CachePolicy.GetAll(ids, PerformGetAll);
}
var entities = new List();
- foreach (var groupOfIds in ids.InGroupsOf(Constants.Sql.MaxParameterCount))
+ foreach (var group in ids.InGroupsOf(Constants.Sql.MaxParameterCount))
{
- entities.AddRange(CachePolicy.GetAll(groupOfIds.ToArray(), PerformGetAll));
+ entities.AddRange(CachePolicy.GetAll(group.ToArray(), PerformGetAll));
}
+
return entities;
}
From 1bc3290497626ebe720806c4af1b62820d12a665 Mon Sep 17 00:00:00 2001
From: Ronald Barendse
Date: Wed, 13 Oct 2021 14:10:24 +0200
Subject: [PATCH 04/51] Fix bugs in fetching more than 2000 items (permissions
and culture variations)
---
.../Implement/ContentTypeRepositoryBase.cs | 22 +++++++----
.../Implement/PermissionRepository.cs | 37 +++++++++----------
2 files changed, 31 insertions(+), 28 deletions(-)
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
index 8835f8c647a4..3a74b9209b61 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
@@ -1094,14 +1094,20 @@ private void RenormalizeDocumentEditedFlags(IReadOnlyCollection propertyTyp
}
}
- //lookup all matching rows in umbracoDocumentCultureVariation
- var docCultureVariationsToUpdate = editedLanguageVersions.InGroupsOf(Constants.Sql.MaxParameterCount)
- .SelectMany(_ => Database.Fetch(
- Sql().Select().From()
- .WhereIn(x => x.LanguageId, editedLanguageVersions.Keys.Select(x => x.langId).ToList())
- .WhereIn(x => x.NodeId, editedLanguageVersions.Keys.Select(x => x.nodeId))))
- //convert to dictionary with the same key type
- .ToDictionary(x => (x.NodeId, (int?)x.LanguageId), x => x);
+ // lookup all matching rows in umbracoDocumentCultureVariation
+ // fetch in batches to account for maximum parameter count (distinct languages can't exceed 2000)
+ var languageIds = editedLanguageVersions.Keys.Select(x => x.langId).Distinct().ToArray();
+ var nodeIds = editedLanguageVersions.Keys.Select(x => x.nodeId).Distinct();
+ var docCultureVariationsToUpdate = nodeIds.InGroupsOf(Constants.Sql.MaxParameterCount - languageIds.Length)
+ .SelectMany(group =>
+ {
+ var sql = Sql().Select().From()
+ .WhereIn(x => x.LanguageId, languageIds)
+ .WhereIn(x => x.NodeId, group);
+
+ return Database.Fetch(sql);
+ })
+ .ToDictionary(x => (x.NodeId, (int?)x.LanguageId), x => x); //convert to dictionary with the same key type
var toUpdate = new List();
foreach (var ev in editedLanguageVersions)
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/PermissionRepository.cs
index ce324df6856b..735496a0d187 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/PermissionRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/PermissionRepository.cs
@@ -38,44 +38,41 @@ public PermissionRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogg
///
///
///
- /// This method will not support passing in more than 2000 group Ids
+ /// This method will not support passing in more than 2000 group IDs when also passing in entity IDs.
///
public EntityPermissionCollection GetPermissionsForEntities(int[] groupIds, params int[] entityIds)
{
var result = new EntityPermissionCollection();
- foreach (var groupOfGroupIds in groupIds.InGroupsOf(Constants.Sql.MaxParameterCount))
+ if (entityIds.Length == 0)
{
- //copy local
- var localIds = groupOfGroupIds.ToArray();
-
- if (entityIds.Length == 0)
+ foreach (var group in groupIds.InGroupsOf(Constants.Sql.MaxParameterCount))
{
var sql = Sql()
.SelectAll()
.From()
- .Where(dto => localIds.Contains(dto.UserGroupId));
+ .Where(dto => group.Contains(dto.UserGroupId));
+
var permissions = AmbientScope.Database.Fetch(sql);
foreach (var permission in ConvertToPermissionList(permissions))
{
result.Add(permission);
}
}
- else
+ }
+ else
+ {
+ foreach (var group in entityIds.InGroupsOf(Constants.Sql.MaxParameterCount - groupIds.Length))
{
- //iterate in groups of 2000 since we don't want to exceed the max SQL param count
- foreach (var groupOfEntityIds in entityIds.InGroupsOf(Constants.Sql.MaxParameterCount))
+ var sql = Sql()
+ .SelectAll()
+ .From()
+ .Where(dto => groupIds.Contains(dto.UserGroupId) && group.Contains(dto.NodeId));
+
+ var permissions = AmbientScope.Database.Fetch(sql);
+ foreach (var permission in ConvertToPermissionList(permissions))
{
- var ids = groupOfEntityIds;
- var sql = Sql()
- .SelectAll()
- .From()
- .Where(dto => localIds.Contains(dto.UserGroupId) && ids.Contains(dto.NodeId));
- var permissions = AmbientScope.Database.Fetch(sql);
- foreach (var permission in ConvertToPermissionList(permissions))
- {
- result.Add(permission);
- }
+ result.Add(permission);
}
}
}
From 60be71d1a0037ffb5556511342065f85afce2825 Mon Sep 17 00:00:00 2001
From: Ronald Barendse
Date: Wed, 13 Oct 2021 23:34:50 +0200
Subject: [PATCH 05/51] Remove duplicated comment
---
.../Repositories/Implement/RepositoryBaseOfTIdTEntity.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs
index 5892f2bf4993..a7704272a85f 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/RepositoryBaseOfTIdTEntity.cs
@@ -188,7 +188,6 @@ public IEnumerable GetMany(params TId[] ids)
// the additional overhead of fetching them in groups is minimal compared to the lookup time of each group
if (ids.Length <= Constants.Sql.MaxParameterCount)
{
- // the additional overhead of fetching them in groups is minimal compared to the lookup time of each group
return CachePolicy.GetAll(ids, PerformGetAll);
}
From b13f2603412dc62536d759ab67ae4ed7e4f7feb2 Mon Sep 17 00:00:00 2001
From: Patrick de Mooij
Date: Mon, 11 Oct 2021 21:15:25 +0200
Subject: [PATCH 06/51] 11343: Remove blocklist block on cancel
---
.../blocklist/umbBlockListPropertyEditor.component.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js
index 7334fbeadf5e..11ef37029c54 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbBlockListPropertyEditor.component.js
@@ -65,6 +65,9 @@
vm.layout = []; // The layout object specific to this Block Editor, will be a direct reference from Property Model.
vm.availableBlockTypes = []; // Available block entries of this property editor.
vm.labels = {};
+ vm.options = {
+ createFlow: false
+ };
localizationService.localizeMany(["grid_addElement", "content_createEmpty"]).then(function (data) {
vm.labels.grid_addElement = data[0];
@@ -380,7 +383,7 @@
function editBlock(blockObject, openSettings, blockIndex, parentForm, options) {
- options = options || {};
+ options = options || vm.options;
// this must be set
if (blockIndex === undefined) {
@@ -560,7 +563,9 @@
if (inlineEditing === true) {
blockObject.activate();
} else if (inlineEditing === false && blockObject.hideContentInOverlay !== true) {
+ vm.options.createFlow = true;
blockObject.edit();
+ vm.options.createFlow = false;
}
}
}
From 809671b9caa4355d086afb8abeaec041287640f4 Mon Sep 17 00:00:00 2001
From: patrickdemooij9
Date: Thu, 21 Oct 2021 02:09:08 +0200
Subject: [PATCH 07/51] Align template picking more towards the other pickers
(#11363)
* Align template picking more towards the other pickers
* style/cleanup - remove unused args, updated openItemPicker function name to openTemplatePicker since that's what we're opening
Co-authored-by: Nathan Woulfe
---
.../components/umbgridselector.directive.js | 49 ++++++++++---------
.../src/common/services/editor.service.js | 25 +++++++++-
.../treepicker/treepicker.controller.js | 3 ++
.../views/templates/templates.controller.js | 26 +++++++++-
.../views/templates/templates.html | 3 +-
.../src/views/templates/edit.controller.js | 26 +++++-----
src/Umbraco.Web/Editors/EntityController.cs | 18 +++++++
7 files changed, 110 insertions(+), 40 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgridselector.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgridselector.directive.js
index bf03749faa7c..4c628391cb97 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgridselector.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgridselector.directive.js
@@ -1,9 +1,9 @@
(function () {
'use strict';
- function GridSelector($location, overlayService, editorService) {
+ function GridSelector(overlayService, editorService) {
- function link(scope, el, attr, ctrl) {
+ function link(scope) {
var eventBindings = [];
scope.dialogModel = {};
@@ -33,26 +33,30 @@
};
scope.openItemPicker = function ($event) {
- var dialogModel = {
- view: "itempicker",
- title: "Choose " + scope.itemLabel,
- availableItems: scope.availableItems,
- selectedItems: scope.selectedItems,
- position: "target",
- event: $event,
- submit: function (model) {
- scope.selectedItems.push(model.selectedItem);
- // if no default item - set item as default
- if (scope.defaultItem === null) {
- scope.setAsDefaultItem(model.selectedItem);
+ if (scope.itemPicker) {
+ scope.itemPicker();
+ } else {
+ var dialogModel = {
+ view: "itempicker",
+ title: "Choose " + scope.itemLabel,
+ availableItems: scope.availableItems,
+ selectedItems: scope.selectedItems,
+ position: "target",
+ event: $event,
+ submit: function (model) {
+ scope.selectedItems.push(model.selectedItem);
+ // if no default item - set item as default
+ if (scope.defaultItem === null) {
+ scope.setAsDefaultItem(model.selectedItem);
+ }
+ overlayService.close();
+ },
+ close: function () {
+ overlayService.close();
}
- overlayService.close();
- },
- close: function() {
- overlayService.close();
- }
- };
- overlayService.open(dialogModel);
+ };
+ overlayService.open(dialogModel);
+ }
};
scope.openTemplate = function (selectedItem) {
@@ -156,7 +160,8 @@
availableItems: "=",
defaultItem: "=",
itemName: "@",
- updatePlaceholder: "="
+ updatePlaceholder: "=",
+ itemPicker: "="
},
link: link
};
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js
index b917d6fa4f33..a68f68fbb35f 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js
@@ -179,7 +179,7 @@ When building a custom infinite editor view you can use the same components as a
} else {
focus();
}
- });
+ });
/**
* @ngdoc method
@@ -928,6 +928,28 @@ When building a custom infinite editor view you can use the same components as a
open(editor);
}
+ /**
+ * @ngdoc method
+ * @name umbraco.services.editorService#templatePicker
+ * @methodOf umbraco.services.editorService
+ *
+ * @description
+ * Opens a template picker in infinite editing, the submit callback returns an array of selected items.
+ *
+ * @param {object} editor rendering options.
+ * @param {boolean} editor.multiPicker Pick one or multiple items.
+ * @param {function} editor.submit Callback function when the submit button is clicked. Returns the editor model object.
+ * @param {function} editor.close Callback function when the close button is clicked.
+ * @returns {object} editor object.
+ */
+ function templatePicker(editor) {
+ editor.view = "views/common/infiniteeditors/treepicker/treepicker.html";
+ if (!editor.size) editor.size = "small";
+ editor.section = "settings";
+ editor.treeAlias = "templates";
+ open(editor);
+ }
+
/**
* @ngdoc method
* @name umbraco.services.editorService#macroPicker
@@ -1088,6 +1110,7 @@ When building a custom infinite editor view you can use the same components as a
templateSections: templateSections,
userPicker: userPicker,
itemPicker: itemPicker,
+ templatePicker: templatePicker,
macroPicker: macroPicker,
memberGroupPicker: memberGroupPicker,
memberPicker: memberPicker,
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js
index ed5c4096bcca..72eb504c600d 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js
@@ -141,6 +141,9 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController",
});
}
}
+ else if (vm.treeAlias === "templates") {
+ vm.entityType = "Template";
+ }
// TODO: Seems odd this logic is here, i don't think it needs to be and should just exist on the property editor using this
if ($scope.model.minNumber) {
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/templates/templates.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/templates/templates.controller.js
index e2a964c29365..46e856410d94 100644
--- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/templates/templates.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/templates/templates.controller.js
@@ -9,7 +9,7 @@
(function () {
'use strict';
- function TemplatesController($scope, entityResource, contentTypeHelper, templateResource, contentTypeResource, $routeParams) {
+ function TemplatesController($scope, entityResource, contentTypeHelper, contentTypeResource, editorService, $routeParams) {
/* ----------- SCOPE VARIABLES ----------- */
@@ -22,6 +22,7 @@
vm.isElement = $scope.model.isElement;
vm.createTemplate = createTemplate;
+ vm.openTemplatePicker = openTemplatePicker;
/* ---------- INIT ---------- */
@@ -81,6 +82,29 @@
vm.canCreateTemplate = existingTemplate ? false : true;
}
+ function openTemplatePicker() {
+ const editor = {
+ title: "Choose template",
+ filterCssClass: 'not-allowed',
+ multiPicker: true,
+ filter: item => {
+ return !vm.availableTemplates.some(template => template.id == item.id) ||
+ $scope.model.allowedTemplates.some(template => template.id == item.id);
+ },
+ submit: model => {
+ model.selection.forEach(item => {
+ $scope.model.allowedTemplates.push(item);
+ });
+ editorService.close();
+ },
+ close: function() {
+ editorService.close();
+ }
+ }
+
+ editorService.templatePicker(editor);
+ }
+
var unbindWatcher = $scope.$watch("model.isElement",
function(newValue, oldValue) {
vm.isElement = newValue;
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/templates/templates.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/templates/templates.html
index 279ffb73c0e4..04fd61be3cd7 100644
--- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/templates/templates.html
+++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/templates/templates.html
@@ -17,7 +17,8 @@
item-name="template"
name="model.name"
alias="model.alias"
- update-placeholder="vm.updateTemplatePlaceholder">
+ update-placeholder="vm.updateTemplatePlaceholder"
+ item-picker="vm.openTemplatePicker">
{
+ const editor = {
+ title,
+ filterCssClass: 'not-allowed',
+ filter: item => !availableMasterTemplates.some(template => template.id == item.id),
+ submit: model => {
+ var template = model.selection[0];
if (template && template.alias) {
vm.template.masterTemplateAlias = template.alias;
setLayout(template.alias + ".cshtml");
@@ -575,14 +575,10 @@
}
editorService.close();
},
- close: function (oldModel) {
- // close dialog
- editorService.close();
- // focus editor
- vm.editor.focus();
- }
- };
- editorService.itemPicker(masterTemplate);
+ close: () => editorService.close(),
+ }
+
+ editorService.templatePicker(editor);
});
}
diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs
index 0b6273e79da8..573246e1f063 100644
--- a/src/Umbraco.Web/Editors/EntityController.cs
+++ b/src/Umbraco.Web/Editors/EntityController.cs
@@ -997,6 +997,15 @@ private EntityBasic GetResultForKey(Guid key, UmbracoEntityTypes entityType)
case UmbracoEntityTypes.Macro:
+ case UmbracoEntityTypes.Template:
+ var template = Services.FileService.GetTemplate(key);
+ if (template is null)
+ {
+ throw new HttpResponseException(HttpStatusCode.NotFound);
+ }
+
+ return Mapper.Map(template);
+
default:
throw new NotSupportedException("The " + typeof(EntityController) + " does not currently support data for the type " + entityType);
}
@@ -1029,6 +1038,15 @@ private EntityBasic GetResultForId(int id, UmbracoEntityTypes entityType)
case UmbracoEntityTypes.Macro:
+ case UmbracoEntityTypes.Template:
+ var template = Services.FileService.GetTemplate(id);
+ if (template is null)
+ {
+ throw new HttpResponseException(HttpStatusCode.NotFound);
+ }
+
+ return Mapper.Map(template);
+
default:
throw new NotSupportedException("The " + typeof(EntityController) + " does not currently support data for the type " + entityType);
}
From aa6c5d48a2ec61bc19dc7d00d67e004c926dcc94 Mon Sep 17 00:00:00 2001
From: BatJan <1932158+BatJan@users.noreply.github.com>
Date: Wed, 20 Oct 2021 22:30:31 +0200
Subject: [PATCH 08/51] Add fallback text
---
.../views/propertyeditors/grid/overlays/rowdeleteconfirm.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/overlays/rowdeleteconfirm.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/overlays/rowdeleteconfirm.html
index 2ba56a5b8832..ded2e5b9408f 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/overlays/rowdeleteconfirm.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/overlays/rowdeleteconfirm.html
@@ -11,6 +11,6 @@
- ?
+ Are you sure you want to delete?
From 00d7422d2fe894810c50a242b3cf07e020a4f678 Mon Sep 17 00:00:00 2001
From: Paul Seal
Date: Fri, 22 Oct 2021 02:47:35 +0100
Subject: [PATCH 09/51] V8/feature/app header localized titles (#11429)
* Added a link to 404 documentation
I think it will be useful to include a link to the documentation on how to create a custom 404
* added localized titles to the search and help buttons in the app header (top right of Umbraco)
---
.../src/views/components/application/umb-app-header.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html
index 3ad4ebc18815..e0fb4aeb7793 100644
--- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html
+++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html
@@ -10,7 +10,7 @@
-
-
+ Open/Close backoffice help...
From 79e820555686f4ef7731a8b0a1987aa0be983219 Mon Sep 17 00:00:00 2001
From: BatJan <1932158+BatJan@users.noreply.github.com>
Date: Fri, 22 Oct 2021 08:31:13 +0200
Subject: [PATCH 10/51] Remove comma
---
.../src/views/templates/edit.controller.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js
index 5a0f49834af5..856886a8701a 100644
--- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js
@@ -575,7 +575,7 @@
}
editorService.close();
},
- close: () => editorService.close(),
+ close: () => editorService.close()
}
editorService.templatePicker(editor);
From 76879caaaae85a06ec21ae370fab5e236c3e0cc6 Mon Sep 17 00:00:00 2001
From: Paul Johnson
Date: Mon, 25 Oct 2021 16:31:16 +0100
Subject: [PATCH 11/51] Prevent github actions spam
---
.github/{workflows => config}/codeql-config.yml | 0
.github/workflows/codeql-analysis.yml | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
rename .github/{workflows => config}/codeql-config.yml (100%)
diff --git a/.github/workflows/codeql-config.yml b/.github/config/codeql-config.yml
similarity index 100%
rename from .github/workflows/codeql-config.yml
rename to .github/config/codeql-config.yml
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 3d08bf3a4b27..ee912262d7fb 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -56,7 +56,7 @@ jobs:
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
with:
- config-file: ./.github/workflows/codeql-config.yml
+ config-file: ./.github/config/codeql-config.yml
# This job is to prevent the workflow status from showing as failed when all other jobs are skipped - See https://github.community/t/workflow-is-failing-if-no-job-can-be-ran-due-to-condition/16873
always_job:
From 45dec5c181714c159f1776a35953d8688d986c45 Mon Sep 17 00:00:00 2001
From: BatJan <1932158+BatJan@users.noreply.github.com>
Date: Sat, 23 Oct 2021 14:44:24 +0200
Subject: [PATCH 12/51] Add missing fallack values
---
.../src/views/users/views/users/users.html | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html
index 29be78241535..5569e0a985be 100644
--- a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html
+++ b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html
@@ -181,7 +181,7 @@
-
+ Sorry, we can not find what you are looking for.
@@ -305,7 +305,7 @@
Invite User
-
+ Invite new users to give them access to Umbraco. An invite email will be sent to the user with information on how to log in to Umbraco. Invites last for 72 hours.
@@ -313,7 +313,7 @@
Create user
-
+ Create new users to give them access to Umbraco. When a new user is created a password will be generated that you can share with the user.
@@ -437,7 +437,7 @@
-
+
The new user has successfully been created. To log in to Umbraco use the password below.
@@ -520,7 +520,7 @@
-
+
An invitation has been sent to the new user with details about how to log in to Umbraco.
From 123bceef085caa48817f7d3c18042f4d418a9471 Mon Sep 17 00:00:00 2001
From: Erik-Jan Westendorp
Date: Tue, 12 Oct 2021 03:50:17 +0200
Subject: [PATCH 13/51] Localization add tab text dutch (#11346)
* Update nl.xml add addTab key
* tab to tablad
* Add translation to shortcuts sections
(cherry picked from commit d22297270beacc9364b6eec39dd04f102215f010)
---
src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml
index c8f39cf02081..1f27ef5fc7b8 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml
@@ -791,6 +791,7 @@
Blauw
+ Tabblad toevoegenGroep toevoegenEigenschap toevoegenEditor toevoegen
@@ -1530,6 +1531,7 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
Een documenttype kan niet worden gewijzigd in een elementtype nadat het is gebruikt om een of meer contentitems te maken.Dit is niet van toepassing op een elementtypeJe hebt wijzigingen aangebracht aan deze eigenschap. Ben je zeker dat je ze wil weggooien?
+ Tabblad toevoegenTaal toevoegen
From f9ed371cb779a13f49d15d6bf5170054f5c50bed Mon Sep 17 00:00:00 2001
From: BatJan <1932158+BatJan@users.noreply.github.com>
Date: Sat, 23 Oct 2021 15:16:20 +0200
Subject: [PATCH 14/51] Add missing fallback texts
---
.../views/propertyeditors/contentpicker/contentpicker.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html
index e93637c67142..fa148ecfc725 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html
@@ -1,7 +1,7 @@
-
-
+
You have picked a content item currently deleted or in the recycle bin
+
You have picked content items currently deleted or in the recycle bin
+ The selected page in the content tree doesn't allow for any pages to be created below it.
+
-
+ There are no allowed Document Types available for creating content here. You must enable these in Document
+ Types within the Settings section, by editing the Allowed child node
+ types under Permissions.
-
+ Edit permissions for this Document Type
-
+ There are no allowed Document Types available for creating content here. You must enable these in Document
+ Types within the Settings section, by changing the Allow as root option under Permissions.
-
+ There are no Document Types available for creating content here. You must create these in Document
+ Types within the Settings section.
-
+ Valid domain names are: "example.com", "www.example.com", "example.com:8080", or "https://www.example.com/".
+ Furthermore also one-level paths in domains are supported, eg. "example.com/en" or "/en".
-
+ Domain*
-
+ Language*
@@ -49,9 +50,9 @@
Domains
-
+ Value cannot be empty
- ({{domain.other}})
+ Domain has already been assigned.({{domain.other}})