diff --git a/Directory.Packages.props b/Directory.Packages.props
index fe1b24d22d..5bcf6f8e8a 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -72,7 +72,7 @@
-
+
diff --git a/src/Umbraco.Cms.Imaging.ImageSharp2/Umbraco.Cms.Imaging.ImageSharp2.csproj b/src/Umbraco.Cms.Imaging.ImageSharp2/Umbraco.Cms.Imaging.ImageSharp2.csproj
index 1dcba40ab6..260c5bec53 100644
--- a/src/Umbraco.Cms.Imaging.ImageSharp2/Umbraco.Cms.Imaging.ImageSharp2.csproj
+++ b/src/Umbraco.Cms.Imaging.ImageSharp2/Umbraco.Cms.Imaging.ImageSharp2.csproj
@@ -7,6 +7,8 @@
+
+
diff --git a/src/Umbraco.Cms.Persistence.EFCore/Locking/SqlServerEFCoreDistributedLockingMechanism.cs b/src/Umbraco.Cms.Persistence.EFCore/Locking/SqlServerEFCoreDistributedLockingMechanism.cs
index ef9b9443ae..4652c513a3 100644
--- a/src/Umbraco.Cms.Persistence.EFCore/Locking/SqlServerEFCoreDistributedLockingMechanism.cs
+++ b/src/Umbraco.Cms.Persistence.EFCore/Locking/SqlServerEFCoreDistributedLockingMechanism.cs
@@ -136,9 +136,7 @@ internal class SqlServerEFCoreDistributedLockingMechanism : IDistributedLocki
"A transaction with minimum ReadCommitted isolation level is required.");
}
- await dbContext.Database.ExecuteSqlRawAsync($"SET LOCK_TIMEOUT {(int)_timeout.TotalMilliseconds};");
-
- var number = await dbContext.Database.ExecuteScalarAsync($"SELECT value FROM dbo.umbracoLock WITH (REPEATABLEREAD) WHERE id={LockId}");
+ var number = await dbContext.Database.ExecuteScalarAsync($"SET LOCK_TIMEOUT {(int)_timeout.TotalMilliseconds};SELECT value FROM dbo.umbracoLock WITH (REPEATABLEREAD) WHERE id={LockId}");
if (number == null)
{
diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDistributedLockingMechanism.cs b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDistributedLockingMechanism.cs
index cc2e1b5feb..77975e8f31 100644
--- a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDistributedLockingMechanism.cs
+++ b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDistributedLockingMechanism.cs
@@ -143,9 +143,10 @@ public class SqlServerDistributedLockingMechanism : IDistributedLockingMechanism
const string query = "SELECT value FROM umbracoLock WITH (REPEATABLEREAD) WHERE id=@id";
- db.Execute("SET LOCK_TIMEOUT " + _timeout.TotalMilliseconds + ";");
+ var lockTimeoutQuery = $"SET LOCK_TIMEOUT {_timeout.TotalMilliseconds}";
- var i = db.ExecuteScalar(query, new { id = LockId });
+ // execute the lock timeout query and the actual query in a single server roundtrip
+ var i = db.ExecuteScalar($"{lockTimeoutQuery};{query}", new { id = LockId });
if (i == null)
{
@@ -178,9 +179,10 @@ public class SqlServerDistributedLockingMechanism : IDistributedLockingMechanism
const string query =
@"UPDATE umbracoLock WITH (REPEATABLEREAD) SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id";
- db.Execute("SET LOCK_TIMEOUT " + _timeout.TotalMilliseconds + ";");
+ var lockTimeoutQuery = $"SET LOCK_TIMEOUT {_timeout.TotalMilliseconds}";
- var i = db.Execute(query, new { id = LockId });
+ // execute the lock timeout query and the actual query in a single server roundtrip
+ var i = db.Execute($"{lockTimeoutQuery};{query}", new { id = LockId });
if (i == 0)
{
diff --git a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDistributedLockingMechanism.cs b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDistributedLockingMechanism.cs
index a29a4d1419..54e30d6fa6 100644
--- a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDistributedLockingMechanism.cs
+++ b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDistributedLockingMechanism.cs
@@ -162,7 +162,7 @@ public class SqliteDistributedLockingMechanism : IDistributedLockingMechanism
try
{
- var i = command.ExecuteNonQuery();
+ var i = db.ExecuteNonQuery(command);
if (i == 0)
{
diff --git a/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs b/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs
index 54ebde6f57..c7fc343787 100644
--- a/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs
+++ b/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs
@@ -16,6 +16,9 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable
private readonly IOptions _options;
private readonly IHostEnvironment? _hostEnvironment;
private readonly ISet _keys = new HashSet();
+ private static readonly TimeSpan _readLockTimeout = TimeSpan.FromSeconds(5);
+ private static readonly TimeSpan _writeLockTimeout = TimeSpan.FromSeconds(5);
+
private readonly ReaderWriterLockSlim _locker = new(LockRecursionPolicy.SupportsRecursion);
private bool _disposedValue;
@@ -54,8 +57,10 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable
Lazy
]]>
Wedi anghofio eich cyfrinair?Bydd ebost yn cael ei anfon i'r cyfeiriad darparwyd gyda dolen i ailosod eich cyfrinair
@@ -1424,6 +1428,44 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
Gallwch ond ddewis eitemau o'r math(au): %0%Rydych wedi dewis eitem gynnwys sydd naill ai wedi'i ddileu neu yn y bin ailgylchuRydych wedi dewis eitemau gynnwys sydd naill ai wedi'u dileu neu yn y bin ailgylchu
+ Nodi gwraidd
+ Dewiswch nod gwraidd
+ Nodi gwraidd efo XPath
+ Nodi Gwraidd Dynamig
+ Nod cychwyn
+ Ymholiad XPath
+
+
+ Ymholiad Gwraidd Dynamig
+ Dewiswch darddiad
+ Diffiniwch darddiad eich Ymholiad Gwraidd Dynamig
+ Gwraidd
+ Nod gwraidd y sesiwn olygu hon
+ Rhiant
+ Nod rhiant y ffynhonnell yn y sesiwn olygu hon
+ Cyfredol
+ Y nod cynnwys sy'n ffynhonnell ar gyfer y sesiwn olygu hon
+ Gwefan
+ Canfod nod agosaf gydag enw gwesteiwr
+ Nod penodol
+ Dewiswch Nod penodol fel tarddiad yr ymholiad hwn
+ Atodwch y cam i'r ymholiad
+ Diffiniwch y cam nesaf yn eich Ymholiad Gwraidd Dynamig
+ Hynafiad Agosaf Neu Hunan
+ Holwch yr hynafiad agosaf neu hunan sy'n cyd-fynd ag un o'r mathau sydd wedi'u ffurfweddu
+ Hynafiad Pellaf Neu Hunan
+ Holwch yr hynafiad pellaf neu'r hunan sy'n cyd-fynd ag un o'r mathau sydd wedi'u ffurfweddu
+ Disgynnydd Agosaf Neu Hunan
+ Holwch y disgynnydd agosaf neu'r hunan sy'n cyd-fynd ag un o'r mathau sydd wedi'u ffurfweddu
+ Disgynnydd Pellaf Neu Hunan
+ Holwch y disgynnydd pellaf neu'r hunan sy'n cyd-fynd ag un o'r mathau sydd wedi'u ffurfweddu
+ Arferiad
+ Ymholiad gan ddefnyddio Cam Ymholiad arferiad
+ Ychwanegu cam ymholiad
+ Sy'n cyfateb i'r mathau:
+ Dim cynnwys cyfatebol
+ Nid yw ffurfweddiad yr eiddo hwn yn cyfateb i unrhyw gynnwys. Creu'r cynnwys coll neu cysylltwch â'ch gweinyddwr i addasu gosodiadau Gwraidd Dynamig ar gyfer yr eiddo hwn.
+ Canslo a chlirio ymholiadRydych wedi dewis eitem gyfrwng sydd naill ai wedi'i ddileu neu yn y bin ailgylchu
@@ -1629,6 +1671,7 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
Digwyddodd gwall wrth analluogi glanhau fersiwn ar gyfer %0%Mae gwybodaeth eich system wedi'i chopïo'n llwyddiannus i'r clipfwrddMethu â chopïo gwybodaeth eich system i'r clipfwrdd
+ Bachyn gwe wedi arbedYchwanegu ardull
@@ -1873,6 +1916,33 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
NODYN! Mae glanhau fersiynau cynnwys hanesyddol wedi'u hanalluogi'n fyd-eang. Ni fydd y gosodiadau hyn yn dod i rym cyn iddo gael ei alluogi.Mae newid math o ddata gyda gwerthoedd storio wedi'i analluogi. I ganiatáu hyn gallwch newid y gosodiad Umbraco:CMS:DataTypes:CanBeChanged yn appssettings.json.
+
+ Creu bachyn gwe
+ Ychwanegu pennyn bachyn gwe
+ Ychwanegu Math o Ddogfen
+ Ychwanegu Math o Gyfrwng
+ Creu pennawd
+ Danfoniadau
+ Nid oes penawdau bachyn gwe wedi'u hychwanegu
+ Ni chanfuwyd unrhyw ddigwyddiadau.
+ Wedi'i alluogi
+ DigwyddiadauD
+ Digwyddiad
+ Url
+ Mathau
+ Allwedd bachyn gwe
+ Cyfrif o Ailgeision
+ Togl modd dadfygio am ragor o wybodaeth.
+ Cod statws ddim yn OK
+ Yr url i'w alw pan fydd y bachyn gwe yn cael ei sbarduno.
+ Y digwyddiadau y dylid sbarduno'r bachyn gwe.
+ Sbardunwch y bachyn gwe am fath penodol o gynnwys yn unig.
+ A yw'r bachyn gwe wedi'i alluogi?
+ Penawdau arferu i'w cynnwys yn y cais bachyn gwe.
+ Math o Gynnwys
+ Penawdau
+ Dewiswch ddigwyddiad yn gyntaf.
+
Ychwanegu iaithIath gorfodol
@@ -2011,6 +2081,7 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
GosodiadauTemplediTrydydd parti
+ Bachau gweDiweddariad newydd yn barod
@@ -2769,6 +2840,8 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
Ffurfweddu ardalDileu ardalYchwanegu opsiwn rhychwantu %0% colofn
+ Mewnosod Bloc
+ Arddangos yn fewnol â'r testunBeth yw Templedi Gynnwys
diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml
index 0000f92695..b344ed784a 100644
--- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml
+++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml
@@ -336,6 +336,7 @@
Send for approval is not allowedSchedule is not allowedUnpublish is not allowed
+ Select all variants%0%]]>
diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml
index 608c6943ea..58a6f8b704 100644
--- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml
+++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml
@@ -334,6 +334,7 @@
Send for approval is not allowedSchedule is not allowedUnpublish is not allowed
+ Select all variants%0%]]>
diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs
index 7d512fbb2b..3ddb98b40e 100644
--- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs
+++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs
@@ -181,7 +181,7 @@ public class MultiNodeTreePickerValueConverter : PropertyValueConverterBase, IDe
return source;
}
- public PropertyCacheLevel GetDeliveryApiPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Elements;
+ public PropertyCacheLevel GetDeliveryApiPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot;
public PropertyCacheLevel GetDeliveryApiPropertyCacheLevelForExpansion(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot;
diff --git a/src/Umbraco.Core/Scoping/LockingMechanism.cs b/src/Umbraco.Core/Scoping/LockingMechanism.cs
index e41fe2d874..1fa779d221 100644
--- a/src/Umbraco.Core/Scoping/LockingMechanism.cs
+++ b/src/Umbraco.Core/Scoping/LockingMechanism.cs
@@ -13,8 +13,7 @@ public class LockingMechanism : ILockingMechanism
{
private readonly IDistributedLockingMechanismFactory _distributedLockingMechanismFactory;
private readonly ILogger _logger;
- private readonly object _lockQueueLocker = new();
- private readonly object _dictionaryLocker = new();
+ private readonly object _locker = new();
private StackQueue<(DistributedLockType lockType, TimeSpan timeout, Guid instanceId, int lockId)>? _queuedLocks;
private HashSet? _readLocks;
private Dictionary>? _readLocksDictionary;
@@ -35,12 +34,12 @@ public class LockingMechanism : ILockingMechanism
}
///
- public void ReadLock(Guid instanceId, TimeSpan? timeout = null, params int[] lockIds) => LazyReadLockInner(instanceId, timeout, lockIds);
+ public void ReadLock(Guid instanceId, TimeSpan? timeout = null, params int[] lockIds) => EagerReadLockInner(instanceId, timeout, lockIds);
public void ReadLock(Guid instanceId, params int[] lockIds) => ReadLock(instanceId, null, lockIds);
///
- public void WriteLock(Guid instanceId, TimeSpan? timeout = null, params int[] lockIds) => LazyWriteLockInner(instanceId, timeout, lockIds);
+ public void WriteLock(Guid instanceId, TimeSpan? timeout = null, params int[] lockIds) => EagerReadLockInner(instanceId, timeout, lockIds);
public void WriteLock(Guid instanceId, params int[] lockIds) => WriteLock(instanceId, null, lockIds);
@@ -64,7 +63,7 @@ public class LockingMechanism : ILockingMechanism
/// Array of lock object identifiers.
private void EagerWriteLockInner(Guid instanceId, TimeSpan? timeout, params int[] lockIds)
{
- lock (_dictionaryLocker)
+ lock (_locker)
{
foreach (var lockId in lockIds)
{
@@ -106,7 +105,7 @@ public class LockingMechanism : ILockingMechanism
/// Array of lock object identifiers.
private void EagerReadLockInner(Guid instanceId, TimeSpan? timeout, params int[] lockIds)
{
- lock (_dictionaryLocker)
+ lock (_locker)
{
foreach (var lockId in lockIds)
{
@@ -219,7 +218,7 @@ public class LockingMechanism : ILockingMechanism
private void LazyLockInner(DistributedLockType lockType, Guid instanceId, TimeSpan? timeout = null, params int[] lockIds)
{
- lock (_lockQueueLocker)
+ lock (_locker)
{
if (_queuedLocks == null)
{
@@ -239,7 +238,7 @@ public class LockingMechanism : ILockingMechanism
/// Instance ID of the scope to clear.
public void ClearLocks(Guid instanceId)
{
- lock (_dictionaryLocker)
+ lock (_locker)
{
_readLocksDictionary?.Remove(instanceId);
_writeLocksDictionary?.Remove(instanceId);
@@ -294,7 +293,7 @@ public class LockingMechanism : ILockingMechanism
///
public void EnsureLocks(Guid scopeInstanceId)
{
- lock (_lockQueueLocker)
+ lock (_locker)
{
if (!(_queuedLocks?.Count > 0))
{
diff --git a/src/Umbraco.Core/Services/PropertyValidationService.cs b/src/Umbraco.Core/Services/PropertyValidationService.cs
index 74faacabd0..0f12e5cbdd 100644
--- a/src/Umbraco.Core/Services/PropertyValidationService.cs
+++ b/src/Umbraco.Core/Services/PropertyValidationService.cs
@@ -1,5 +1,8 @@
using System.ComponentModel.DataAnnotations;
+using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.Cache;
+using Umbraco.Cms.Core.DependencyInjection;
+using Umbraco.Cms.Core.Dictionary;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Extensions;
@@ -9,27 +12,33 @@ namespace Umbraco.Cms.Core.Services;
public class PropertyValidationService : IPropertyValidationService
{
private readonly IDataTypeService _dataTypeService;
+ private readonly ILocalizedTextService _textService;
private readonly PropertyEditorCollection _propertyEditors;
private readonly IValueEditorCache _valueEditorCache;
+ private readonly ICultureDictionary _cultureDictionary;
- [Obsolete($"Use the constructor that does not accept {nameof(ILocalizedTextService)}. Will be removed in V15.")]
+ [Obsolete("Use the constructor that accepts ICultureDictionary. Will be removed in V15.")]
public PropertyValidationService(
PropertyEditorCollection propertyEditors,
IDataTypeService dataTypeService,
ILocalizedTextService textService,
IValueEditorCache valueEditorCache)
- : this(propertyEditors, dataTypeService, valueEditorCache)
+ : this(propertyEditors, dataTypeService, textService, valueEditorCache, StaticServiceProvider.Instance.GetRequiredService())
{
}
public PropertyValidationService(
PropertyEditorCollection propertyEditors,
IDataTypeService dataTypeService,
- IValueEditorCache valueEditorCache)
+ ILocalizedTextService textService,
+ IValueEditorCache valueEditorCache,
+ ICultureDictionary cultureDictionary)
{
_propertyEditors = propertyEditors;
_dataTypeService = dataTypeService;
+ _textService = textService;
_valueEditorCache = valueEditorCache;
+ _cultureDictionary = cultureDictionary;
}
///
@@ -80,13 +89,13 @@ public class PropertyValidationService : IPropertyValidationService
if (isRequired && !string.IsNullOrWhiteSpace(isRequiredMessage) &&
requiredDefaultMessages.Contains(validationResult.ErrorMessage, StringComparer.OrdinalIgnoreCase))
{
- validationResult.ErrorMessage = isRequiredMessage;
+ validationResult.ErrorMessage = _textService.UmbracoDictionaryTranslate(_cultureDictionary, isRequiredMessage);
}
if (!string.IsNullOrWhiteSpace(validationRegExp) && !string.IsNullOrWhiteSpace(validationRegExpMessage) &&
formatDefaultMessages.Contains(validationResult.ErrorMessage, StringComparer.OrdinalIgnoreCase))
{
- validationResult.ErrorMessage = validationRegExpMessage;
+ validationResult.ErrorMessage = _textService.UmbracoDictionaryTranslate(_cultureDictionary, validationRegExpMessage);
}
yield return validationResult;
diff --git a/src/Umbraco.Examine.Lucene/DeliveryApiContentIndex.cs b/src/Umbraco.Examine.Lucene/DeliveryApiContentIndex.cs
index a3508a3fed..c572fc85f7 100644
--- a/src/Umbraco.Examine.Lucene/DeliveryApiContentIndex.cs
+++ b/src/Umbraco.Examine.Lucene/DeliveryApiContentIndex.cs
@@ -1,9 +1,9 @@
+using System.Globalization;
using Examine;
using Examine.Lucene;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
-using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DeliveryApi;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Hosting;
@@ -125,8 +125,14 @@ public class DeliveryApiContentIndex : UmbracoExamineIndex
private (string? ContentId, string? Culture) ParseItemId(string id)
{
+ if (int.TryParse(id, out _))
+ {
+ return (id, null);
+ }
+
DeliveryApiIndexCompositeIdModel compositeIdModel = _deliveryApiCompositeIdHandler.Decompose(id);
- return (compositeIdModel.Id.ToString() ?? id, compositeIdModel.Culture);
+
+ return (compositeIdModel.Id?.ToString(CultureInfo.InvariantCulture), compositeIdModel.Culture);
}
protected override void OnTransformingIndexValues(IndexingItemEventArgs e)
diff --git a/src/Umbraco.Infrastructure/Persistence/IUmbracoDatabase.cs b/src/Umbraco.Infrastructure/Persistence/IUmbracoDatabase.cs
index 431ddeb5e8..b86648f05c 100644
--- a/src/Umbraco.Infrastructure/Persistence/IUmbracoDatabase.cs
+++ b/src/Umbraco.Infrastructure/Persistence/IUmbracoDatabase.cs
@@ -1,3 +1,4 @@
+using System.Data.Common;
using NPoco;
using Umbraco.Cms.Infrastructure.Migrations.Install;
@@ -33,4 +34,7 @@ public interface IUmbracoDatabase : IDatabase
bool IsUmbracoInstalled();
DatabaseSchemaResult ValidateSchema();
+
+ /// The number of rows affected.
+ int ExecuteNonQuery(DbCommand command) => command.ExecuteNonQuery();
}
diff --git a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabase.cs b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabase.cs
index af8eb8e1fe..3abec76ec2 100644
--- a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabase.cs
+++ b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabase.cs
@@ -223,6 +223,14 @@ public class UmbracoDatabase : Database, IUmbracoDatabase
return databaseSchemaValidationResult ?? new DatabaseSchemaResult();
}
+ public int ExecuteNonQuery(DbCommand command)
+ {
+ OnExecutingCommand(command);
+ var i = command.ExecuteNonQuery();
+ OnExecutedCommand(command);
+ return i;
+ }
+
///
/// Returns true if Umbraco database tables are detected to be installed
///
diff --git a/src/Umbraco.PublishedCache.NuCache/ContentStore.cs b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs
index f38b9dd2fc..0230032dc2 100644
--- a/src/Umbraco.PublishedCache.NuCache/ContentStore.cs
+++ b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs
@@ -27,6 +27,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache;
///
public class ContentStore
{
+ private static readonly TimeSpan _monitorTimeout = TimeSpan.FromSeconds(30);
+
// TODO: collection trigger (ok for now)
// see SnapDictionary notes
private const long CollectMinGenDelta = 8;
@@ -330,7 +332,12 @@ public class ContentStore
throw new InvalidOperationException("Recursive locks not allowed");
}
- Monitor.Enter(_wlocko, ref lockInfo.Taken);
+ Monitor.TryEnter(_wlocko, _monitorTimeout, ref lockInfo.Taken);
+
+ if (Monitor.IsEntered(_wlocko) is false)
+ {
+ throw new TimeoutException("Could not enter monitor before timeout in content store");
+ }
lock (_rlocko)
{
diff --git a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentService.cs b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentService.cs
index 09855f5682..13e911f137 100644
--- a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentService.cs
+++ b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentService.cs
@@ -127,9 +127,25 @@ public class NuCacheContentService : RepositoryService, INuCacheContentService
{
using (ICoreScope scope = ScopeProvider.CreateCoreScope(repositoryCacheMode: RepositoryCacheMode.Scoped))
{
- scope.ReadLock(Constants.Locks.ContentTree);
- scope.ReadLock(Constants.Locks.MediaTree);
- scope.ReadLock(Constants.Locks.MemberTree);
+ if (contentTypeIds is null && mediaTypeIds is null && memberTypeIds is null)
+ {
+ scope.ReadLock(Constants.Locks.ContentTree,Constants.Locks.MediaTree,Constants.Locks.MemberTree);
+ }
+
+ if (contentTypeIds is not null && contentTypeIds.Any())
+ {
+ scope.ReadLock(Constants.Locks.ContentTree);
+ }
+
+ if (mediaTypeIds is not null && mediaTypeIds.Any())
+ {
+ scope.ReadLock(Constants.Locks.MediaTree);
+ }
+
+ if (memberTypeIds is not null && memberTypeIds.Any())
+ {
+ scope.ReadLock(Constants.Locks.MemberTree);
+ }
_repository.Rebuild(contentTypeIds, mediaTypeIds, memberTypeIds);
diff --git a/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs b/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs
index 0d042380d2..b6c87e22bb 100644
--- a/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs
+++ b/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs
@@ -9,6 +9,8 @@ public class SnapDictionary
where TValue : class
where TKey : notnull
{
+ private static readonly TimeSpan _monitorTimeout = TimeSpan.FromSeconds(30);
+
// minGenDelta to be adjusted
// we may want to throttle collects even if delta is reached
// we may want to force collect if delta is not reached but very old
@@ -198,7 +200,12 @@ public class SnapDictionary
throw new InvalidOperationException("Recursive locks not allowed");
}
- Monitor.Enter(_wlocko, ref lockInfo.Taken);
+ Monitor.TryEnter(_wlocko, _monitorTimeout, ref lockInfo.Taken);
+
+ if (Monitor.IsEntered(_wlocko) is false)
+ {
+ throw new TimeoutException("Could not enter the monitor before timeout in SnapDictionary");
+ }
lock (_rlocko)
{
diff --git a/src/Umbraco.Web.UI.Client b/src/Umbraco.Web.UI.Client
deleted file mode 160000
index 2cc9da721e..0000000000
--- a/src/Umbraco.Web.UI.Client
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 2cc9da721ee7002cba26c2a4aae1f49d46f973fb
diff --git a/src/Umbraco.Web.UI.Login/src/context/auth.repository.ts b/src/Umbraco.Web.UI.Login/src/context/auth.repository.ts
index 4725d8148d..e1f51ee5ef 100644
--- a/src/Umbraco.Web.UI.Login/src/context/auth.repository.ts
+++ b/src/Umbraco.Web.UI.Login/src/context/auth.repository.ts
@@ -25,14 +25,7 @@ export class UmbAuthRepository {
});
const response = await fetch(request);
- const responseData: LoginResponse = {
- status: response.status,
- };
-
- if (!response.ok) {
- responseData.error = await this.#getErrorText(response);
- return responseData;
- }
+ let responseData: any = undefined;
// Additionally authenticate with the Management API
await this.#managementApiLogin(data.username, data.password);
@@ -40,14 +33,15 @@ export class UmbAuthRepository {
try {
const text = await response.text();
if (text) {
- responseData.data = JSON.parse(this.#removeAngularJSResponseData(text));
+ responseData = JSON.parse(this.#removeAngularJSResponseData(text));
}
} catch {
}
return {
status: response.status,
- data: responseData?.data,
+ error: response.ok ? undefined : await this.#getErrorText(response),
+ data: responseData,
twoFactorView: responseData?.twoFactorView,
};
} catch (error) {
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ContentServiceTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ContentServiceTests.cs
index 21bffb23aa..40c2ab7915 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ContentServiceTests.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ContentServiceTests.cs
@@ -7,6 +7,7 @@ using Moq;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
+using Umbraco.Cms.Core.Dictionary;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
@@ -1250,7 +1251,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
Assert.IsFalse(content.HasIdentity);
// content cannot publish values because they are invalid
- var propertyValidationService = new PropertyValidationService(PropertyEditorCollection, DataTypeService, ValueEditorCache);
+ var propertyValidationService = new PropertyValidationService(PropertyEditorCollection, DataTypeService, ValueEditorCache, Mock.Of());
var isValid = propertyValidationService.IsPropertyDataValid(content, out var invalidProperties,
CultureImpact.Invariant);
Assert.IsFalse(isValid);
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/LocksTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/LocksTests.cs
index da3ba0f298..8387aaeb7a 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/LocksTests.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/LocksTests.cs
@@ -1,8 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NPoco;
@@ -14,7 +12,6 @@ using Umbraco.Cms.Persistence.Sqlite.Interceptors;
using Umbraco.Cms.Tests.Common.Attributes;
using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.Testing;
-using Umbraco.Extensions;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence;
@@ -125,6 +122,7 @@ public class LocksTests : UmbracoIntegrationTest
}
}
+ [NUnit.Framework.Ignore("We currently do not have a way to force lazy locks")]
[Test]
public void GivenNonEagerLocking_WhenNoDbIsAccessed_ThenNoSqlIsExecuted()
{
@@ -154,6 +152,37 @@ public class LocksTests : UmbracoIntegrationTest
Assert.AreEqual(0, sqlCount);
}
+ [Test]
+ public void GivenNonEagerLocking_WhenDbIsAccessed_ThenSqlIsExecuted()
+ {
+ var sqlCount = 0;
+
+ using (var scope = ScopeProvider.CreateScope())
+ {
+ var db = ScopeAccessor.AmbientScope.Database;
+ try
+ {
+ db.EnableSqlCount = true;
+
+ // Issue a lock request, but we are using non-eager
+ // locks so this only queues the request.
+ // The lock will not be issued unless we resolve
+ // scope.Database
+ scope.WriteLock(Constants.Locks.Servers);
+
+ scope.Database.ExecuteScalar("SELECT 1");
+
+ sqlCount = db.SqlCount;
+ }
+ finally
+ {
+ db.EnableSqlCount = false;
+ }
+ }
+
+ Assert.AreEqual(2,sqlCount);
+ }
+
[Test]
[LongRunning]
public void ConcurrentWritersTest()
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Models/VariationTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Models/VariationTests.cs
index 2598f0a559..79d247c118 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Models/VariationTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Models/VariationTests.cs
@@ -5,6 +5,7 @@ using Moq;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
+using Umbraco.Cms.Core.Dictionary;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.PropertyEditors;
@@ -648,6 +649,7 @@ public class VariationTests
return new PropertyValidationService(
propertyEditorCollection,
dataTypeService,
- new ValueEditorCache());
+ new ValueEditorCache(),
+ Mock.Of());
}
}
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Services/PropertyValidationServiceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Services/PropertyValidationServiceTests.cs
index 4bf81cd88d..cd1e128e4a 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Services/PropertyValidationServiceTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Services/PropertyValidationServiceTests.cs
@@ -5,6 +5,7 @@ using Moq;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
+using Umbraco.Cms.Core.Dictionary;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.PropertyEditors;
@@ -43,7 +44,7 @@ public class PropertyValidationServiceTests
var propEditors = new PropertyEditorCollection(new DataEditorCollection(() => new[] { dataEditor }));
- validationService = new PropertyValidationService(propEditors, dataTypeService.Object, new ValueEditorCache());
+ validationService = new PropertyValidationService(propEditors, dataTypeService.Object, new ValueEditorCache(), Mock.Of());
}
[Test]