This commit is contained in:
Bjarke Berg
2024-03-15 15:34:00 +01:00
24 changed files with 224 additions and 54 deletions

View File

@@ -7,6 +7,8 @@
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" VersionOverride="[2.1.6, 3)" />
<PackageReference Include="SixLabors.ImageSharp.Web" VersionOverride="[2.0.2, 3)" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
<PackageReference Include="SixLabors.ImageSharp.Web" Version="2.0.2" />
</ItemGroup>
<ItemGroup>

View File

@@ -136,9 +136,7 @@ internal class SqlServerEFCoreDistributedLockingMechanism<T> : 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<int?>($"SELECT value FROM dbo.umbracoLock WITH (REPEATABLEREAD) WHERE id={LockId}");
var number = await dbContext.Database.ExecuteScalarAsync<int?>($"SET LOCK_TIMEOUT {(int)_timeout.TotalMilliseconds};SELECT value FROM dbo.umbracoLock WITH (REPEATABLEREAD) WHERE id={LockId}");
if (number == null)
{

View File

@@ -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<int?>(query, new { id = LockId });
// execute the lock timeout query and the actual query in a single server roundtrip
var i = db.ExecuteScalar<int?>($"{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)
{

View File

@@ -162,7 +162,7 @@ public class SqliteDistributedLockingMechanism : IDistributedLockingMechanism
try
{
var i = command.ExecuteNonQuery();
var i = db.ExecuteNonQuery(command);
if (i == 0)
{

View File

@@ -16,6 +16,9 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable
private readonly IOptions<MemoryCacheOptions> _options;
private readonly IHostEnvironment? _hostEnvironment;
private readonly ISet<string> _keys = new HashSet<string>();
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<object?>? result;
try
{
_locker.EnterReadLock();
if (_locker.TryEnterReadLock(_readLockTimeout) is false)
{
throw new TimeoutException("Timeout exceeded to the memory cache when getting item");
}
result = MemoryCache.Get(key) as Lazy<object?>; // null if key not found
}
finally
@@ -201,7 +206,10 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable
{
try
{
_locker.EnterWriteLock();
if (_locker.TryEnterWriteLock(_writeLockTimeout) is false)
{
throw new TimeoutException("Timeout exceeded to the memory cache when clearing item");
}
MemoryCache.Remove(key);
_keys.Remove(key);
@@ -286,7 +294,10 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable
{
try
{
_locker.EnterWriteLock();
if (_locker.TryEnterWriteLock(_writeLockTimeout) is false)
{
throw new TimeoutException("Timeout exceeded to the memory cache.");
}
// ToArray required to remove
foreach (var key in _keys.Where(predicate).ToArray())

View File

@@ -459,9 +459,10 @@
<key alias="confirmlogout">Ydych chi'n sicr?</key>
<key alias="confirmSure">Ydych chi'n sicr?</key>
<key alias="cut">Torri</key>
<key alias="editdictionary">Golygu Eitem Geiriadur</key>
<key alias="editlanguage">Golygu Iaith</key>
<key alias="editDictionary">Golygu Eitem Geiriadur</key>
<key alias="editLanguage">Golygu Iaith</key>
<key alias="editSelectedMedia">Golygu cyfrwng a dewiswyd</key>
<key alias="editWebhook">Golygu bachyn gwe</key>
<key alias="insertAnchor">Mewnosod dolen leol</key>
<key alias="insertCharacter">Mewnosod nod</key>
<key alias="insertgraphicheadline">Mewnosod pennawd graffig</key>
@@ -504,6 +505,7 @@
<key alias="openInNewWindow">Agor y ddolen ddogfen mewn ffenestr neu tab newydd</key>
<key alias="linkToMedia">Dolen i gyfrwng</key>
<key alias="selectContentStartNode">Dewis nod cychwyn cynnwys</key>
<key alias="selectEvent">Dewis digwyddiad</key>
<key alias="selectMedia">Dewis cyfrwng</key>
<key alias="selectMediaType">Dewis y math o gyfrwng</key>
<key alias="selectIcon">Dewis eicon</key>
@@ -857,6 +859,7 @@
<key alias="umbracoInfo">Gwybodaeth Umbraco</key>
<key alias="skipToMenu">Neidio i'r dewislen</key>
<key alias="skipToContent">Neidio i'r cynnwys</key>
<key alias="primary">Prif</key>
</area>
<area alias="colors">
<key alias="blue">Glas</key>
@@ -1061,6 +1064,7 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
<key alias="instruction">Mewngofnodwch isod</key>
<key alias="signInWith">Mewngofnodwch gyda</key>
<key alias="timeout">Sesiwn wedi cyrraedd terfyn amser</key>
<key alias="userFailedLogin">Wps! Mae mewngofnodi wedi methu. Gwiriwch eich manylion a thrio eto.</key>
<key alias="bottomText"><![CDATA[<p style="text-align:right;">&copy; 2001 - %0% <br /><a href="https://umbraco.com" style="text-decoration: none" target="_blank" rel="noopener">Umbraco.com</a></p> ]]></key>
<key alias="forgottenPassword">Wedi anghofio eich cyfrinair?</key>
<key alias="forgottenPasswordInstruction">Bydd ebost yn cael ei anfon i'r cyfeiriad darparwyd gyda dolen i ailosod eich cyfrinair</key>
@@ -1424,6 +1428,44 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
<key alias="allowedItemTypes">Gallwch ond ddewis eitemau o'r math(au): %0%</key>
<key alias="pickedTrashedItem">Rydych wedi dewis eitem gynnwys sydd naill ai wedi'i ddileu neu yn y bin ailgylchu</key>
<key alias="pickedTrashedItems">Rydych wedi dewis eitemau gynnwys sydd naill ai wedi'u dileu neu yn y bin ailgylchu</key>
<key alias="specifyPickerRootTitle">Nodi gwraidd</key>
<key alias="defineRootNode">Dewiswch nod gwraidd</key>
<key alias="defineXPathOrigin">Nodi gwraidd efo XPath</key>
<key alias="defineDynamicRoot">Nodi Gwraidd Dynamig</key>
<key alias="configurationStartNodeTitle">Nod cychwyn</key>
<key alias="configurationXPathTitle">Ymholiad XPath</key>
</area>
<area alias="dynamicRoot">
<key alias="configurationTitle">Ymholiad Gwraidd Dynamig</key>
<key alias="pickDynamicRootOriginTitle">Dewiswch darddiad</key>
<key alias="pickDynamicRootOriginDesc">Diffiniwch darddiad eich Ymholiad Gwraidd Dynamig</key>
<key alias="originRootTitle">Gwraidd</key>
<key alias="originRootDesc">Nod gwraidd y sesiwn olygu hon</key>
<key alias="originParentTitle">Rhiant</key>
<key alias="originParentDesc">Nod rhiant y ffynhonnell yn y sesiwn olygu hon</key>
<key alias="originCurrentTitle">Cyfredol</key>
<key alias="originCurrentDesc">Y nod cynnwys sy'n ffynhonnell ar gyfer y sesiwn olygu hon</key>
<key alias="originSiteTitle">Gwefan</key>
<key alias="originSiteDesc">Canfod nod agosaf gydag enw gwesteiwr</key>
<key alias="originByKeyTitle">Nod penodol</key>
<key alias="originByKeyDesc">Dewiswch Nod penodol fel tarddiad yr ymholiad hwn</key>
<key alias="pickDynamicRootQueryStepTitle">Atodwch y cam i'r ymholiad</key>
<key alias="pickDynamicRootQueryStepDesc">Diffiniwch y cam nesaf yn eich Ymholiad Gwraidd Dynamig</key>
<key alias="queryStepNearestAncestorOrSelfTitle">Hynafiad Agosaf Neu Hunan</key>
<key alias="queryStepNearestAncestorOrSelfDesc">Holwch yr hynafiad agosaf neu hunan sy'n cyd-fynd ag un o'r mathau sydd wedi'u ffurfweddu</key>
<key alias="queryStepFurthestAncestorOrSelfTitle">Hynafiad Pellaf Neu Hunan</key>
<key alias="queryStepFurthestAncestorOrSelfDesc">Holwch yr hynafiad pellaf neu'r hunan sy'n cyd-fynd ag un o'r mathau sydd wedi'u ffurfweddu</key>
<key alias="queryStepNearestDescendantOrSelfTitle">Disgynnydd Agosaf Neu Hunan</key>
<key alias="queryStepNearestDescendantOrSelfDesc">Holwch y disgynnydd agosaf neu'r hunan sy'n cyd-fynd ag un o'r mathau sydd wedi'u ffurfweddu</key>
<key alias="queryStepFurthestDescendantOrSelfTitle">Disgynnydd Pellaf Neu Hunan</key>
<key alias="queryStepFurthestDescendantOrSelfDesc">Holwch y disgynnydd pellaf neu'r hunan sy'n cyd-fynd ag un o'r mathau sydd wedi'u ffurfweddu</key>
<key alias="queryStepCustomTitle">Arferiad</key>
<key alias="queryStepCustomDesc">Ymholiad gan ddefnyddio Cam Ymholiad arferiad</key>
<key alias="addQueryStep">Ychwanegu cam ymholiad</key>
<key alias="queryStepTypes">Sy'n cyfateb i'r mathau: </key>
<key alias="noValidStartNodeTitle">Dim cynnwys cyfatebol</key>
<key alias="noValidStartNodeDesc">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.</key>
<key alias="cancelAndClearQuery">Canslo a chlirio ymholiad</key>
</area>
<area alias="mediaPicker">
<key alias="pickedTrashedItem">Rydych wedi dewis eitem gyfrwng sydd naill ai wedi'i ddileu neu yn y bin ailgylchu</key>
@@ -1629,6 +1671,7 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
<key alias="preventCleanupDisableError">Digwyddodd gwall wrth analluogi glanhau fersiwn ar gyfer %0%</key>
<key alias="copySuccessMessage">Mae gwybodaeth eich system wedi'i chopïo'n llwyddiannus i'r clipfwrdd</key>
<key alias="cannotCopyInformation">Methu â chopïo gwybodaeth eich system i'r clipfwrdd</key>
<key alias="webhookSaved">Bachyn gwe wedi arbed</key>
</area>
<area alias="stylesheet">
<key alias="addRule">Ychwanegu ardull</key>
@@ -1873,6 +1916,33 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
<key alias="historyCleanupGloballyDisabled"><strong>NODYN!</strong> 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.</key>
<key alias="changeDataTypeHelpText">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.</key>
</area>
<area alias="webhooks">
<key alias="addWebhook">Creu bachyn gwe</key>
<key alias="addWebhookHeader">Ychwanegu pennyn bachyn gwe</key>
<key alias="addDocumentType">Ychwanegu Math o Ddogfen</key>
<key alias="addMediaType">Ychwanegu Math o Gyfrwng</key>
<key alias="createHeader">Creu pennawd</key>
<key alias="deliveries">Danfoniadau</key>
<key alias="noHeaders">Nid oes penawdau bachyn gwe wedi'u hychwanegu</key>
<key alias="noEventsFound">Ni chanfuwyd unrhyw ddigwyddiadau.</key>
<key alias="enabled">Wedi'i alluogi</key>
<key alias="events">Digwyddiadau</key>D
<key alias="event">Digwyddiad</key>
<key alias="url">Url</key>
<key alias="types">Mathau</key>
<key alias="webhookKey">Allwedd bachyn gwe</key>
<key alias="retryCount">Cyfrif o Ailgeision</key>
<key alias="toggleDebug">Togl modd dadfygio am ragor o wybodaeth.</key>
<key alias="statusNotOk">Cod statws ddim yn OK</key>
<key alias="urlDescription">Yr url i'w alw pan fydd y bachyn gwe yn cael ei sbarduno.</key>
<key alias="eventDescription">Y digwyddiadau y dylid sbarduno'r bachyn gwe.</key>
<key alias="contentTypeDescription">Sbardunwch y bachyn gwe am fath penodol o gynnwys yn unig.</key>
<key alias="enabledDescription">A yw'r bachyn gwe wedi'i alluogi?</key>
<key alias="headersDescription">Penawdau arferu i'w cynnwys yn y cais bachyn gwe.</key>
<key alias="contentType">Math o Gynnwys</key>
<key alias="headers">Penawdau</key>
<key alias="selectEventFirst">Dewiswch ddigwyddiad yn gyntaf.</key>
</area>
<area alias="languages">
<key alias="addLanguage">Ychwanegu iaith</key>
<key alias="mandatoryLanguage">Iath gorfodol</key>
@@ -2011,6 +2081,7 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
<key alias="settingsGroup">Gosodiadau</key>
<key alias="templatingGroup">Templedi</key>
<key alias="thirdPartyGroup">Trydydd parti</key>
<key alias="webhooks">Bachau gwe</key>
</area>
<area alias="update">
<key alias="updateAvailable">Diweddariad newydd yn barod</key>
@@ -2769,6 +2840,8 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
<key alias="configureArea">Ffurfweddu ardal</key>
<key alias="deleteArea">Dileu ardal</key>
<key alias="addColumnSpanOption">Ychwanegu opsiwn rhychwantu %0% colofn</key>
<key alias="insertBlock">Mewnosod Bloc</key>
<key alias="labelInlineMode">Arddangos yn fewnol â'r testun</key>
</area>
<area alias="contentTemplatesDashboard">
<key alias="whatHeadline">Beth yw Templedi Gynnwys</key>

View File

@@ -336,6 +336,7 @@
<key alias="variantSendForApprovalNotAllowed">Send for approval is not allowed</key>
<key alias="variantScheduleNotAllowed">Schedule is not allowed</key>
<key alias="variantUnpublishNotAllowed">Unpublish is not allowed</key>
<key alias="selectAllVariants">Select all variants</key>
</area>
<area alias="blueprints">
<key alias="createBlueprintFrom"><![CDATA[Create a new Content Template from <em>%0%</em>]]></key>

View File

@@ -334,6 +334,7 @@
<key alias="variantSendForApprovalNotAllowed">Send for approval is not allowed</key>
<key alias="variantScheduleNotAllowed">Schedule is not allowed</key>
<key alias="variantUnpublishNotAllowed">Unpublish is not allowed</key>
<key alias="selectAllVariants">Select all variants</key>
</area>
<area alias="blueprints">
<key alias="createBlueprintFrom"><![CDATA[Create a new Content Template from <em>%0%</em>]]></key>

View File

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

View File

@@ -13,8 +13,7 @@ public class LockingMechanism : ILockingMechanism
{
private readonly IDistributedLockingMechanismFactory _distributedLockingMechanismFactory;
private readonly ILogger<LockingMechanism> _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<int>? _readLocks;
private Dictionary<Guid, Dictionary<int, int>>? _readLocksDictionary;
@@ -35,12 +34,12 @@ public class LockingMechanism : ILockingMechanism
}
/// <inheritdoc />
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);
/// <inheritdoc />
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
/// <param name="lockIds">Array of lock object identifiers.</param>
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
/// <param name="lockIds">Array of lock object identifiers.</param>
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
/// <param name="instanceId">Instance ID of the scope to clear.</param>
public void ClearLocks(Guid instanceId)
{
lock (_dictionaryLocker)
lock (_locker)
{
_readLocksDictionary?.Remove(instanceId);
_writeLocksDictionary?.Remove(instanceId);
@@ -294,7 +293,7 @@ public class LockingMechanism : ILockingMechanism
/// </summary>
public void EnsureLocks(Guid scopeInstanceId)
{
lock (_lockQueueLocker)
lock (_locker)
{
if (!(_queuedLocks?.Count > 0))
{

View File

@@ -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<ICultureDictionary>())
{
}
public PropertyValidationService(
PropertyEditorCollection propertyEditors,
IDataTypeService dataTypeService,
IValueEditorCache valueEditorCache)
ILocalizedTextService textService,
IValueEditorCache valueEditorCache,
ICultureDictionary cultureDictionary)
{
_propertyEditors = propertyEditors;
_dataTypeService = dataTypeService;
_textService = textService;
_valueEditorCache = valueEditorCache;
_cultureDictionary = cultureDictionary;
}
/// <inheritdoc />
@@ -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;

View File

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

View File

@@ -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();
/// <returns>The number of rows affected.</returns>
int ExecuteNonQuery(DbCommand command) => command.ExecuteNonQuery();
}

View File

@@ -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;
}
/// <summary>
/// Returns true if Umbraco database tables are detected to be installed
/// </summary>

View File

@@ -27,6 +27,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache;
/// </remarks>
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)
{

View File

@@ -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);

View File

@@ -9,6 +9,8 @@ public class SnapDictionary<TKey, TValue>
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<TKey, TValue>
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)
{

View File

@@ -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) {