diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDistributedLockingMechanism.cs b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDistributedLockingMechanism.cs index 358eae2e38..cc2e1b5feb 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDistributedLockingMechanism.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDistributedLockingMechanism.cs @@ -70,8 +70,10 @@ public class SqlServerDistributedLockingMechanism : IDistributedLockingMechanism _timeout = timeout; LockId = lockId; LockType = lockType; - - _parent._logger.LogDebug("Requesting {lockType} for id {id}", LockType, LockId); + if (_parent._logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _parent._logger.LogDebug("Requesting {lockType} for id {id}", LockType, LockId); + } try { @@ -96,17 +98,24 @@ public class SqlServerDistributedLockingMechanism : IDistributedLockingMechanism throw new DistributedWriteLockTimeoutException(LockId); } - - _parent._logger.LogDebug("Acquired {lockType} for id {id}", LockType, LockId); + if (_parent._logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _parent._logger.LogDebug("Acquired {lockType} for id {id}", LockType, LockId); + } } public int LockId { get; } public DistributedLockType LockType { get; } - public void Dispose() => - // Mostly no op, cleaned up by completing transaction in scope. - _parent._logger.LogDebug("Dropped {lockType} for id {id}", LockType, LockId); + public void Dispose() + { + if (_parent._logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + // Mostly no op, cleaned up by completing transaction in scope. + _parent._logger.LogDebug("Dropped {lockType} for id {id}", LockType, LockId); + } + } public override string ToString() => $"SqlServerDistributedLock({LockId}, {LockType}"; diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerSyntaxProvider.cs b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerSyntaxProvider.cs index a1b9d2d8f0..055da32d75 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerSyntaxProvider.cs @@ -86,9 +86,10 @@ public class SqlServerSyntaxProvider : MicrosoftSqlSyntaxProviderBase - // Mostly no op, cleaned up by completing transaction in scope. - _parent._logger.LogDebug("Dropped {lockType} for id {id}", LockType, LockId); + public void Dispose() + { + if (_parent._logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + // Mostly no op, cleaned up by completing transaction in scope. + _parent._logger.LogDebug("Dropped {lockType} for id {id}", LockType, LockId); + } + } public override string ToString() => $"SqliteDistributedLock({LockId})"; diff --git a/src/Umbraco.Core/Composing/ComponentCollection.cs b/src/Umbraco.Core/Composing/ComponentCollection.cs index d64de626d0..506eb23134 100644 --- a/src/Umbraco.Core/Composing/ComponentCollection.cs +++ b/src/Umbraco.Core/Composing/ComponentCollection.cs @@ -23,13 +23,13 @@ public class ComponentCollection : BuilderCollectionBase public void Initialize() { - using (_profilingLogger.DebugDuration( + using (!_profilingLogger.IsEnabled(Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration( $"Initializing. (log components when >{LogThresholdMilliseconds}ms)", "Initialized.")) { foreach (IComponent component in this) { Type componentType = component.GetType(); - using (_profilingLogger.DebugDuration( + using (!_profilingLogger.IsEnabled(Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration( $"Initializing {componentType.FullName}.", $"Initialized {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) @@ -42,14 +42,14 @@ public class ComponentCollection : BuilderCollectionBase public void Terminate() { - using (_profilingLogger.DebugDuration( + using (!_profilingLogger.IsEnabled(Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration( $"Terminating. (log components when >{LogThresholdMilliseconds}ms)", "Terminated.")) { // terminate components in reverse order foreach (IComponent component in this.Reverse()) { Type componentType = component.GetType(); - using (_profilingLogger.DebugDuration( + using (!_profilingLogger.IsEnabled(Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration( $"Terminating {componentType.FullName}.", $"Terminated {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) diff --git a/src/Umbraco.Core/Composing/ComponentCollectionBuilder.cs b/src/Umbraco.Core/Composing/ComponentCollectionBuilder.cs index b77dfde819..04461db4fb 100644 --- a/src/Umbraco.Core/Composing/ComponentCollectionBuilder.cs +++ b/src/Umbraco.Core/Composing/ComponentCollectionBuilder.cs @@ -18,7 +18,7 @@ public class { IProfilingLogger logger = factory.GetRequiredService(); - using (logger.DebugDuration( + using (!logger.IsEnabled(Logging.LogLevel.Debug) ? null : logger.DebugDuration( $"Creating components. (log when >{LogThresholdMilliseconds}ms)", "Created.")) { return base.CreateItems(factory); @@ -29,7 +29,7 @@ public class { IProfilingLogger logger = factory.GetRequiredService(); - using (logger.DebugDuration( + using (!logger.IsEnabled(Logging.LogLevel.Debug) ? null : logger.DebugDuration( $"Creating {itemType.FullName}.", $"Created {itemType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) diff --git a/src/Umbraco.Core/Composing/ComposerGraph.cs b/src/Umbraco.Core/Composing/ComposerGraph.cs index 3c602b0ad9..a4e8f189f7 100644 --- a/src/Umbraco.Core/Composing/ComposerGraph.cs +++ b/src/Umbraco.Core/Composing/ComposerGraph.cs @@ -126,7 +126,10 @@ internal class ComposerGraph // bit verbose but should help for troubleshooting // var text = "Ordered Composers: " + Environment.NewLine + string.Join(Environment.NewLine, sortedComposerTypes) + Environment.NewLine; - _logger.LogDebug("Ordered Composers: {SortedComposerTypes}", sortedComposerTypes); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Ordered Composers: {SortedComposerTypes}", sortedComposerTypes); + } return sortedComposerTypes; } diff --git a/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs index f9e4ed6dbe..1b516636b3 100644 --- a/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs +++ b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs @@ -57,7 +57,10 @@ internal class FindAssembliesWithReferencesTo catch (FileNotFoundException ex) { // occurs if we cannot load this ... for example in a test project where we aren't currently referencing Umbraco.Web, etc... - _logger.LogDebug(ex, "Could not load assembly " + target); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug(ex, "Could not load assembly " + target); + } } } } diff --git a/src/Umbraco.Core/Composing/ReferenceResolver.cs b/src/Umbraco.Core/Composing/ReferenceResolver.cs index 1924fb4b75..7ad290a192 100644 --- a/src/Umbraco.Core/Composing/ReferenceResolver.cs +++ b/src/Umbraco.Core/Composing/ReferenceResolver.cs @@ -70,7 +70,10 @@ internal class ReferenceResolver } catch (BadImageFormatException e) { - _logger.LogDebug(e, "Could not load {dll} for type scanning, skipping", dll); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug(e, "Could not load {dll} for type scanning, skipping", dll); + } } catch (SecurityException e) { diff --git a/src/Umbraco.Core/Composing/RuntimeHash.cs b/src/Umbraco.Core/Composing/RuntimeHash.cs index e66bedf79f..e7a621fe03 100644 --- a/src/Umbraco.Core/Composing/RuntimeHash.cs +++ b/src/Umbraco.Core/Composing/RuntimeHash.cs @@ -45,7 +45,7 @@ public class RuntimeHash : IRuntimeHash /// private string GetFileHash(IEnumerable<(FileSystemInfo fileOrFolder, bool scanFileContent)> filesAndFolders) { - using (_logger.DebugDuration("Determining hash of code files on disk", "Hash determined")) + using (!_logger.IsEnabled(Logging.LogLevel.Debug) ? null : _logger.DebugDuration("Determining hash of code files on disk", "Hash determined")) { // get the distinct file infos to hash var uniqInfos = new HashSet(); diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index 7fadd102da..9fc692f778 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -190,10 +190,13 @@ public sealed class TypeLoader if (!typeof(IDiscoverable).IsAssignableFrom(typeof(T))) { // warn - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "Running a full, " + (cache ? string.Empty : "non-") + "cached, scan for non-discoverable type {TypeName} (slow).", typeof(T).FullName); + } return GetTypesInternal( typeof(T), @@ -214,9 +217,12 @@ public sealed class TypeLoader // warn if (!cache) { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "Running a non-cached, filter for discoverable type {TypeName} (slowish).", typeof(T).FullName); + } } // filter the cached discovered types (and maybe cache the result) @@ -253,11 +259,14 @@ public sealed class TypeLoader // if not IDiscoverable, directly get types if (!typeof(IDiscoverable).IsAssignableFrom(typeof(T))) { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "Running a full, " + (cache ? string.Empty : "non-") + "cached, scan for non-discoverable type {TypeName} / attribute {AttributeName} (slow).", typeof(T).FullName, typeof(TAttribute).FullName); + } return GetTypesInternal( typeof(T), @@ -278,10 +287,13 @@ public sealed class TypeLoader // warn if (!cache) { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "Running a non-cached, filter for discoverable type {TypeName} / attribute {AttributeName} (slowish).", typeof(T).FullName, typeof(TAttribute).FullName); + } } // filter the cached discovered types (and maybe cache the result) @@ -318,9 +330,12 @@ public sealed class TypeLoader if (!cache) { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "Running a full, non-cached, scan for types / attribute {AttributeName} (slow).", typeof(TAttribute).FullName); + } } return GetTypesInternal( @@ -376,7 +391,10 @@ public sealed class TypeLoader if (typeList != null) { // need to put some logging here to try to figure out why this is happening: http://issues.umbraco.org/issue/U4-3505 - _logger.LogDebug("Getting {TypeName}: found a cached type list.", GetName(baseType, attributeType)); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Getting {TypeName}: found a cached type list.", GetName(baseType, attributeType)); + } return typeList.Types; } @@ -384,7 +402,10 @@ public sealed class TypeLoader typeList = new TypeList(baseType, attributeType); // either we had to scan, or we could not get the types from the cache file - scan now - _logger.LogDebug("Getting {TypeName}: " + action + ".", GetName(baseType, attributeType)); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Getting {TypeName}: " + action + ".", GetName(baseType, attributeType)); + } foreach (Type t in finder()) { @@ -399,12 +420,17 @@ public sealed class TypeLoader { _types[listKey] = typeList; } - - _logger.LogDebug("Got {TypeName}, caching ({CacheType}).", GetName(baseType, attributeType), added.ToString().ToLowerInvariant()); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Got {TypeName}, caching ({CacheType}).", GetName(baseType, attributeType), added.ToString().ToLowerInvariant()); + } } else { - _logger.LogDebug("Got {TypeName}.", GetName(baseType, attributeType)); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Got {TypeName}.", GetName(baseType, attributeType)); + } } return typeList.Types; diff --git a/src/Umbraco.Core/ConventionsHelper.cs b/src/Umbraco.Core/ConventionsHelper.cs index 7d79338142..697b3904f5 100644 --- a/src/Umbraco.Core/ConventionsHelper.cs +++ b/src/Umbraco.Core/ConventionsHelper.cs @@ -6,17 +6,5 @@ namespace Umbraco.Cms.Core; public static class ConventionsHelper { public static Dictionary GetStandardPropertyTypeStubs(IShortStringHelper shortStringHelper) => - new() - { - { - Constants.Conventions.Member.Comments, - new PropertyType( - shortStringHelper, - Constants.PropertyEditors.Aliases.TextArea, - ValueStorageType.Ntext, - true, - Constants.Conventions.Member.Comments) - { Name = Constants.Conventions.Member.CommentsLabel } - }, - }; + new(); } diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/bs.xml b/src/Umbraco.Core/EmbeddedResources/Lang/bs.xml index f7b763bdfa..c22086a81a 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/bs.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/bs.xml @@ -2238,8 +2238,8 @@ Da upravljate svojom web lokacijom, jednostavno otvorite Umbraco backoffice i po Neaktivan Ime (A-Z) Ime (Z-A) - Najnovije - Najstarije + Najstarije + Najnovije Zadnja prijava Nijedna korisnička grupa nije dodana Ako želite da onemogućite ovog dvofaktorskog provajdera, onda morate uneti kod prikazan na vašem uređaju za autentifikaciju: diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/cs.xml b/src/Umbraco.Core/EmbeddedResources/Lang/cs.xml index 09c679475d..26c7087c80 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/cs.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/cs.xml @@ -1869,8 +1869,8 @@ Neaktivní Jméno (A-Z) Jméno (Z-A) - Nejnovější - Nejstarší + Nejstarší + Nejnovější Poslední přihlášení Nebyly přidány žádné skupiny uživatelů diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml index 50e4667078..5cc91c1ae5 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml @@ -607,6 +607,8 @@ Dette index kan ikke genbygges for det ikke har nogen IIndexPopulator + Der blev ikke fundet nogen resultater + Viser %0% - %1% af %2% resultat(er) - Side %3% af %4% Indtast dit brugernavn @@ -1927,8 +1929,8 @@ Mange hilsner fra Umbraco robotten Inaktiv Navn (A-Å) Navn (Å-A) - Nyeste - Ældste + Ældste + Nyeste Sidst logget ind Ingen brugere er blevet tilføjet Hvis du ønsker at slå denne totrinsbekræftelse fra, så skal du nu indtaste koden fra din enhed: diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/de.xml b/src/Umbraco.Core/EmbeddedResources/Lang/de.xml index 310516a6bb..4bcf80994e 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/de.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/de.xml @@ -2179,8 +2179,8 @@ Nicht aktiv Name (A-Z) Name (Z-A) - Newest - Oldest + Oldest + Newest Last login diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml index 56a0838896..3b8b70c360 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml @@ -628,6 +628,8 @@ This index cannot be rebuilt because it has no assigned IIndexPopulator + No results were found + Showing %0% - %1% of %2% result(s) - Page %3% of %4% Enter your username @@ -2246,8 +2248,8 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Inactive Name (A-Z) Name (Z-A) - Newest - Oldest + Oldest + Newest Last login No user groups have been added If you wish to disable this two-factor provider, then you must enter the code shown on your authentication device: diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml index 035d0e42f1..9136f09c5e 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml @@ -644,6 +644,8 @@ This index cannot be rebuilt because it has no assigned IIndexPopulator + No results were found + Showing %0% - %1% of %2% result(s) - Page %3% of %4% Enter your username @@ -2343,8 +2345,8 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Inactive Name (A-Z) Name (Z-A) - Newest - Oldest + Oldest + Newest Last login No user groups have been added If you wish to disable this two-factor provider, then you must enter the code shown on your authentication device: diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/fr.xml b/src/Umbraco.Core/EmbeddedResources/Lang/fr.xml index 1576cedbe4..55dad6e986 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/fr.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/fr.xml @@ -1910,8 +1910,8 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à Inactif Nom (A-Z) Nom (Z-A) - Plus récent - Plus ancien + Plus ancien + Plus récent Dernière connexion diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/it.xml b/src/Umbraco.Core/EmbeddedResources/Lang/it.xml index 5e45248d48..b1ac0385e1 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/it.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/it.xml @@ -2319,8 +2319,8 @@ Per gestire il tuo sito web, è sufficiente aprire il backoffice di Umbraco e in Inattivi Nome (A-Z) Nome (Z-A) - - + + Ultimo login Non sono stati aggiunti gruppi di utenti diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/nl.xml b/src/Umbraco.Core/EmbeddedResources/Lang/nl.xml index dbacedc658..1b9d2882ab 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/nl.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/nl.xml @@ -2059,8 +2059,8 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Inactief Naam (A-Z) Naam (Z-A) - Nieuwste - Oudste + Oudste + Nieuwste Laatste login Er zijn geen gebruikersgroepen toegevoegd diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/pt.xml b/src/Umbraco.Core/EmbeddedResources/Lang/pt.xml index f6cdb21208..29ae77a2d8 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/pt.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/pt.xml @@ -822,6 +822,8 @@ Você pode publicar esta página e todas suas sub-páginas ao selecionar pub Tipo de usuário Tipos de usuários Escrevente + Mais antigo + Mais recente Selecionar tudo diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/sv.xml b/src/Umbraco.Core/EmbeddedResources/Lang/sv.xml index ec52c0343c..0d8050da52 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/sv.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/sv.xml @@ -97,6 +97,13 @@ Sortera Historik (alla varianter) + + Lägg till innehåll + Lägg till %0% + Du har gjort ändringar i detta innehåll. Är du säker på att du vill ta bort dem? + Ignorera skapandet + + %0%]]> Tom @@ -108,6 +115,7 @@ Rensa urval + Bekräfta Fetstil Minska indrag Infoga formulärfält @@ -140,6 +148,7 @@ Välj stil Visa stil Infoga tabell + Skicka Du har inte konfigurerat några giltiga färger @@ -204,6 +213,9 @@ Inga ändringar har gjorts Ej skapad + + Ja, ta bort + Var vill du skapa den nya %0% Skapa innehåll under @@ -264,6 +276,7 @@ Hantera domännamn Stäng fönstret Är du säker på att du vill ta bort + %0% av %1% objekt]]> Är du säker på att du vill avaktivera Är du säker? Är du säker? @@ -389,6 +402,7 @@ Tar bort... Design Dimensioner + Ignorera Ner Ladda ned Redigera @@ -669,6 +683,9 @@ Publicera %0% och alla dess underordnade sidor ok för att publicera %0%. Därmed blir innehållet publikt.

Du kan publicera denna sida och alla dess undersidor genom att kryssa i publicera alla undersidor. ]]>
+ + Följande objekt refereras till + ange en extern länk ange en intern sida @@ -1001,8 +1018,8 @@ Skribent Din nuvarande historik Din profil - Nyast - Äldst + Äldst + Nyast Senaste login diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/tr.xml b/src/Umbraco.Core/EmbeddedResources/Lang/tr.xml index 1195378516..ebd284465f 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/tr.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/tr.xml @@ -1996,8 +1996,8 @@ Web sitenizi yönetmek için, Umbraco'nun arka ofisini açın ve içerik eklemey Etkin Değil Ad (AZ) Ad (ZA) - En yeni - En eski + En eski + En yeni Son giriş Hiçbir kullanıcı grubu eklenmedi diff --git a/src/Umbraco.Core/Events/UserNotificationsHandler.cs b/src/Umbraco.Core/Events/UserNotificationsHandler.cs index 4b581788e8..6d15ec36aa 100644 --- a/src/Umbraco.Core/Events/UserNotificationsHandler.cs +++ b/src/Umbraco.Core/Events/UserNotificationsHandler.cs @@ -192,8 +192,11 @@ public sealed class UserNotificationsHandler : // if there is no current user, then use the admin if (user == null) { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "There is no current Umbraco user logged in, the notifications will be sent from the administrator"); + } user = _userService.GetUserById(Constants.Security.SuperUserId); if (user == null) { diff --git a/src/Umbraco.Core/IO/FileSystems.cs b/src/Umbraco.Core/IO/FileSystems.cs index 2a5fa685df..69b66dc270 100644 --- a/src/Umbraco.Core/IO/FileSystems.cs +++ b/src/Umbraco.Core/IO/FileSystems.cs @@ -283,8 +283,10 @@ namespace Umbraco.Cms.Core.IO } _shadowCurrentId = id; - - _logger.LogDebug("Shadow '{ShadowId}'", _shadowCurrentId); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Shadow '{ShadowId}'", _shadowCurrentId); + } foreach (ShadowWrapper wrapper in _shadowWrappers) { @@ -307,8 +309,10 @@ namespace Umbraco.Cms.Core.IO { throw new InvalidOperationException("Not the current shadow."); } - - _logger.LogDebug("UnShadow '{ShadowId}' {Status}", id, completed ? "complete" : "abort"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("UnShadow '{ShadowId}' {Status}", id, completed ? "complete" : "abort"); + } var exceptions = new List(); foreach (ShadowWrapper wrapper in _shadowWrappers) diff --git a/src/Umbraco.Core/Logging/DisposableTimer.cs b/src/Umbraco.Core/Logging/DisposableTimer.cs index b153e096c4..02bb52a19d 100644 --- a/src/Umbraco.Core/Logging/DisposableTimer.cs +++ b/src/Umbraco.Core/Logging/DisposableTimer.cs @@ -53,14 +53,21 @@ public class DisposableTimer : DisposableObjectSlim case LogLevel.Debug: if (startMessageArgs == null) { - logger.LogDebug("{StartMessage} [Timing {TimingId}]", startMessage, _timingId); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + logger.LogDebug("{StartMessage} [Timing {TimingId}]", startMessage, _timingId); + } } else { var args = new object[startMessageArgs.Length + 1]; startMessageArgs.CopyTo(args, 0); - args[startMessageArgs.Length] = _timingId; - logger.LogDebug(startMessage + " [Timing {TimingId}]", args); + args[^1] = _timingId; + + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + logger.LogDebug(startMessage + " [Timing {TimingId}]", args); + } } break; @@ -73,7 +80,7 @@ public class DisposableTimer : DisposableObjectSlim { var args = new object[startMessageArgs.Length + 1]; startMessageArgs.CopyTo(args, 0); - args[startMessageArgs.Length] = _timingId; + args[^1] = _timingId; logger.LogInformation(startMessage + " [Timing {TimingId}]", args); } @@ -127,8 +134,8 @@ public class DisposableTimer : DisposableObjectSlim { var args = new object[_failMessageArgs.Length + 2]; _failMessageArgs.CopyTo(args, 0); - args[_failMessageArgs.Length - 1] = Stopwatch.ElapsedMilliseconds; - args[_failMessageArgs.Length] = _timingId; + args[^2] = Stopwatch.ElapsedMilliseconds; + args[^1] = _timingId; _logger.LogError(_failException, _failMessage + " ({Duration}ms) [Timing {TimingId}]", args); } } @@ -139,19 +146,25 @@ public class DisposableTimer : DisposableObjectSlim case LogLevel.Debug: if (_endMessageArgs == null) { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "{EndMessage} ({Duration}ms) [Timing {TimingId}]", _endMessage, Stopwatch.ElapsedMilliseconds, _timingId); + } } else { var args = new object[_endMessageArgs.Length + 2]; _endMessageArgs.CopyTo(args, 0); - args[^1] = Stopwatch.ElapsedMilliseconds; - args[args.Length] = _timingId; - _logger.LogDebug(_endMessage + " ({Duration}ms) [Timing {TimingId}]", args); + args[^2] = Stopwatch.ElapsedMilliseconds; + args[^1] = _timingId; + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug(_endMessage + " ({Duration}ms) [Timing {TimingId}]", args); + } } break; @@ -168,8 +181,8 @@ public class DisposableTimer : DisposableObjectSlim { var args = new object[_endMessageArgs.Length + 2]; _endMessageArgs.CopyTo(args, 0); - args[_endMessageArgs.Length - 1] = Stopwatch.ElapsedMilliseconds; - args[_endMessageArgs.Length] = _timingId; + args[^2] = Stopwatch.ElapsedMilliseconds; + args[^1] = _timingId; _logger.LogInformation(_endMessage + " ({Duration}ms) [Timing {TimingId}]", args); } diff --git a/src/Umbraco.Core/Logging/IProfilingLogger.cs b/src/Umbraco.Core/Logging/IProfilingLogger.cs index 92c4d55f0c..77c4c869f6 100644 --- a/src/Umbraco.Core/Logging/IProfilingLogger.cs +++ b/src/Umbraco.Core/Logging/IProfilingLogger.cs @@ -62,4 +62,11 @@ public interface IProfilingLogger object[]? startMessageArgs = null, object[]? endMessageArgs = null, object[]? failMessageArgs = null); + + /// + /// Checks if the given logLevel is enabled. + /// + /// Level to be checked. + /// true if enabled. + bool IsEnabled(LogLevel logLevel) => true; } diff --git a/src/Umbraco.Core/Logging/LogProfiler.cs b/src/Umbraco.Core/Logging/LogProfiler.cs index 0504a2a1ae..84a57979bf 100644 --- a/src/Umbraco.Core/Logging/LogProfiler.cs +++ b/src/Umbraco.Core/Logging/LogProfiler.cs @@ -15,7 +15,10 @@ public class LogProfiler : IProfiler /// public IDisposable Step(string name) { - _logger.LogDebug("Begin: {ProfileName}", name); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Begin: {ProfileName}", name); + } return new LightDisposableTimer(duration => _logger.LogInformation("End {ProfileName} ({ProfileDuration}ms)", name, duration)); } diff --git a/src/Umbraco.Core/Logging/ProfilingLogger.cs b/src/Umbraco.Core/Logging/ProfilingLogger.cs index 997f139539..9c5c8bc17e 100644 --- a/src/Umbraco.Core/Logging/ProfilingLogger.cs +++ b/src/Umbraco.Core/Logging/ProfilingLogger.cs @@ -141,5 +141,26 @@ public sealed class ProfilingLogger : IProfilingLogger public void LogTrace(string messageTemplate, params object[] propertyValues) => Logger.LogTrace(messageTemplate, propertyValues); + ////> + public bool IsEnabled(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Verbose: + return Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Trace); + case LogLevel.Debug: + return Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug); + case LogLevel.Information: + return Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information); + case LogLevel.Warning: + return Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Warning); + case LogLevel.Error: + return Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Error); + case LogLevel.Fatal: + return Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Critical); + default: + return true; + } + } #endregion } diff --git a/src/Umbraco.Core/Models/ContentEditing/SearchResults.cs b/src/Umbraco.Core/Models/ContentEditing/SearchResults.cs index fb7b0fc101..f847e6922e 100644 --- a/src/Umbraco.Core/Models/ContentEditing/SearchResults.cs +++ b/src/Umbraco.Core/Models/ContentEditing/SearchResults.cs @@ -5,6 +5,9 @@ namespace Umbraco.Cms.Core.Models.ContentEditing; [DataContract(Name = "results", Namespace = "")] public class SearchResults { + [DataMember(Name = "pageSize")] + public int PageSize { get; set; } + [DataMember(Name = "totalRecords")] public long TotalRecords { get; set; } diff --git a/src/Umbraco.Core/Notifications/ContentPublishedNotification.cs b/src/Umbraco.Core/Notifications/ContentPublishedNotification.cs index a5929a393f..7c82561785 100644 --- a/src/Umbraco.Core/Notifications/ContentPublishedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentPublishedNotification.cs @@ -21,8 +21,11 @@ public sealed class ContentPublishedNotification : EnumerableObjectNotification< { } - /// - /// Gets a enumeration of with the published entities. + public ContentPublishedNotification(IEnumerable target, EventMessages messages, bool includeDescendants) + : base(target, messages) => IncludeDescendants = includeDescendants; + /// public IEnumerable PublishedEntities => Target; + + public bool IncludeDescendants { get; set; } } diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs index 2e26894d23..3066086e7b 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs @@ -1,4 +1,6 @@ -using Umbraco.Cms.Core.Models.PublishedContent; +using System.Collections.Generic; +using System.Xml; +using Umbraco.Cms.Core.Models.PublishedContent; namespace Umbraco.Cms.Core.PropertyEditors; @@ -48,7 +50,29 @@ public abstract class PropertyValueConverterBase : IPropertyValueConverter /// public virtual object? ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object? inter, bool preview) - => inter?.ToString() ?? string.Empty; + { + var d = new XmlDocument(); + XmlElement e = d.CreateElement("values"); + d.AppendChild(e); + + if (inter is IEnumerable collection) + { + foreach (var value in collection) + { + XmlElement ee = d.CreateElement("value"); + ee.InnerText = value; + e.AppendChild(ee); + } + } + else + { + XmlElement ee = d.CreateElement("value"); + ee.InnerText = inter?.ToString() ?? string.Empty; + e.AppendChild(ee); + } + + return d.CreateNavigator(); + } [Obsolete( "This method is not part of the IPropertyValueConverter contract, therefore not used and will be removed in future versions; use IsValue instead.")] diff --git a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs index 173515a001..97e2f73e88 100644 --- a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs @@ -137,9 +137,12 @@ public class DefaultUrlProvider : IUrlProvider { if (string.IsNullOrWhiteSpace(route)) { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "Couldn't find any page with nodeId={NodeId}. This is most likely caused by the page not being published.", id); + } return null; } diff --git a/src/Umbraco.Core/Routing/UrlProviderExtensions.cs b/src/Umbraco.Core/Routing/UrlProviderExtensions.cs index 4142b56a2f..1c86502cd1 100644 --- a/src/Umbraco.Core/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Core/Routing/UrlProviderExtensions.cs @@ -250,9 +250,12 @@ public static class UrlProviderExtensions if (!pcr.HasPublishedContent()) { - const string logMsg = nameof(DetectCollisionAsync) + + if (logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + const string logMsg = nameof(DetectCollisionAsync) + " did not resolve a content item for original url: {Url}, translated to {TranslatedUrl} and culture: {Culture}"; - logger.LogDebug(logMsg, url, uri, culture); + logger.LogDebug(logMsg, url, uri, culture); + } var urlInfo = UrlInfo.Message(textService.Localize("content", "routeErrorCannotRoute"), culture); return Attempt.Succeed(urlInfo); diff --git a/src/Umbraco.Core/Runtime/MainDom.cs b/src/Umbraco.Core/Runtime/MainDom.cs index 83736914a2..4d4c7ab2e8 100644 --- a/src/Umbraco.Core/Runtime/MainDom.cs +++ b/src/Umbraco.Core/Runtime/MainDom.cs @@ -108,7 +108,10 @@ namespace Umbraco.Cms.Core.Runtime lock (_locko) { - _logger.LogDebug("Signaled ({Signaled}) ({SignalSource})", _signaled ? "again" : "first", source); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Signaled ({Signaled}) ({SignalSource})", _signaled ? "again" : "first", source); + } if (_signaled) { return; @@ -136,8 +139,10 @@ namespace Umbraco.Cms.Core.Runtime continue; } } - - _logger.LogDebug("Stopped ({SignalSource})", source); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Stopped ({SignalSource})", source); + } } finally { @@ -201,7 +206,10 @@ namespace Umbraco.Cms.Core.Runtime } else { - _logger.LogDebug("Listening task completed with {TaskStatus}", _listenTask.Status); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Listening task completed with {TaskStatus}", _listenTask.Status); + } } OnSignal("signal"); diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 5e092db621..c624a7b8c1 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -2164,7 +2164,7 @@ public class ContentService : RepositoryService, IContentService // (SaveAndPublishBranchOne does *not* do it) scope.Notifications.Publish( new ContentTreeChangeNotification(document, TreeChangeTypes.RefreshBranch, eventMessages)); - scope.Notifications.Publish(new ContentPublishedNotification(publishedDocuments, eventMessages)); + scope.Notifications.Publish(new ContentPublishedNotification(publishedDocuments, eventMessages, true)); scope.Complete(); } diff --git a/src/Umbraco.Core/Services/ContentVersionService.cs b/src/Umbraco.Core/Services/ContentVersionService.cs index 24443a3957..f0c92db28f 100644 --- a/src/Umbraco.Core/Services/ContentVersionService.cs +++ b/src/Umbraco.Core/Services/ContentVersionService.cs @@ -129,8 +129,10 @@ internal class ContentVersionService : IContentVersionService { return Array.Empty(); } - - _logger.LogDebug("Discovered {count} candidate(s) for ContentVersion cleanup", allHistoricVersions.Count); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Discovered {count} candidate(s) for ContentVersion cleanup", allHistoricVersions.Count); + } versionsToDelete = new List(allHistoricVersions.Count); IEnumerable filteredContentVersions = @@ -143,7 +145,10 @@ internal class ContentVersionService : IContentVersionService if (scope.Notifications.PublishCancelable( new ContentDeletingVersionsNotification(version.ContentId, messages, version.VersionId))) { - _logger.LogDebug("Delete cancelled for ContentVersion [{versionId}]", version.VersionId); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Delete cancelled for ContentVersion [{versionId}]", version.VersionId); + } continue; } @@ -153,7 +158,10 @@ internal class ContentVersionService : IContentVersionService if (!versionsToDelete.Any()) { - _logger.LogDebug("No remaining ContentVersions for cleanup"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("No remaining ContentVersions for cleanup"); + } return Array.Empty(); } diff --git a/src/Umbraco.Core/Services/NotificationService.cs b/src/Umbraco.Core/Services/NotificationService.cs index ff857986b0..72d46b2fb6 100644 --- a/src/Umbraco.Core/Services/NotificationService.cs +++ b/src/Umbraco.Core/Services/NotificationService.cs @@ -568,7 +568,10 @@ public class NotificationService : INotificationService private void Process(BlockingCollection notificationRequests) => ThreadPool.QueueUserWorkItem(state => { - _logger.LogDebug("Begin processing notifications."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Begin processing notifications."); + } while (true) { // stay on for 8s @@ -578,7 +581,10 @@ public class NotificationService : INotificationService { _emailSender.SendAsync(request.Mail, Constants.Web.EmailTypes.Notification).GetAwaiter() .GetResult(); - _logger.LogDebug("Notification '{Action}' sent to {Username} ({Email})", request.Action, request.UserName, request.Email); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Notification '{Action}' sent to {Username} ({Email})", request.Action, request.UserName, request.Email); + } } catch (Exception ex) { @@ -597,8 +603,10 @@ public class NotificationService : INotificationService break; } } - - _logger.LogDebug("Done processing notifications."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Done processing notifications."); + } }); private class NotificationRequest diff --git a/src/Umbraco.Core/Templates/HtmlUrlParser.cs b/src/Umbraco.Core/Templates/HtmlUrlParser.cs index f4a817485d..16d27eb205 100644 --- a/src/Umbraco.Core/Templates/HtmlUrlParser.cs +++ b/src/Umbraco.Core/Templates/HtmlUrlParser.cs @@ -46,14 +46,17 @@ public sealed class HtmlUrlParser return text; } - using (DisposableTimer? timer = _profilingLogger.DebugDuration( + using (DisposableTimer? timer = !(_profilingLogger.IsEnabled(Core.Logging.LogLevel.Debug)) ? null : _profilingLogger.DebugDuration( typeof(IOHelper), "ResolveUrlsFromTextString starting", "ResolveUrlsFromTextString complete")) { // find all relative URLs (ie. URLs that contain ~) MatchCollection tags = ResolveUrlPattern.Matches(text); - _logger.LogDebug("After regex: {Duration} matched: {TagsCount}", timer?.Stopwatch.ElapsedMilliseconds, tags.Count); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("After regex: {Duration} matched: {TagsCount}", timer?.Stopwatch.ElapsedMilliseconds, tags.Count); + } foreach (Match tag in tags) { var url = string.Empty; diff --git a/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs index 7aa2eea5fc..3053da44cf 100644 --- a/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs @@ -135,8 +135,10 @@ public class UmbracoContentIndex : UmbracoExamineIndex, IUmbracoContentIndex IBooleanOperation? filtered = c.NativeQuery(rawQuery); IOrdering? selectedFields = filtered.SelectFields(_idOnlyFieldSet); ISearchResults? results = selectedFields.Execute(); - - _logger.LogDebug("DeleteFromIndex with query: {Query} (found {TotalItems} results)", rawQuery, results.TotalItemCount); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("DeleteFromIndex with query: {Query} (found {TotalItems} results)", rawQuery, results.TotalItemCount); + } var toRemove = results.Select(x => x.Id).ToList(); // delete those descendants (ensure base. is used here so we aren't calling ourselves!) diff --git a/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs index dd02ee5754..eb9e9f135f 100644 --- a/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs @@ -68,7 +68,10 @@ public class ContentIndexPopulator : IndexPopulator { if (indexes.Count == 0) { - _logger.LogDebug($"{nameof(PopulateIndexes)} called with no indexes to populate. Typically means no index is registered with this populator."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug($"{nameof(PopulateIndexes)} called with no indexes to populate. Typically means no index is registered with this populator."); + } return; } diff --git a/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs b/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs index 7f4f3cd50e..ec4dff77f4 100644 --- a/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs @@ -19,10 +19,28 @@ public class ContentValueSetValidator : ValueSetValidator, IContentValueSetValid // used for tests public ContentValueSetValidator(bool publishedValuesOnly, int? parentId = null, IEnumerable? includeItemTypes = null, IEnumerable? excludeItemTypes = null) - : this(publishedValuesOnly, true, null, null, parentId, includeItemTypes, excludeItemTypes) + : this(publishedValuesOnly, true, null, null, parentId, includeItemTypes, excludeItemTypes, null, null) { } + [Obsolete("Use the overload accepting includeFields and excludeFields instead. This overload will be removed in Umbraco 14.")] + public ContentValueSetValidator( + bool publishedValuesOnly, + bool supportProtectedContent, + IPublicAccessService? publicAccessService, + IScopeProvider? scopeProvider, + int? parentId, + IEnumerable? includeItemTypes, + IEnumerable? excludeItemTypes) + : base(includeItemTypes, excludeItemTypes, null, null) + { + PublishedValuesOnly = publishedValuesOnly; + SupportProtectedContent = supportProtectedContent; + ParentId = parentId; + _publicAccessService = publicAccessService; + _scopeProvider = scopeProvider; + } + public ContentValueSetValidator( bool publishedValuesOnly, bool supportProtectedContent, @@ -30,8 +48,10 @@ public class ContentValueSetValidator : ValueSetValidator, IContentValueSetValid IScopeProvider? scopeProvider, int? parentId = null, IEnumerable? includeItemTypes = null, - IEnumerable? excludeItemTypes = null) - : base(includeItemTypes, excludeItemTypes, null, null) + IEnumerable? excludeItemTypes = null, + IEnumerable? includeFields = null, + IEnumerable? excludeFields = null) + : base(includeItemTypes, excludeItemTypes, includeFields, excludeFields) { PublishedValuesOnly = publishedValuesOnly; SupportProtectedContent = supportProtectedContent; diff --git a/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs index a1c70d0ec3..5394cdc275 100644 --- a/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs @@ -88,7 +88,10 @@ public class ExamineIndexRebuilder : IIndexRebuilder if (useBackgroundThread) { - _logger.LogDebug($"Queuing background job for {nameof(RebuildIndexes)}."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug($"Queuing background job for {nameof(RebuildIndexes)}."); + } _backgroundTaskQueue.QueueBackgroundWorkItem( cancellationToken => diff --git a/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs index 20df2c229e..6f4a4db4a3 100644 --- a/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs @@ -38,8 +38,11 @@ public class MediaIndexPopulator : IndexPopulator { if (indexes.Count == 0) { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( $"{nameof(PopulateIndexes)} called with no indexes to populate. Typically means no index is registered with this populator."); + } return; } diff --git a/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs b/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs index 1b62e8e31d..37eeb668f9 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs @@ -60,10 +60,16 @@ public class ContentVersionCleanup : RecurringHostedServiceBase switch (_serverRoleAccessor.CurrentServerRole) { case ServerRole.Subscriber: - _logger.LogDebug("Does not run on subscriber servers"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run on subscriber servers"); + } return Task.CompletedTask; case ServerRole.Unknown: - _logger.LogDebug("Does not run on servers with unknown role"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run on servers with unknown role"); + } return Task.CompletedTask; case ServerRole.Single: case ServerRole.SchedulingPublisher: @@ -74,7 +80,10 @@ public class ContentVersionCleanup : RecurringHostedServiceBase // Ensure we do not run if not main domain, but do NOT lock it if (!_mainDom.IsMainDom) { - _logger.LogDebug("Does not run if not MainDom"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run if not MainDom"); + } return Task.FromResult(false); // do NOT repeat, going down } @@ -86,7 +95,10 @@ public class ContentVersionCleanup : RecurringHostedServiceBase } else { - _logger.LogDebug("Task complete, no items were Deleted"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Task complete, no items were Deleted"); + } } return Task.FromResult(true); diff --git a/src/Umbraco.Infrastructure/HostedServices/HealthCheckNotifier.cs b/src/Umbraco.Infrastructure/HostedServices/HealthCheckNotifier.cs index 30d164276a..e1a10d9f71 100644 --- a/src/Umbraco.Infrastructure/HostedServices/HealthCheckNotifier.cs +++ b/src/Umbraco.Infrastructure/HostedServices/HealthCheckNotifier.cs @@ -93,17 +93,26 @@ public class HealthCheckNotifier : RecurringHostedServiceBase switch (_serverRegistrar.CurrentServerRole) { case ServerRole.Subscriber: - _logger.LogDebug("Does not run on subscriber servers."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run on subscriber servers."); + } return; case ServerRole.Unknown: - _logger.LogDebug("Does not run on servers with unknown role."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run on servers with unknown role."); + } return; } // Ensure we do not run if not main domain, but do NOT lock it if (_mainDom.IsMainDom == false) { - _logger.LogDebug("Does not run if not MainDom."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run if not MainDom."); + } return; } @@ -111,7 +120,7 @@ public class HealthCheckNotifier : RecurringHostedServiceBase // checks can be making service/database calls so we want to ensure the CallContext/Ambient scope // isn't used since that can be problematic. using (ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true)) - using (_profilingLogger.DebugDuration("Health checks executing", "Health checks complete")) + using (!_profilingLogger.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration("Health checks executing", "Health checks complete")) { // Don't notify for any checks that are disabled, nor for any disabled just for notifications. Guid[] disabledCheckIds = _healthChecksSettings.Notification.DisabledChecks diff --git a/src/Umbraco.Infrastructure/HostedServices/KeepAlive.cs b/src/Umbraco.Infrastructure/HostedServices/KeepAlive.cs index b10f56cc74..5db59ff225 100644 --- a/src/Umbraco.Infrastructure/HostedServices/KeepAlive.cs +++ b/src/Umbraco.Infrastructure/HostedServices/KeepAlive.cs @@ -69,10 +69,16 @@ public class KeepAlive : RecurringHostedServiceBase switch (_serverRegistrar.CurrentServerRole) { case ServerRole.Subscriber: - _logger.LogDebug("Does not run on subscriber servers."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run on subscriber servers."); + } return; case ServerRole.Unknown: - _logger.LogDebug("Does not run on servers with unknown role."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run on servers with unknown role."); + } return; } @@ -83,7 +89,7 @@ public class KeepAlive : RecurringHostedServiceBase return; } - using (_profilingLogger.DebugDuration("Keep alive executing", "Keep alive complete")) + using (!_profilingLogger.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration("Keep alive executing", "Keep alive complete")) { var umbracoAppUrl = _hostingEnvironment.ApplicationMainUrl?.ToString(); if (umbracoAppUrl.IsNullOrWhiteSpace()) diff --git a/src/Umbraco.Infrastructure/HostedServices/LogScrubber.cs b/src/Umbraco.Infrastructure/HostedServices/LogScrubber.cs index b69342d25b..9ae0dfe656 100644 --- a/src/Umbraco.Infrastructure/HostedServices/LogScrubber.cs +++ b/src/Umbraco.Infrastructure/HostedServices/LogScrubber.cs @@ -63,23 +63,32 @@ public class LogScrubber : RecurringHostedServiceBase switch (_serverRegistrar.CurrentServerRole) { case ServerRole.Subscriber: - _logger.LogDebug("Does not run on subscriber servers."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run on subscriber servers."); + } return Task.CompletedTask; case ServerRole.Unknown: - _logger.LogDebug("Does not run on servers with unknown role."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run on servers with unknown role."); + } return Task.CompletedTask; } // Ensure we do not run if not main domain, but do NOT lock it if (_mainDom.IsMainDom == false) { - _logger.LogDebug("Does not run if not MainDom."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run if not MainDom."); + } return Task.CompletedTask; } // Ensure we use an explicit scope since we are running on a background thread. using (ICoreScope scope = _scopeProvider.CreateCoreScope()) - using (_profilingLogger.DebugDuration("Log scrubbing executing", "Log scrubbing complete")) + using (!_profilingLogger.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration("Log scrubbing executing", "Log scrubbing complete")) { _auditService.CleanLogs((int)_settings.MaxLogAge.TotalMinutes); _ = scope.Complete(); diff --git a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs index 47f1afe650..52a877a976 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs @@ -93,7 +93,10 @@ public class ReportSiteTask : RecurringHostedServiceBase // Silently swallow // The user does not need the logs being polluted if our service has fallen over or is down etc // Hence only logging this at a more verbose level (which users should not be using in production) - _logger.LogDebug("There was a problem sending a request to the Umbraco telemetry service"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("There was a problem sending a request to the Umbraco telemetry service"); + } } } } diff --git a/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs b/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs index d593124ccb..328cf5ee5c 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs @@ -62,24 +62,36 @@ public class ScheduledPublishing : RecurringHostedServiceBase switch (_serverRegistrar.CurrentServerRole) { case ServerRole.Subscriber: - _logger.LogDebug("Does not run on subscriber servers."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run on subscriber servers."); + } return Task.CompletedTask; case ServerRole.Unknown: - _logger.LogDebug("Does not run on servers with unknown role."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run on servers with unknown role."); + } return Task.CompletedTask; } // Ensure we do not run if not main domain, but do NOT lock it if (_mainDom.IsMainDom == false) { - _logger.LogDebug("Does not run if not MainDom."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run if not MainDom."); + } return Task.CompletedTask; } // Do NOT run publishing if not properly running if (_runtimeState.Level != RuntimeLevel.Run) { - _logger.LogDebug("Does not run if run level is not Run."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run if run level is not Run."); + } return Task.CompletedTask; } diff --git a/src/Umbraco.Infrastructure/HostedServices/TempFileCleanup.cs b/src/Umbraco.Infrastructure/HostedServices/TempFileCleanup.cs index 663a89b05a..cf46e38750 100644 --- a/src/Umbraco.Infrastructure/HostedServices/TempFileCleanup.cs +++ b/src/Umbraco.Infrastructure/HostedServices/TempFileCleanup.cs @@ -44,7 +44,10 @@ public class TempFileCleanup : RecurringHostedServiceBase // Ensure we do not run if not main domain if (_mainDom.IsMainDom == false) { - _logger.LogDebug("Does not run if not MainDom."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Does not run if not MainDom."); + } return Task.CompletedTask; } @@ -62,7 +65,10 @@ public class TempFileCleanup : RecurringHostedServiceBase switch (result.Status) { case CleanFolderResultStatus.FailedAsDoesNotExist: - _logger.LogDebug("The cleanup folder doesn't exist {Folder}", folder.FullName); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("The cleanup folder doesn't exist {Folder}", folder.FullName); + } break; case CleanFolderResultStatus.FailedWithException: foreach (CleanFolderResult.Error error in result.Errors!) @@ -77,7 +83,10 @@ public class TempFileCleanup : RecurringHostedServiceBase folder.Refresh(); // In case it's changed during runtime if (!folder.Exists) { - _logger.LogDebug("The cleanup folder doesn't exist {Folder}", folder.FullName); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("The cleanup folder doesn't exist {Folder}", folder.FullName); + } return; } diff --git a/src/Umbraco.Infrastructure/Install/UnattendedInstaller.cs b/src/Umbraco.Infrastructure/Install/UnattendedInstaller.cs index bf9817ca94..64cb6e8ff2 100644 --- a/src/Umbraco.Infrastructure/Install/UnattendedInstaller.cs +++ b/src/Umbraco.Infrastructure/Install/UnattendedInstaller.cs @@ -73,8 +73,10 @@ public class UnattendedInstaller : INotificationAsyncHandler( + using (!_profilingLogger.IsEnabled(Core.Logging.LogLevel.Verbose) ? null : _profilingLogger.TraceDuration( "Starting unattended upgrade.", "Unattended upgrade completed.")) { diff --git a/src/Umbraco.Infrastructure/Mail/EmailSender.cs b/src/Umbraco.Infrastructure/Mail/EmailSender.cs index 742075656d..03b46dbedc 100644 --- a/src/Umbraco.Infrastructure/Mail/EmailSender.cs +++ b/src/Umbraco.Infrastructure/Mail/EmailSender.cs @@ -83,18 +83,24 @@ public class EmailSender : IEmailSender // if a handler handled sending the email then don't continue. if (notification.IsHandled) { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "The email sending for {Subject} was handled by a notification handler", notification.Message.Subject); + } return; } } if (!_globalSettings.IsSmtpServerConfigured && !_globalSettings.IsPickupDirectoryLocationConfigured) { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "Could not send email for {Subject}. It was not handled by a notification handler and there is no SMTP configured.", message.Subject); + } return; } diff --git a/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs b/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs index b548112d8f..23de94e824 100644 --- a/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs +++ b/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; +using Umbraco.Cms.Infrastructure.Migrations.Expressions.Execute.Expressions; using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; using Umbraco.Extensions; @@ -110,6 +111,25 @@ namespace Umbraco.Cms.Infrastructure.Migrations return indexes.Any(x => x.Item2.InvariantEquals(indexName)); } + protected void CreateIndex(string toCreate) + { + TableDefinition tableDef = DefinitionFactory.GetTableDefinition(typeof(T), Context.SqlContext.SqlSyntax); + IndexDefinition index = tableDef.Indexes.First(x => x.Name == toCreate); + new ExecuteSqlStatementExpression(Context) { SqlStatement = Context.SqlContext.SqlSyntax.Format(index) } + .Execute(); + } + + protected void DeleteIndex(string toDelete) + { + if (!IndexExists(toDelete)) + { + return; + } + + TableDefinition tableDef = DefinitionFactory.GetTableDefinition(typeof(T), Context.SqlContext.SqlSyntax); + Delete.Index(toDelete).OnTable(tableDef.Name).Do(); + } + protected bool PrimaryKeyExists(string tableName, string primaryKeyName) { return SqlSyntax.DoesPrimaryKeyExist(Context.Database, tableName, primaryKeyName); diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index 48e44bffe0..e2974ffe1d 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -78,7 +78,10 @@ public class UmbracoPlan : MigrationPlan To("{888A0D5D-51E4-4C7E-AA0A-01306523C7FB}"); To("{539F2F83-FBA7-4C48-81A3-75081A56BB9D}"); - // To 13.0.0 + // To 12.1.0 + To("{1187192D-EDB5-4619-955D-91D48D738871}"); + + // To 14.0.0 To("{419827A0-4FCE-464B-A8F3-247C6092AF55}"); To("{5F15A1CC-353D-4889-8C7E-F303B4766196}"); To("{69E12556-D9B3-493A-8E8A-65EC89FB658D}"); diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_1_0/TablesIndexesImprovement.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_1_0/TablesIndexesImprovement.cs new file mode 100644 index 0000000000..5483f25593 --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_1_0/TablesIndexesImprovement.cs @@ -0,0 +1,65 @@ +using Umbraco.Cms.Infrastructure.Persistence.Dtos; + +namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_12_1_0; + +public class TablesIndexesImprovement : MigrationBase +{ + public TablesIndexesImprovement(IMigrationContext context) : base(context) + { + } + + protected override void Migrate() + { + var nodeDtoTrashedIndex = $"IX_{NodeDto.TableName}_ObjectType_trashed_sorted"; + DeleteIndex(nodeDtoTrashedIndex); + CreateIndex(nodeDtoTrashedIndex); + + var redirectUrlCreateDateUtcIndex = $"IX_{RedirectUrlDto.TableName}_culture_hash"; + DeleteIndex(redirectUrlCreateDateUtcIndex); + CreateIndex(redirectUrlCreateDateUtcIndex); + + var contentVersionCultureVariationVersionIdIndex = $"IX_{ContentVersionCultureVariationDto.TableName}_VersionId"; + DeleteIndex(contentVersionCultureVariationVersionIdIndex); + CreateIndex(contentVersionCultureVariationVersionIdIndex); + + var contentVersionDtoNodeIdV2Index = $"IX_{ContentVersionDto.TableName}_NodeId"; + DeleteIndex(contentVersionDtoNodeIdV2Index); + CreateIndex(contentVersionDtoNodeIdV2Index); + + var tagRelationshipDtoTagNodeIndex = $"IX_{TagRelationshipDto.TableName}_tagId_nodeId"; + DeleteIndex(tagRelationshipDtoTagNodeIndex); + CreateIndex(tagRelationshipDtoTagNodeIndex); + + var tagDtoLanguageGroupIndex = $"IX_{TagDto.TableName}_languageId_group"; + DeleteIndex(tagDtoLanguageGroupIndex); + CreateIndex(tagDtoLanguageGroupIndex); + + var documentVersionDtoIdPublishedIndex = $"IX_{DocumentVersionDto.TableName}_id_published"; + DeleteIndex(documentVersionDtoIdPublishedIndex); + CreateIndex(documentVersionDtoIdPublishedIndex); + + var documentVersionDtoPublishedIndex = $"IX_{DocumentVersionDto.TableName}_published"; + DeleteIndex(documentVersionDtoPublishedIndex); + CreateIndex(documentVersionDtoPublishedIndex); + + var logDtoDatestampIndex = $"IX_{LogDto.TableName}_datestamp"; + DeleteIndex(logDtoDatestampIndex); + CreateIndex(logDtoDatestampIndex); + + var logDtoDatestampHeaderIndex = $"IX_{LogDto.TableName}_datestamp_logheader"; + DeleteIndex(logDtoDatestampHeaderIndex); + CreateIndex(logDtoDatestampHeaderIndex); + + var propertyDataDtoVersionIdIndex = $"IX_{PropertyDataDto.TableName}_VersionId"; + DeleteIndex(propertyDataDtoVersionIdIndex); + CreateIndex(propertyDataDtoVersionIdIndex); + + var contentNuDtoPublishedIdIndex = $"IX_{ContentNuDto.TableName}_published"; + DeleteIndex(contentNuDtoPublishedIdIndex); + CreateIndex(contentNuDtoPublishedIdIndex); + + var nodeDtoParentIdNodeObjectTypeIndex = $"IX_{NodeDto.TableName}_parentId_nodeObjectType"; + DeleteIndex(nodeDtoParentIdNodeObjectTypeIndex); + CreateIndex(nodeDtoParentIdNodeObjectTypeIndex); + } +} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs index 422bbc1e8c..40ee54b42a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs @@ -17,8 +17,11 @@ public class PreValueMigratorCollection : BuilderCollectionBase x.CanMigrate(editorAlias)); - _logger.LogDebug("Getting migrator for \"{EditorAlias}\" = {MigratorType}", editorAlias, + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Getting migrator for \"{EditorAlias}\" = {MigratorType}", editorAlias, migrator == null ? "" : migrator.GetType().Name); + } return migrator; } } diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/AutoModelsNotificationHandler.cs b/src/Umbraco.Infrastructure/ModelsBuilder/AutoModelsNotificationHandler.cs index d879306226..b1d3ea48bb 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/AutoModelsNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/AutoModelsNotificationHandler.cs @@ -118,8 +118,10 @@ public sealed class AutoModelsNotificationHandler : INotificationHandler"); + } } return pendingMigrations; diff --git a/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/IndexAttribute.cs b/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/IndexAttribute.cs index 826e56ad89..0628da82aa 100644 --- a/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/IndexAttribute.cs +++ b/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/IndexAttribute.cs @@ -3,7 +3,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; /// /// Attribute that represents an Index /// -[AttributeUsage(AttributeTargets.Property)] +[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] public class IndexAttribute : Attribute { public IndexAttribute(IndexTypes indexType) => IndexType = indexType; diff --git a/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs b/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs index 32bdd213e6..ad043c08c4 100644 --- a/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs @@ -55,8 +55,14 @@ public static class DefinitionFactory } // Creates an index definition and adds it to the collection on the table definition - IndexAttribute? indexAttribute = propertyInfo.FirstAttribute(); - if (indexAttribute != null) + IEnumerable? indexAttributes = propertyInfo.MultipleAttribute(); + + if (indexAttributes == null) + { + continue; + } + + foreach (IndexAttribute indexAttribute in indexAttributes) { IndexDefinition indexDefinition = GetIndexDefinition(modelType, propertyInfo, indexAttribute, columnName, tableName); diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentNuDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentNuDto.cs index 6fa45d9cce..a3a978f2e4 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentNuDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentNuDto.cs @@ -10,12 +10,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; [ExplicitColumns] public class ContentNuDto { + public const string TableName = Constants.DatabaseSchema.Tables.NodeData; + [Column("nodeId")] [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsContentNu", OnColumns = "nodeId, published")] [ForeignKey(typeof(ContentDto), Column = "nodeId", OnDelete = Rule.Cascade)] public int NodeId { get; set; } [Column("published")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_published", ForColumns = "published,nodeId,rv", IncludeColumns = "dataRaw")] public bool Published { get; set; } /// diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCultureVariationDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCultureVariationDto.cs index 48c6ee97ef..b7d675f9a8 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCultureVariationDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCultureVariationDto.cs @@ -18,7 +18,7 @@ internal class ContentVersionCultureVariationDto [Column("versionId")] [ForeignKey(typeof(ContentVersionDto))] - [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_VersionId", ForColumns = "versionId,languageId")] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_VersionId", ForColumns = "versionId,languageId", IncludeColumns = "id,name,date,availableUserId")] public int VersionId { get; set; } [Column("languageId")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionDto.cs index 3a6aae2aff..07fe6d9a84 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionDto.cs @@ -19,7 +19,7 @@ public class ContentVersionDto [Column("nodeId")] [ForeignKey(typeof(ContentDto))] - [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_NodeId", ForColumns = "nodeId,current", IncludeColumns = "id,versionDate,text,userId")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_NodeId", ForColumns = "nodeId,current", IncludeColumns = "id,versionDate,text,userId,preventCleanup")] public int NodeId { get; set; } [Column("versionDate")] // TODO: db rename to 'updateDate' diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/DocumentVersionDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/DocumentVersionDto.cs index 75dea080a2..f99a6676be 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/DocumentVersionDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/DocumentVersionDto.cs @@ -9,11 +9,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; [ExplicitColumns] public class DocumentVersionDto { - private const string TableName = Constants.DatabaseSchema.Tables.DocumentVersion; + public const string TableName = Constants.DatabaseSchema.Tables.DocumentVersion; [Column("id")] [PrimaryKeyColumn(AutoIncrement = false)] [ForeignKey(typeof(ContentVersionDto))] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_id_published", ForColumns = "id,published", IncludeColumns = "templateId")] public int Id { get; set; } [Column("templateId")] @@ -22,6 +23,7 @@ public class DocumentVersionDto public int? TemplateId { get; set; } [Column("published")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_published", ForColumns = "published", IncludeColumns = "id,templateId")] public bool Published { get; set; } [ResultColumn] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/LogDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/LogDto.cs index b464d6628d..89bfeb8612 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/LogDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/LogDto.cs @@ -35,14 +35,14 @@ internal class LogDto [NullSetting(NullSetting = NullSettings.Null)] public string? EntityType { get; set; } - // TODO: Should we have an index on this since we allow searching on it? [Column("Datestamp")] [Constraint(Default = SystemMethods.CurrentDateTime)] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_datestamp", ForColumns = "Datestamp,userId,NodeId")] public DateTime Datestamp { get; set; } - // TODO: Should we have an index on this since we allow searching on it? [Column("logHeader")] [Length(50)] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_datestamp_logheader", ForColumns = "Datestamp,logHeader")] public string Header { get; set; } = null!; [Column("logComment")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/NodeDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/NodeDto.cs index d11ebc96ce..5bf3a26207 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/NodeDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/NodeDto.cs @@ -26,7 +26,7 @@ public class NodeDto [Column("parentId")] [ForeignKey(typeof(NodeDto))] - [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_ParentId")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_parentId_nodeObjectType", ForColumns = "parentID,nodeObjectType", IncludeColumns = "trashed,nodeUser,level,path,sortOrder,uniqueID,text,createDate")] public int ParentId { get; set; } // NOTE: This index is primarily for the nucache data lookup, see https://github.com/umbraco/Umbraco-CMS/pull/8365#issuecomment-673404177 @@ -40,6 +40,7 @@ public class NodeDto public string Path { get; set; } = null!; [Column("sortOrder")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_ObjectType_trashed_sorted", ForColumns = "nodeObjectType,trashed,sortOrder,id", IncludeColumns = "uniqueID,parentID,level,path,nodeUser,text,createDate")] public int SortOrder { get; set; } [Column("trashed")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/RedirectUrlDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/RedirectUrlDto.cs index b377b49177..453ab1e308 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/RedirectUrlDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/RedirectUrlDto.cs @@ -9,6 +9,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; [ExplicitColumns] internal class RedirectUrlDto { + public const string TableName = Constants.DatabaseSchema.Tables.RedirectUrl; + public RedirectUrlDto() => CreateDateUtc = DateTime.UtcNow; // notes @@ -31,6 +33,7 @@ internal class RedirectUrlDto [Column("createDateUtc")] [NullSetting(NullSetting = NullSettings.NotNull)] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_culture_hash", ForColumns = "createDateUtc", IncludeColumns = "culture,url,urlHash,contentKey")] public DateTime CreateDateUtc { get; set; } [Column("url")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/TagDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/TagDto.cs index cc8b80c777..d2a68cf971 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/TagDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/TagDto.cs @@ -17,6 +17,7 @@ internal class TagDto [Column("group")] [Length(100)] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_languageId_group", ForColumns = "languageId,group", IncludeColumns = "id,tag")] public string Group { get; set; } = null!; [Column("languageId")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/TagRelationshipDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/TagRelationshipDto.cs index 3799679e4d..f5f9fd0b38 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/TagRelationshipDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/TagRelationshipDto.cs @@ -14,6 +14,7 @@ internal class TagRelationshipDto [Column("nodeId")] [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsTagRelationship", OnColumns = "nodeId, propertyTypeId, tagId")] [ForeignKey(typeof(ContentDto), Name = "FK_cmsTagRelationship_cmsContent", Column = "nodeId")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_tagId_nodeId", ForColumns = "tagId,nodeId", IncludeColumns = "propertyTypeId")] public int NodeId { get; set; } [Column("tagId")] diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs index 2d27843486..3db1ea4fb3 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs @@ -308,13 +308,19 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 if (DateTime.UtcNow - found.LastValidatedUtc > _globalSettings.TimeOut) { //timeout detected, update the record - Logger.LogDebug("ClearLoginSession for sessionId {sessionId}", sessionId); + if (Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + {Logger.LogDebug("ClearLoginSession for sessionId {sessionId}", sessionId); + } ClearLoginSession(sessionId); return false; } //update the validate date - Logger.LogDebug("Updating LastValidatedUtc for sessionId {sessionId}", sessionId);found.LastValidatedUtc = DateTime.UtcNow; + if (Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + Logger.LogDebug("Updating LastValidatedUtc for sessionId {sessionId}", sessionId); + } + found.LastValidatedUtc = DateTime.UtcNow; Database.Update(found); return true; } diff --git a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabase.cs b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabase.cs index 5be7cfdde2..4c88e7659a 100644 --- a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabase.cs +++ b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabase.cs @@ -271,11 +271,17 @@ public class UmbracoDatabase : Database, IUmbracoDatabase protected override void OnException(Exception ex) { _logger.LogError(ex, "Exception ({InstanceId}).", InstanceId); - _logger.LogDebug("At:\r\n{StackTrace}", Environment.StackTrace); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("At:\r\n{StackTrace}", Environment.StackTrace); + } if (EnableSqlTrace == false) { - _logger.LogDebug("Sql:\r\n{Sql}", CommandToString(LastSQL, LastArgs)); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Sql:\r\n{Sql}", CommandToString(LastSQL, LastArgs)); + } } base.OnException(ex); @@ -293,7 +299,10 @@ public class UmbracoDatabase : Database, IUmbracoDatabase if (EnableSqlTrace) { - _logger.LogDebug("SQL Trace:\r\n{Sql}", CommandToString(cmd).Replace("{", "{{").Replace("}", "}}")); // TODO: these escapes should be builtin + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("SQL Trace:\r\n{Sql}", CommandToString(cmd).Replace("{", "{{").Replace("}", "}}")); // TODO: these escapes should be builtin + } } #if DEBUG_DATABASES diff --git a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs index 7530ab7854..d8753bfd37 100644 --- a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs @@ -84,7 +84,10 @@ public class UmbracoDatabaseFactory : DisposableObjectSlim, IUmbracoDatabaseFact ConnectionStrings umbracoConnectionString = connectionStrings.CurrentValue; if (!umbracoConnectionString.IsConnectionStringConfigured()) { - logger.LogDebug("Missing connection string, defer configuration."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + logger.LogDebug("Missing connection string, defer configuration."); + } return; // not configured } @@ -197,7 +200,10 @@ public class UmbracoDatabaseFactory : DisposableObjectSlim, IUmbracoDatabaseFact private SqlContext Initialize() { - _logger.LogDebug("Initializing."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Initializing."); + } if (ConnectionString.IsNullOrWhiteSpace()) { @@ -261,8 +267,10 @@ public class UmbracoDatabaseFactory : DisposableObjectSlim, IUmbracoDatabaseFact throw new NullReferenceException( "The call to UmbracoDatabaseFactory.Config yielded a null UmbracoDatabaseFactory instance."); } - - _logger.LogDebug("Initialized."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Initialized."); + } return new SqlContext(_sqlSyntax, _databaseType, _pocoDataFactory, _mappers); } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockGridPropertyValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockGridPropertyValueConverter.cs index ea0fa1f1e7..7270064840 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockGridPropertyValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockGridPropertyValueConverter.cs @@ -94,7 +94,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters private BlockGridModel? ConvertIntermediateToBlockGridModel(IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object? inter, bool preview) { - using (_proflog.DebugDuration($"ConvertPropertyToBlockGrid ({propertyType.DataType.Id})")) + using (!_proflog.IsEnabled(LogLevel.Debug) ? null : _proflog.DebugDuration($"ConvertPropertyToBlockGrid ({propertyType.DataType.Id})")) { // Get configuration BlockGridConfiguration? configuration = propertyType.DataType.ConfigurationAs(); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs index b00d98c7e3..fb253cc325 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -103,7 +103,7 @@ public class BlockListPropertyValueConverter : BlockPropertyValueConverterBase intermediate since source is always just a JSON string - using (_proflog.DebugDuration( + using (!_proflog.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _proflog.DebugDuration( $"ConvertPropertyToBlockList ({propertyType.DataType.Id})")) { BlockListModel? blockListModel = ConvertIntermediateToBlockListModel(owner, propertyType, referenceCacheLevel, inter, preview); @@ -141,7 +141,7 @@ public class BlockListPropertyValueConverter : BlockPropertyValueConverterBase intermediate since source is always just a JSON string - using (_proflog.DebugDuration( + using (!_proflog.IsEnabled(LogLevel.Debug) ? null : _proflog.DebugDuration( $"ConvertPropertyToBlockList ({propertyType.DataType.Id})")) { // Get configuration diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs index 6c770eaf5e..5c9eafd5d6 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs @@ -85,7 +85,7 @@ public class ImageCropperValue : IHtmlEncodedString, IEquatable( + using (!_proflog.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _proflog.DebugDuration( $"ConvertPropertyToLinks ({propertyType.DataType.Id})")) { var maxNumber = propertyType.DataType.ConfigurationAs()!.MaxNumber; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs index db3455b645..4b6ab30ded 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs @@ -80,7 +80,7 @@ public class NestedContentManyValueConverter : NestedContentValueConverterBase, /// public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object? inter, bool preview) { - using (_proflog.DebugDuration( + using (!_proflog.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _proflog.DebugDuration( $"ConvertPropertyToNestedContent ({propertyType.DataType.Id})")) { NestedContentConfiguration? configuration = diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs index 1bfc9975b5..03ff098cbf 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs @@ -79,7 +79,7 @@ public class NestedContentSingleValueConverter : NestedContentValueConverterBase /// public override object? ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object? inter, bool preview) { - using (_proflog.DebugDuration( + using (!_proflog.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _proflog.DebugDuration( $"ConvertPropertyToNestedContent ({propertyType.DataType.Id})")) { var value = (string?)inter; diff --git a/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs b/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs index b8d93631d0..f21635d2df 100644 --- a/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs +++ b/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs @@ -60,7 +60,10 @@ public class PublishedContentTypeCache : IDisposable /// public void ClearAll() { - _logger.LogDebug("Clear all."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Clear all."); + } try { @@ -84,7 +87,10 @@ public class PublishedContentTypeCache : IDisposable /// An identifier. public void ClearContentType(int id) { - _logger.LogDebug("Clear content type w/id {ContentTypeId}", id); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Clear content type w/id {ContentTypeId}", id); + } try { @@ -125,7 +131,10 @@ public class PublishedContentTypeCache : IDisposable /// A data type identifier. public void ClearDataType(int id) { - _logger.LogDebug("Clear data type w/id {DataTypeId}.", id); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Clear data type w/id {DataTypeId}.", id); + } // there is no recursion to handle here because a PublishedContentType contains *all* its // properties ie both its own properties and those that were inherited (it's based upon an diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index 5bc005fc6d..19c947481d 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -236,7 +236,7 @@ public class CoreRuntime : IRuntime private void AcquireMainDom() { - using DisposableTimer? timer = _profilingLogger.DebugDuration("Acquiring MainDom.", "Acquired."); + using DisposableTimer? timer = !_profilingLogger.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration("Acquiring MainDom.", "Acquired."); try { @@ -257,18 +257,23 @@ public class CoreRuntime : IRuntime return; } - using DisposableTimer? timer = + using DisposableTimer? timer = !_profilingLogger.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration("Determining runtime level.", "Determined."); try { State.DetermineRuntimeLevel(); - - _logger.LogDebug("Runtime level: {RuntimeLevel} - {RuntimeLevelReason}", State.Level, State.Reason); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Runtime level: {RuntimeLevel} - {RuntimeLevelReason}", State.Level, State.Reason); + } if (State.Level == RuntimeLevel.Upgrade) { - _logger.LogDebug("Configure database factory for upgrades."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Configure database factory for upgrades."); + } _databaseFactory.ConfigureForUpgrade(); } } diff --git a/src/Umbraco.Infrastructure/Runtime/FileSystemMainDomLock.cs b/src/Umbraco.Infrastructure/Runtime/FileSystemMainDomLock.cs index 26b8d55f96..6dcd3ef9b0 100644 --- a/src/Umbraco.Infrastructure/Runtime/FileSystemMainDomLock.cs +++ b/src/Umbraco.Infrastructure/Runtime/FileSystemMainDomLock.cs @@ -44,16 +44,22 @@ internal class FileSystemMainDomLock : IMainDomLock try { Directory.CreateDirectory(_hostingEnvironment.LocalTempPath); - _logger.LogDebug("Attempting to obtain MainDom lock file handle {lockFilePath}", _lockFilePath); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Attempting to obtain MainDom lock file handle {lockFilePath}", _lockFilePath); + } _lockFileStream = File.Open(_lockFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); DeleteLockReleaseSignalFile(); return Task.FromResult(true); } catch (IOException) { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "Couldn't obtain MainDom lock file handle, signalling for release of {lockFilePath}", _lockFilePath); + } CreateLockReleaseSignalFile(); } catch (Exception ex) @@ -107,13 +113,19 @@ internal class FileSystemMainDomLock : IMainDomLock { if (_cancellationTokenSource.IsCancellationRequested) { - _logger.LogDebug("ListenAsync Task canceled, exiting loop"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("ListenAsync Task canceled, exiting loop"); + } return; } if (File.Exists(_releaseSignalFilePath)) { - _logger.LogDebug("Found lock release signal file, releasing lock on {lockFilePath}", _lockFilePath); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Found lock release signal file, releasing lock on {lockFilePath}", _lockFilePath); + } _lockFileStream?.Close(); _lockFileStream = null; break; diff --git a/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs b/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs index 6597fadf61..37c99c64f2 100644 --- a/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs +++ b/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs @@ -168,7 +168,10 @@ public class RuntimeState : IRuntimeState { // local version *does* match code version, but the database is not configured // install - may happen with Deploy/Cloud/etc - _logger.LogDebug("Database is not configured, need to install Umbraco."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Database is not configured, need to install Umbraco."); + } Level = RuntimeLevel.Install; Reason = RuntimeLevelReason.InstallNoDatabase; @@ -206,8 +209,11 @@ public class RuntimeState : IRuntimeState { case UmbracoDatabaseState.CannotConnect: { - // cannot connect to configured database, this is bad, fail - _logger.LogDebug("Could not connect to database."); + // cannot connect to configured database, this is bad, fail + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Could not connect to database."); + } if (_globalSettings.Value.InstallMissingDatabase || _databaseProviderMetadata.CanForceCreateDatabase(_databaseFactory)) { @@ -231,12 +237,15 @@ public class RuntimeState : IRuntimeState } case UmbracoDatabaseState.NeedsUpgrade: { - // the db version does not match... but we do have a migration table - // so, at least one valid table, so we quite probably are installed & need to upgrade + // the db version does not match... but we do have a migration table + // so, at least one valid table, so we quite probably are installed & need to upgrade - // although the files version matches the code version, the database version does not - // which means the local files have been upgraded but not the database - need to upgrade - _logger.LogDebug("Has not reached the final upgrade step, need to upgrade Umbraco."); + // although the files version matches the code version, the database version does not + // which means the local files have been upgraded but not the database - need to upgrade + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Has not reached the final upgrade step, need to upgrade Umbraco."); + } Level = _unattendedSettings.Value.UpgradeUnattended ? RuntimeLevel.Run : RuntimeLevel.Upgrade; Reason = RuntimeLevelReason.UpgradeMigrations; } @@ -249,7 +258,10 @@ public class RuntimeState : IRuntimeState if (_unattendedSettings.Value.PackageMigrationsUnattended) { - _logger.LogDebug("Package migrations need to execute."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Package migrations need to execute."); + } Reason = RuntimeLevelReason.UpgradePackageMigrations; } else @@ -354,9 +366,10 @@ public class RuntimeState : IRuntimeState } FinalMigrationState = upgrader.Plan.FinalState; - - _logger.LogDebug("Final upgrade state is {FinalMigrationState}, database contains {DatabaseState}", FinalMigrationState, CurrentMigrationState ?? ""); - + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Final upgrade state is {FinalMigrationState}, database contains {DatabaseState}", FinalMigrationState, CurrentMigrationState ?? ""); + } return CurrentMigrationState != FinalMigrationState; } @@ -373,8 +386,10 @@ public class RuntimeState : IRuntimeState { break; } - - _logger.LogDebug("Could not immediately connect to database, trying again."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Could not immediately connect to database, trying again."); + } Thread.Sleep(1000); } diff --git a/src/Umbraco.Infrastructure/Scoping/Scope.cs b/src/Umbraco.Infrastructure/Scoping/Scope.cs index 0ff1fa5d30..295c92a6d6 100644 --- a/src/Umbraco.Infrastructure/Scoping/Scope.cs +++ b/src/Umbraco.Infrastructure/Scoping/Scope.cs @@ -75,7 +75,10 @@ namespace Umbraco.Cms.Infrastructure.Scoping #if DEBUG_SCOPES _scopeProvider.RegisterScope(this); #endif - logger.LogTrace("Create {InstanceId} on thread {ThreadId}", InstanceId.ToString("N").Substring(0, 8), Thread.CurrentThread.ManagedThreadId); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Trace)) + { + logger.LogTrace("Create {InstanceId} on thread {ThreadId}", InstanceId.ToString("N").Substring(0, 8), Thread.CurrentThread.ManagedThreadId); + } if (detachable) { diff --git a/src/Umbraco.Infrastructure/Security/MemberPasswordHasher.cs b/src/Umbraco.Infrastructure/Security/MemberPasswordHasher.cs index 034014663d..3c7e4eabee 100644 --- a/src/Umbraco.Infrastructure/Security/MemberPasswordHasher.cs +++ b/src/Umbraco.Infrastructure/Security/MemberPasswordHasher.cs @@ -107,8 +107,11 @@ public class MemberPasswordHasher : UmbracoPasswordHasher } else { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "Unable to determine member password hashing algorithm, but this can happen when member enters a wrong password, before it has be rehashed"); + } } return PasswordVerificationResult.Failed; diff --git a/src/Umbraco.Infrastructure/Services/CacheInstructionService.cs b/src/Umbraco.Infrastructure/Services/CacheInstructionService.cs index 53be47e155..f47210fa49 100644 --- a/src/Umbraco.Infrastructure/Services/CacheInstructionService.cs +++ b/src/Umbraco.Infrastructure/Services/CacheInstructionService.cs @@ -154,7 +154,7 @@ namespace Umbraco.Cms DateTime lastPruned, int lastId) { - using (_profilingLogger.DebugDuration("Syncing from database...")) + using (!_profilingLogger.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration("Syncing from database...")) using (ICoreScope scope = ScopeProvider.CreateCoreScope()) { var numberOfInstructionsProcessed = ProcessDatabaseInstructions(cacheRefreshers, cancellationToken, localIdentity, ref lastId); diff --git a/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs b/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs index 20d251696b..1f158fcdf8 100644 --- a/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs +++ b/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs @@ -228,10 +228,12 @@ public abstract class ServerMessengerBase : IServerMessenger { throw new ArgumentNullException(nameof(refresher)); } - - StaticApplicationLogging.Logger.LogDebug( + if (StaticApplicationLogging.Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + StaticApplicationLogging.Logger.LogDebug( "Invoking refresher {RefresherType} on local server for message type RefreshByPayload", refresher.GetType()); + } var payloadRefresher = refresher as IPayloadCacheRefresher; if (payloadRefresher == null) @@ -260,9 +262,11 @@ public abstract class ServerMessengerBase : IServerMessenger { throw new ArgumentNullException(nameof(refresher)); } - - StaticApplicationLogging.Logger.LogDebug( + if (StaticApplicationLogging.Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + StaticApplicationLogging.Logger.LogDebug( "Invoking refresher {RefresherType} on local server for message type {MessageType}", refresher.GetType(), messageType); + } switch (messageType) { @@ -350,9 +354,11 @@ public abstract class ServerMessengerBase : IServerMessenger { throw new ArgumentNullException(nameof(refresher)); } - - StaticApplicationLogging.Logger.LogDebug( + if (StaticApplicationLogging.Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + StaticApplicationLogging.Logger.LogDebug( "Invoking refresher {RefresherType} on local server for message type {MessageType}", refresher.GetType(), messageType); + } var typedRefresher = refresher as ICacheRefresher; diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index fe3c2836c5..68b50cfd91 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -181,7 +181,10 @@ internal class PublishedSnapshotService : IPublishedSnapshotService foreach (ContentTypeCacheRefresher.JsonPayload payload in payloads) { - _logger.LogDebug("Notified {ChangeTypes} for {ItemType} {ItemId}", payload.ChangeTypes, payload.ItemType, payload.Id); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Notified {ChangeTypes} for {ItemType} {ItemId}", payload.ChangeTypes, payload.ItemType, payload.Id); + } } Notify(_contentStore, payloads, RefreshContentTypesLocked); @@ -230,10 +233,13 @@ internal class PublishedSnapshotService : IPublishedSnapshotService foreach (DataTypeCacheRefresher.JsonPayload payload in payloads) { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "Notified {RemovedStatus} for data type {DataTypeId}", payload.Removed ? "Removed" : "Refreshed", payload.Id); + } } using (_contentStore.GetScopedWriteLock(_scopeProvider)) @@ -622,15 +628,23 @@ internal class PublishedSnapshotService : IPublishedSnapshotService /// private void MainDomRelease() { - _logger.LogDebug("Releasing from MainDom..."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Releasing from MainDom..."); + } lock (_storesLock) { - _logger.LogDebug("Releasing content store..."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Releasing content store..."); + } _contentStore?.ReleaseLocalDb(); // null check because we could shut down before being assigned _localContentDb = null; - - _logger.LogDebug("Releasing media store..."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Releasing media store..."); + } _mediaStore?.ReleaseLocalDb(); // null check because we could shut down before being assigned _localMediaDb = null; @@ -742,8 +756,10 @@ internal class PublishedSnapshotService : IPublishedSnapshotService // beware! at that point the cache is inconsistent, // assuming we are going to SetAll content items! _localMediaDb?.Clear(); - - _logger.LogDebug("Loading media from database..."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Loading media from database..."); + } // IMPORTANT GetAllMediaSources sorts kits by level + parentId + sortOrder try @@ -852,7 +868,10 @@ internal class PublishedSnapshotService : IPublishedSnapshotService // contentStore is write-locked during changes - see note above, calls to this method are wrapped in contentStore.GetScopedWriteLock foreach (ContentCacheRefresher.JsonPayload payload in payloads) { - _logger.LogDebug("Notified {ChangeTypes} for content {ContentId}", payload.ChangeTypes, payload.Id); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Notified {ChangeTypes} for content {ContentId}", payload.ChangeTypes, payload.Id); + } if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) { @@ -926,7 +945,10 @@ internal class PublishedSnapshotService : IPublishedSnapshotService // see notes for content cache refresher foreach (MediaCacheRefresher.JsonPayload payload in payloads) { - _logger.LogDebug("Notified {ChangeTypes} for media {MediaId}", payload.ChangeTypes, payload.Id); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Notified {ChangeTypes} for media {MediaId}", payload.ChangeTypes, payload.Id); + } if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) { diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index 97aa5bd118..d3a47157c0 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -633,7 +633,7 @@ public class AuthenticationController : UmbracoApiControllerBase await _signInManager.SignOutAsync(); _logger.LogInformation("User {UserName} from IP address {RemoteIpAddress} has logged out", - User.Identity == null ? "UNKNOWN" : User.Identity.Name, HttpContext.Connection.RemoteIpAddress); + result.Principal.Identity == null ? "UNKNOWN" : result.Principal.Identity.Name, HttpContext.Connection.RemoteIpAddress); var userId = result.Principal.Identity?.GetUserId(); SignOutSuccessResult args = _userManager.NotifyLogoutSuccess(User, userId); diff --git a/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs b/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs index 4ab0ccd072..e59d3166bb 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs @@ -91,6 +91,7 @@ public class ExamineManagementController : UmbracoAuthorizedJsonController return new SearchResults { + PageSize = pageSize, TotalRecords = results.TotalItemCount, Results = results.Select(x => new SearchResult { @@ -265,8 +266,10 @@ public class ExamineManagementController : UmbracoAuthorizedJsonController private void Indexer_IndexOperationComplete(object? sender, EventArgs e) { var indexer = (IIndex?)sender; - - _logger.LogDebug("Logging operation completed for index {IndexName}", indexer?.Name); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Logging operation completed for index {IndexName}", indexer?.Name); + } if (indexer is not null) { diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs index 4184cc5798..1c11c0fd59 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs @@ -57,6 +57,8 @@ public class MemberTypeController : ContentTypeControllerBase localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService)); } + public int GetCount() => _memberTypeService.Count(); + /// /// Gets the member type a given id /// @@ -172,6 +174,21 @@ public class MemberTypeController : ContentTypeControllerBase return Ok(result); } + /// + /// Returns where a particular composition has been used + /// This has been wrapped in a dto instead of simple parameters to support having multiple parameters in post request + /// body + /// + /// + /// + public IActionResult GetWhereCompositionIsUsedInMemberTypes(int contentTypeId) + { + var result = + PerformGetWhereCompositionIsUsedInContentTypes(contentTypeId, UmbracoObjectTypes.MemberType).Value? + .Select(x => new { contentType = x }); + return Ok(result); + } + public MemberTypeDisplay? GetEmpty() { var ct = new MemberType(_shortStringHelper, -1) diff --git a/src/Umbraco.Web.BackOffice/Controllers/RedirectUrlManagementController.cs b/src/Umbraco.Web.BackOffice/Controllers/RedirectUrlManagementController.cs index c4d7d47d87..d87398d574 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/RedirectUrlManagementController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/RedirectUrlManagementController.cs @@ -116,7 +116,10 @@ public class RedirectUrlManagementController : UmbracoAuthorizedApiController { var errorMessage = "User is not a member of the administrators group and so is not allowed to toggle the URL tracker"; - _logger.LogDebug(errorMessage); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug(errorMessage); + } throw new SecurityException(errorMessage); } diff --git a/src/Umbraco.Web.BackOffice/HealthChecks/HealthCheckController.cs b/src/Umbraco.Web.BackOffice/HealthChecks/HealthCheckController.cs index 34adf15a94..9744ed8c5f 100644 --- a/src/Umbraco.Web.BackOffice/HealthChecks/HealthCheckController.cs +++ b/src/Umbraco.Web.BackOffice/HealthChecks/HealthCheckController.cs @@ -76,7 +76,10 @@ public class HealthCheckController : UmbracoAuthorizedJsonController try { - _logger.LogDebug("Running health check: " + check.Name); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Running health check: " + check.Name); + } return await check.GetStatus(); } catch (Exception ex) diff --git a/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs b/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs index 52068c6f8d..5708b3c4fd 100644 --- a/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs +++ b/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs @@ -278,7 +278,7 @@ public class InstallApiController : ControllerBase // executes the step internal async Task ExecuteStepAsync(InstallSetupStep step, object? instruction) { - using (_proflog.TraceDuration($"Executing installation step: '{step.Name}'.", "Step completed")) + using (!_proflog.IsEnabled(Core.Logging.LogLevel.Verbose) ? null : _proflog.TraceDuration($"Executing installation step: '{step.Name}'.", "Step completed")) { Attempt modelAttempt = instruction.TryConvertTo(step.StepType); if (!modelAttempt.Success) diff --git a/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs index 7e709b5904..461d1fc82f 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs @@ -26,14 +26,13 @@ namespace Umbraco.Cms.Web.BackOffice.Trees; [PluginController(Constants.Web.Mvc.BackOfficeTreeArea)] public class ApplicationTreeController : UmbracoAuthorizedApiController { - private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider; - private readonly IControllerFactory _controllerFactory; - private readonly ILocalizedTextService _localizedTextService; - private readonly ISectionService _sectionService; private readonly ITreeService _treeService; + private readonly ISectionService _sectionService; + private readonly ILocalizedTextService _localizedTextService; + private readonly IControllerFactory _controllerFactory; + private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider; - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public ApplicationTreeController( ITreeService treeService, @@ -209,14 +208,19 @@ public class ApplicationTreeController : UmbracoAuthorizedApiController throw new ArgumentNullException(nameof(tree)); } - ActionResult? childrenResult = await GetChildren(tree, id, querystring); + // Force tree querystring param + Dictionary? td = querystring?.ToDictionary(x => x.Key, x => x.Value) ?? new Dictionary(); + td["tree"] = tree.TreeAlias; + var qs = new FormCollection(td); + + ActionResult? childrenResult = await GetChildren(tree, id, qs); if (!(childrenResult?.Result is null)) { return new ActionResult(childrenResult.Result); } TreeNodeCollection? children = childrenResult?.Value; - ActionResult? rootNodeResult = await GetRootNode(tree, querystring); + ActionResult? rootNodeResult = await GetRootNode(tree, qs); if (!(rootNodeResult?.Result is null)) { return rootNodeResult.Result; @@ -224,7 +228,6 @@ public class ApplicationTreeController : UmbracoAuthorizedApiController TreeNode? rootNode = rootNodeResult?.Value; - var sectionRoot = TreeRootNode.CreateSingleTreeRoot( Constants.System.RootString, rootNode!.ChildNodesUrl, @@ -256,7 +259,12 @@ public class ApplicationTreeController : UmbracoAuthorizedApiController throw new ArgumentNullException(nameof(tree)); } - ActionResult result = await GetApiControllerProxy(tree.TreeControllerType, "GetRootNode", querystring); + // Force tree querystring param + Dictionary? td = querystring?.ToDictionary(x => x.Key, x => x.Value) ?? new Dictionary(); + td["tree"] = tree.TreeAlias; + var qs = new FormCollection(td); + + ActionResult result = await GetApiControllerProxy(tree.TreeControllerType, "GetRootNode", qs); // return null if the user isn't authorized to view that tree if (!((ForbidResult?)result.Result is null)) @@ -268,7 +276,7 @@ public class ApplicationTreeController : UmbracoAuthorizedApiController TreeNode? rootNode = null; if (controller is not null) { - ActionResult rootNodeResult = await controller.GetRootNode(querystring); + ActionResult rootNodeResult = await controller.GetRootNode(qs); if (!(rootNodeResult.Result is null)) { return rootNodeResult.Result; diff --git a/src/Umbraco.Web.Common/Controllers/RenderController.cs b/src/Umbraco.Web.Common/Controllers/RenderController.cs index dad8d8a84b..4b9ab5d98d 100644 --- a/src/Umbraco.Web.Common/Controllers/RenderController.cs +++ b/src/Umbraco.Web.Common/Controllers/RenderController.cs @@ -55,12 +55,14 @@ public class RenderController : UmbracoPageController, IRenderController public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { IPublishedRequest pcr = UmbracoRouteValues.PublishedRequest; - - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "Response status: Content={Content}, StatusCode={ResponseStatusCode}, Culture={Culture}", pcr.PublishedContent?.Id ?? -1, pcr.ResponseStatusCode, pcr.Culture); + } UmbracoRouteResult routeStatus = pcr.GetRouteResult(); switch (routeStatus) diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.MembersIdentity.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.MembersIdentity.cs index 79c60bc230..09ff520766 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.MembersIdentity.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.MembersIdentity.cs @@ -65,6 +65,7 @@ public static partial class UmbracoBuilderExtensions services.ConfigureOptions(); services.ConfigureOptions(); + services.AddScoped(); services.AddUnique(); diff --git a/src/Umbraco.Web.Common/Macros/MacroRenderer.cs b/src/Umbraco.Web.Common/Macros/MacroRenderer.cs index 9ff69bcb7a..1bc46f0db0 100644 --- a/src/Umbraco.Web.Common/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web.Common/Macros/MacroRenderer.cs @@ -249,9 +249,10 @@ public class MacroRenderer : IMacroRenderer { return null; } - - _logger.LogDebug("Macro content loaded from cache '{MacroCacheId}'", model.CacheIdentifier); - + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Macro content loaded from cache '{MacroCacheId}'", model.CacheIdentifier); + } // ensure that the source has not changed // note: does not handle dependencies, and never has FileInfo? macroSource = GetMacroFile(model); // null if macro is not file-based @@ -259,13 +260,19 @@ public class MacroRenderer : IMacroRenderer { if (macroSource.Exists == false) { - _logger.LogDebug("Macro source does not exist anymore, ignore cache."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Macro source does not exist anymore, ignore cache."); + } return null; } if (macroContent.Date < macroSource.LastWriteTime) { - _logger.LogDebug("Macro source has changed, ignore cache."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Macro source has changed, ignore cache."); + } return null; } } @@ -304,8 +311,10 @@ public class MacroRenderer : IMacroRenderer CacheKeys.MacroContentCacheKey + model.CacheIdentifier, () => macroContent, new TimeSpan(0, 0, model.CacheDuration)); - - _logger.LogDebug("Macro content saved to cache '{MacroCacheId}'", model.CacheIdentifier); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Macro content saved to cache '{MacroCacheId}'", model.CacheIdentifier); + } } // gets the macro source file name @@ -372,7 +381,7 @@ public class MacroRenderer : IMacroRenderer } var macroInfo = $"Render Macro: {macro.Name}, cache: {macro.CacheDuration}"; - using (_profilingLogger.DebugDuration(macroInfo, "Rendered Macro.")) + using (!_profilingLogger.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration(macroInfo, "Rendered Macro.")) { // parse macro parameters ie replace the special [#key], [$key], etc. syntaxes foreach (MacroPropertyModel prop in macro.Properties) @@ -424,7 +433,7 @@ public class MacroRenderer : IMacroRenderer /// private Attempt ExecuteMacroWithErrorWrapper(MacroModel macro, string msgIn, string msgOut, Func getMacroContent, Func msgErr) { - using (_profilingLogger.DebugDuration(msgIn, msgOut)) + using (!_profilingLogger.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration(msgIn, msgOut)) { return ExecuteProfileMacroWithErrorWrapper(macro, msgIn, getMacroContent, msgErr); } diff --git a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs index 9b93ad890c..f5b4f06a69 100644 --- a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs +++ b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs @@ -114,9 +114,12 @@ public class UmbracoRequestMiddleware : IMiddleware try { - // Verbose log start of every request - LogHttpRequest.TryGetCurrentHttpRequestId(out Guid? httpRequestId, _requestCache); - _logger.LogTrace("Begin request [{HttpRequestId}]: {RequestUrl}", httpRequestId, pathAndQuery); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Trace)) + { + // Verbose log start of every request + LogHttpRequest.TryGetCurrentHttpRequestId(out Guid? httpRequestId, _requestCache); + _logger.LogTrace("Begin request [{HttpRequestId}]: {RequestUrl}", httpRequestId, pathAndQuery); + } try { @@ -144,14 +147,17 @@ public class UmbracoRequestMiddleware : IMiddleware } finally { - // Verbose log end of every request (in v8 we didn't log the end request of ALL requests, only the front-end which was - // strange since we always logged the beginning, so now we just log start/end of all requests) - LogHttpRequest.TryGetCurrentHttpRequestId(out Guid? httpRequestId, _requestCache); - _logger.LogTrace( - "End Request [{HttpRequestId}]: {RequestUrl} ({RequestDuration}ms)", - httpRequestId, - pathAndQuery, - DateTime.Now.Subtract(umbracoContextReference.UmbracoContext.ObjectCreated).TotalMilliseconds); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Trace)) + { + // Verbose log end of every request (in v8 we didn't log the end request of ALL requests, only the front-end which was + // strange since we always logged the beginning, so now we just log start/end of all requests) + LogHttpRequest.TryGetCurrentHttpRequestId(out Guid? httpRequestId, _requestCache); + _logger.LogTrace( + "End Request [{HttpRequestId}]: {RequestUrl} ({RequestDuration}ms)", + httpRequestId, + pathAndQuery, + DateTime.Now.Subtract(umbracoContextReference.UmbracoContext.ObjectCreated).TotalMilliseconds); + } try { diff --git a/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/InMemoryModelFactory.cs b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/InMemoryModelFactory.cs index 4ed40eaf55..1da1b32a22 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/InMemoryModelFactory.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/InMemoryModelFactory.cs @@ -215,7 +215,10 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto // tells the factory that it should build a new generation of models private void ResetModels() { - _logger.LogDebug("Resetting models."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Resetting models."); + } try { @@ -257,7 +260,10 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto { if (_debugLevel > 0) { - _logger.LogDebug("Ensuring models."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Ensuring models."); + } } // don't use an upgradeable lock here because only 1 thread at a time could enter it @@ -291,7 +297,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto // we don't have models, // either they haven't been loaded from the cache yet // or they have been reseted and are pending a rebuild - using (_profilingLogger.DebugDuration("Get models.", "Got models.")) + using (!_profilingLogger.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration("Get models.", "Got models.")) { try { @@ -396,13 +402,19 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto // currentHash hashes both the types & the user's partials if (!forceRebuild) { - _logger.LogDebug("Looking for cached models."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Looking for cached models."); + } if (File.Exists(modelsHashFile) && File.Exists(projFile)) { var cachedHash = File.ReadAllText(modelsHashFile); if (currentHash != cachedHash) { - _logger.LogDebug("Found obsolete cached models."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Found obsolete cached models."); + } forceRebuild = true; } @@ -426,8 +438,10 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto if (File.Exists(dllPathFile)) { var dllPath = File.ReadAllText(dllPathFile); - - _logger.LogDebug($"Cached models dll at {dllPath}."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Cached models dll at {dllPath}.",dllPath); + } if (File.Exists(dllPath) && !File.Exists(dllPath + ".delete")) { @@ -441,24 +455,37 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto // ver 1, but we remember we want to skip that one - so we never end up // with the "same but different" version of the assembly in memory _skipver = assembly.GetName().Version?.Revision; - - _logger.LogDebug("Loading cached models (dll)."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Loading cached models (dll)."); + } return assembly; } - - _logger.LogDebug("Cached models dll cannot be loaded (invalid assembly)."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Cached models dll cannot be loaded (invalid assembly)."); + } } else if (!File.Exists(dllPath)) { - _logger.LogDebug("Cached models dll does not exist."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Cached models dll does not exist."); + } } else if (File.Exists(dllPath + ".delete")) { - _logger.LogDebug("Cached models dll is marked for deletion."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Cached models dll is marked for deletion."); + } } else { - _logger.LogDebug("Cached models dll cannot be loaded (why?)."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Cached models dll cannot be loaded (why?)."); + } } } @@ -487,13 +514,18 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto ClearOnFailingToCompile(dllPathFile, modelsHashFile, projFile); throw; } - - _logger.LogDebug("Loading cached models (source)."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Loading cached models (source)."); + } return assembly; } // need to rebuild - _logger.LogDebug("Rebuilding models."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Rebuilding models."); + } // generate code, save var code = GenerateModelsCode(typeModels); @@ -531,8 +563,10 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto ClearOnFailingToCompile(dllPathFile, modelsHashFile, projFile); throw; } - - _logger.LogDebug("Done rebuilding."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Done rebuilding."); + } return assembly; } @@ -577,7 +611,10 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto private void ClearOnFailingToCompile(string dllPathFile, string modelsHashFile, string projFile) { - _logger.LogDebug("Failed to compile."); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Failed to compile."); + } // the dll file reference still points to the previous dll, which is obsolete // now and will be deleted by ASP.NET eventually, so better clear that reference. diff --git a/src/Umbraco.Web.Common/Security/ConfigureMemberCookieOptions.cs b/src/Umbraco.Web.Common/Security/ConfigureMemberCookieOptions.cs index 968f070162..b8c2874641 100644 --- a/src/Umbraco.Web.Common/Security/ConfigureMemberCookieOptions.cs +++ b/src/Umbraco.Web.Common/Security/ConfigureMemberCookieOptions.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; @@ -47,6 +48,14 @@ public sealed class ConfigureMemberCookieOptions : IConfigureNamedOptions + { + // We need to resolve the BackOfficeSecurityStampValidator per request as a requirement (even in aspnetcore they do this) + MemberSecurityStampValidator securityStampValidator = + ctx.HttpContext.RequestServices.GetRequiredService(); + + await securityStampValidator.ValidateAsync(ctx); + }, OnRedirectToAccessDenied = ctx => { ctx.Response.StatusCode = StatusCodes.Status403Forbidden; diff --git a/src/Umbraco.Web.Common/Security/MemberSecurityStampValidator.cs b/src/Umbraco.Web.Common/Security/MemberSecurityStampValidator.cs new file mode 100644 index 0000000000..3623626112 --- /dev/null +++ b/src/Umbraco.Web.Common/Security/MemberSecurityStampValidator.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Security; + +namespace Umbraco.Cms.Web.Common.Security; + +/// +/// A security stamp validator for the back office +/// +public class MemberSecurityStampValidator : SecurityStampValidator +{ + public MemberSecurityStampValidator( + IOptions options, + MemberSignInManager signInManager, ISystemClock clock, ILoggerFactory logger) + : base(options, signInManager, clock, logger) + { + } + + public override Task ValidateAsync(CookieValidatePrincipalContext context) + { + return base.ValidateAsync(context); + } +} diff --git a/src/Umbraco.Web.Common/Security/MemberSecurityStampValidatorOptions.cs b/src/Umbraco.Web.Common/Security/MemberSecurityStampValidatorOptions.cs new file mode 100644 index 0000000000..38189b2d6a --- /dev/null +++ b/src/Umbraco.Web.Common/Security/MemberSecurityStampValidatorOptions.cs @@ -0,0 +1,7 @@ +using Microsoft.AspNetCore.Identity; + +namespace Umbraco.Cms.Web.Common.Security; + +public class MemberSecurityStampValidatorOptions : SecurityStampValidatorOptions +{ +} diff --git a/src/Umbraco.Web.Common/Security/MemberSignInManager.cs b/src/Umbraco.Web.Common/Security/MemberSignInManager.cs index 6b392c42fd..d402e5f843 100644 --- a/src/Umbraco.Web.Common/Security/MemberSignInManager.cs +++ b/src/Umbraco.Web.Common/Security/MemberSignInManager.cs @@ -79,10 +79,13 @@ public class MemberSignInManager : UmbracoSignInManager, IMe IDictionary? items = auth.Properties?.Items; if (auth.Principal == null || items == null) { - Logger.LogDebug( + if (Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + Logger.LogDebug( auth.Failure ?? new NullReferenceException("Context.AuthenticateAsync(ExternalAuthenticationType) is null"), "The external login authentication failed. No user Principal or authentication items was resolved."); + } return null; } diff --git a/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs b/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs index 052a3ae631..6a167b39f7 100644 --- a/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs +++ b/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs @@ -59,8 +59,11 @@ public abstract class UmbracoSignInManager : SignInManager IDictionary? items = auth.Properties?.Items; if (auth.Principal == null || items == null) { - Logger.LogDebug( + if (Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + Logger.LogDebug( "The external login authentication failed. No user Principal or authentication items was resolved."); + } return null; } @@ -238,6 +241,14 @@ public abstract class UmbracoSignInManager : SignInManager /// public override async Task SignOutAsync() { + // Update the security stamp to sign out everywhere. + TUser? user = await UserManager.GetUserAsync(Context.User); + + if (user is not null) + { + await UserManager.UpdateSecurityStampAsync(user); + } + // override to replace IdentityConstants.ApplicationScheme with custom auth types // code taken from aspnetcore: https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs await Context.SignOutAsync(AuthenticationType); diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 8afbd85ebf..4090bc5ad8 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -82,8 +82,8 @@ "run-sequence": "2.2.1" }, "engines": { - "node": ">=16.17", - "npm": ">=8.15" + "node": ">=18.16", + "npm": ">=9.5" } }, "node_modules/@ampproject/remapping": { @@ -2110,10 +2110,13 @@ "dev": true }, "node_modules/@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", - "dev": true + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/eslint": { "version": "8.37.0", @@ -6261,9 +6264,9 @@ } }, "node_modules/engine.io": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", - "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz", + "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==", "dev": true, "dependencies": { "@types/cookie": "^0.4.1", @@ -6275,25 +6278,25 @@ "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.0.3", - "ws": "~8.2.3" + "ws": "~8.11.0" }, "engines": { "node": ">=10.0.0" } }, "node_modules/engine.io-parser": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", - "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.7.tgz", + "integrity": "sha512-P+jDFbvK6lE3n1OL+q9KuzdOFWkkZ/cMV9gol/SbVfpyqfvrfrFTOFJ6fQm2VC3PZHlU3QPhVwmbsCnauHF2MQ==", "dev": true, "engines": { "node": ">=10.0.0" } }, "node_modules/engine.io/node_modules/ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "dev": true, "engines": { "node": ">=10.0.0" @@ -15385,32 +15388,56 @@ "dev": true }, "node_modules/socket.io": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.3.tgz", - "integrity": "sha512-zdpnnKU+H6mOp7nYRXH4GNv1ux6HL6+lHL8g7Ds7Lj8CkdK1jJK/dlwsKDculbyOHifcJ0Pr/yeXnZQ5GeFrcg==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", + "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", "dev": true, "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "debug": "~4.3.2", - "engine.io": "~6.2.0", - "socket.io-adapter": "~2.4.0", - "socket.io-parser": "~4.2.0" + "engine.io": "~6.4.1", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.1" }, "engines": { "node": ">=10.0.0" } }, "node_modules/socket.io-adapter": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", - "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", - "dev": true + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dev": true, + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } }, "node_modules/socket.io-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", - "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", + "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", "dev": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 7a681c07f4..a9dcf095bc 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -676,7 +676,8 @@ } }; - $scope.saveAndPublish = function () { + $scope.saveAndPublish = function (submitButtonLabelKey) { + var deferred = $q.defer(); clearNotifications($scope.content); if (hasVariants($scope.content)) { //before we launch the dialog we want to execute all client side validations first @@ -686,7 +687,7 @@ view: "views/content/overlays/publish.html", variants: $scope.content.variants, //set a model property for the dialog skipFormValidation: true, //when submitting the overlay form, skip any client side validation - submitButtonLabelKey: "buttons_saveAndPublish", + submitButtonLabelKey: submitButtonLabelKey || "buttons_saveAndPublish", submit: function (model) { model.submitButtonState = "busy"; clearNotifications($scope.content); @@ -700,6 +701,7 @@ formHelper.showNotifications(data); clearNotifications($scope.content); overlayService.close(); + deferred.resolve(); return $q.when(data); }, function (err) { clearDirtyState($scope.content.variants); @@ -712,16 +714,19 @@ clearNotifications($scope.content); handleHttpException(err); + deferred.reject(err); }); }, close: function () { overlayService.close(); + deferred.reject(); } }; overlayService.open(dialog); } else { showValidationNotification(); + deferred.reject(); } } else { @@ -734,14 +739,19 @@ action: "publish" }).then(function () { $scope.page.buttonGroupState = "success"; + deferred.resolve(); }, function (err) { $scope.page.buttonGroupState = "error"; handleHttpException(err); + deferred.reject(err); }); } + + return deferred.promise; }; - $scope.save = function () { + $scope.save = function (submitButtonLabelKey) { + var deferred = $q.defer(); clearNotifications($scope.content); // TODO: Add "..." to save button label if there are more than one variant to publish - currently it just adds the elipses if there's more than 1 variant if (hasVariants($scope.content)) { @@ -750,7 +760,7 @@ view: "views/content/overlays/save.html", variants: $scope.content.variants, //set a model property for the dialog skipFormValidation: true, //when submitting the overlay form, skip any client side validation - submitButtonLabelKey: "buttons_save", + submitButtonLabelKey: submitButtonLabelKey || "buttons_save", submit: function (model) { model.submitButtonState = "busy"; clearNotifications($scope.content); @@ -765,6 +775,7 @@ formHelper.showNotifications(data); clearNotifications($scope.content); overlayService.close(); + deferred.resolve(); return $q.when(data); }, function (err) { clearDirtyState($scope.content.variants); @@ -783,10 +794,12 @@ handleHttpException(err); } + deferred.reject(); }) }, close: function (oldModel) { overlayService.close(); + deferred.reject(); } }; @@ -802,6 +815,7 @@ skipValidation: true }).then(function () { $scope.page.saveButtonState = "success"; + deferred.resolve(); }, function (err) { // Because this is the "save"-action, then we actually save though there was a validation error, therefor we will show success and display the validation errors politely. if(err && err.data && err.data.ModelState && Object.keys(err.data.ModelState).length > 0) { @@ -810,9 +824,11 @@ $scope.page.saveButtonState = "error"; } handleHttpException(err); + deferred.reject(); }); } + return deferred.promise; }; $scope.schedule = function () { @@ -982,25 +998,35 @@ /* publish method used in infinite editing */ $scope.publishAndClose = function (content) { $scope.publishAndCloseButtonState = "busy"; - performSave({ saveMethod: contentResource.publish, action: "publish" }).then(function () { - if ($scope.infiniteModel.submit) { - $scope.infiniteModel.contentNode = content; - $scope.infiniteModel.submit($scope.infiniteModel); + $scope.saveAndPublish("buttons_publishAndClose").then( + function() { + if ($scope.infiniteModel.submit) { + $scope.infiniteModel.contentNode = content; + $scope.infiniteModel.submit($scope.infiniteModel); + } + $scope.publishAndCloseButtonState = "success"; + }, + function() { + $scope.publishAndCloseButtonState = "error"; } - $scope.publishAndCloseButtonState = "success"; - }); + ); }; /* save method used in infinite editing */ $scope.saveAndClose = function (content) { $scope.saveAndCloseButtonState = "busy"; - performSave({ saveMethod: $scope.saveMethod(), action: "save" }).then(function () { - if ($scope.infiniteModel.submit) { - $scope.infiniteModel.contentNode = content; - $scope.infiniteModel.submit($scope.infiniteModel); + $scope.save("buttons_saveAndClose").then( + function() { + if ($scope.infiniteModel.submit) { + $scope.infiniteModel.contentNode = content; + $scope.infiniteModel.submit($scope.infiniteModel); + } + $scope.saveAndCloseButtonState = "success"; + }, + function() { + $scope.saveAndCloseButtonState = "error"; } - $scope.saveAndCloseButtonState = "success"; - }); + ); }; /** diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js index 41d40a4615..6ba0aec2ee 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js @@ -1,7 +1,7 @@ (function () { 'use strict'; - function GroupsBuilderDirective(contentTypeHelper, contentTypeResource, mediaTypeResource, + function GroupsBuilderDirective(contentTypeHelper, contentTypeResource, mediaTypeResource, memberTypeResource, $filter, iconHelper, $q, $timeout, notificationsService, localizationService, editorService, eventsService, overlayService) { @@ -215,7 +215,7 @@ scope.sortableRequestedTabTimeout = $timeout(() => { scope.openTabAlias = scope.sortableRequestedTabAlias; scope.sortableRequestedTabTimeout = null; - /* hack to update sortable positions when switching from one tab to another. + /* hack to update sortable positions when switching from one tab to another. without this sorting direct properties doesn't work correctly */ scope.$apply(); $('.umb-group-builder__ungrouped-properties .umb-group-builder__properties').sortable('refresh'); @@ -238,7 +238,7 @@ if (items && items.length <= 1) { return; } - + // update the moved item sort order to fit into where it is dragged const movedItem = items[movedIndex]; @@ -250,8 +250,8 @@ movedItem.sortOrder = prevItem.sortOrder + 1; } - /* After the above two items next to each other might have the same sort order - to prevent this we run through the rest of the + /* After the above two items next to each other might have the same sort order + to prevent this we run through the rest of the items and update the sort order if they are next to each other. This will make it possible to make gaps without the number being updated */ for (let i = movedIndex; i < items.length; i++) { @@ -289,7 +289,12 @@ }); //use a different resource lookup depending on the content type type - var resourceLookup = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes; + var resourceLookup = mediaTypeResource.getAvailableCompositeContentTypes; + if (scope.contentType === "documentType") { + resourceLookup = contentTypeResource.getAvailableCompositeContentTypes; + } else if (scope.contentType === "memberType") { + resourceLookup = memberTypeResource.getAvailableCompositeContentTypes; + } return resourceLookup(scope.model.id, selectedContentTypeAliases, propAliasesExisting).then(filteredAvailableCompositeTypes => { scope.compositionsDialogModel.availableCompositeContentTypes.forEach(current => { @@ -406,7 +411,12 @@ //merge composition with content type //use a different resource lookup depending on the content type type - var resourceLookup = scope.contentType === "documentType" ? contentTypeResource.getById : mediaTypeResource.getById; + var resourceLookup = mediaTypeResource.getById; + if (scope.contentType === "documentType") { + resourceLookup = contentTypeResource.getById; + } else if (scope.contentType === "memberType") { + resourceLookup = memberTypeResource.getById; + } resourceLookup(selectedContentType.id).then(composition => { //based on the above filtering we shouldn't be able to select an invalid one, but let's be safe and @@ -449,10 +459,19 @@ } }; - //select which resource methods to use, eg document Type or Media Type versions - var availableContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes; - var whereUsedContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getWhereCompositionIsUsedInContentTypes : mediaTypeResource.getWhereCompositionIsUsedInContentTypes; - var countContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getCount : mediaTypeResource.getCount; + var availableContentTypeResource = mediaTypeResource.getAvailableCompositeContentTypes; + var whereUsedContentTypeResource = mediaTypeResource.getWhereCompositionIsUsedInContentTypes; + var countContentTypeResource = mediaTypeResource.getCount; + + if (scope.contentType === "documentType") { + availableContentTypeResource = contentTypeResource.getAvailableCompositeContentTypes; + whereUsedContentTypeResource = contentTypeResource.getWhereCompositionIsUsedInContentTypes; + countContentTypeResource = contentTypeResource.getCount; + } else if (scope.contentType === "memberType") { + availableContentTypeResource = memberTypeResource.getAvailableCompositeContentTypes; + whereUsedContentTypeResource = memberTypeResource.getWhereCompositionIsUsedInContentTypes; + countContentTypeResource = memberTypeResource.getCount; + } //get the currently assigned property type aliases - ensure we pass these to the server side filer var propAliasesExisting = _.filter(_.flatten(_.map(scope.model.groups, g => { @@ -548,7 +567,7 @@ const localizeMany = localizationService.localizeMany(['general_delete', 'contentTypeEditor_confirmDeleteTabNotice']); const localize = localizationService.localize('contentTypeEditor_confirmDeleteTabMessage', [tabName]); - + $q.all([localizeMany, localize]).then(values => { const translations = values[0]; const message = values[1]; @@ -752,7 +771,7 @@ const localizeMany = localizationService.localizeMany(['general_delete', 'contentTypeEditor_confirmDeleteGroupNotice']); const localize = localizationService.localize('contentTypeEditor_confirmDeleteGroupMessage', [groupName]); - + $q.all([localizeMany, localize]).then(values => { const translations = values[0]; const message = values[1]; diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js index e1d0fbe8ac..f96ba02ecb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js @@ -6,7 +6,14 @@ function memberTypeResource($q, $http, umbRequestHelper, umbDataFormatter, localizationService) { return { - + getCount: function () { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "memberTypeApiBaseUrl", + "GetCount")), + 'Failed to retrieve count'); + }, getAvailableCompositeContentTypes: function (contentTypeId, filterContentTypes, filterPropertyTypes) { if (!filterContentTypes) { filterContentTypes = []; @@ -39,7 +46,17 @@ function memberTypeResource($q, $http, umbRequestHelper, umbDataFormatter, local query)), 'Failed to retrieve data for content type id ' + contentTypeId); }, + getWhereCompositionIsUsedInContentTypes: function (contentTypeId) { + var query = "contentTypeId=" + contentTypeId; + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "memberTypeApiBaseUrl", + "GetWhereCompositionIsUsedInMemberTypes", + query)), + "Failed to retrieve data for content type id " + contentTypeId); + }, //return all member types getTypes: function () { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index 4dfb139396..96b0681b8f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -603,7 +603,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s createMediaPicker: function (editor, callback) { editor.ui.registry.addButton('umbmediapicker', { icon: 'image', - tooltip: 'Media Picker', + tooltip: 'Image Picker', stateSelector: 'img[data-udi]', onAction: function () { diff --git a/src/Umbraco.Web.UI.Client/src/installer/installer.service.js b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js index c7f2645b97..c2596633c4 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/installer.service.js +++ b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js @@ -30,7 +30,7 @@ angular.module("umbraco.install").factory('installerService', function ($rootSco "At least 4 people have the Umbraco logo tattooed on them", "'Umbraco' is the Danish name for an allen key", "Umbraco has been around since 2005, that's a looong time in IT", - "More than 700 people from all over the world meet each year in Denmark in May for our annual conference CodeGarden", + "Every year around 1500 Umbraco enthusiasts from around the world join the biggest hybrid Umbraco conference Codegarden", "While you are installing Umbraco someone else on the other side of the planet is probably doing it too", "You can extend Umbraco without modifying the source code using either JavaScript or C#", "Umbraco has been installed in more than 198 countries" diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-color-picker.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-color-picker.less index a84ca17ebc..4634f33dbd 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-color-picker.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-color-picker.less @@ -1,19 +1,15 @@ -.umb-color-picker { +.umb-color-picker { .sp-replacer { display: inline-flex; margin-right: 12px; height: 32px; + padding: 5px; &.sp-light { background-color: @white; } - .sp-preview { - margin: 5px; - height: auto; - } - .sp-dd { line-height: 2rem; } @@ -24,4 +20,4 @@ .sp-replacer { cursor: not-allowed; } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/less/installer.less b/src/Umbraco.Web.UI.Client/src/less/installer.less index 6e127f9b9b..0eb87721ad 100644 --- a/src/Umbraco.Web.UI.Client/src/less/installer.less +++ b/src/Umbraco.Web.UI.Client/src/less/installer.less @@ -1,4 +1,4 @@ -// Core variables and mixins +// Core variables and mixins @import "fonts.less"; // Loading fonts @import "variables.less"; // Modify this for custom colors, font-sizes, etc @import "colors.less"; @@ -146,6 +146,10 @@ legend { &[type=checkbox] { width: auto; + + &:focus { + outline: 2px solid @blueMidLight; + } } } diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js index 96caa4f8d4..117559d4cc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js @@ -195,21 +195,23 @@ function ExamineManagementController($http, $q, $timeout, umbRequestHelper, loca searcher.isProcessing = true; + const pageIndex = pageNumber ? (pageNumber - 1) : 0; + umbRequestHelper.resourcePromise( $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearchResults", { searcherName: searcher.name, query: encodeURIComponent(vm.searchText), - pageIndex: pageNumber ? (pageNumber - 1) : 0 + pageIndex: pageIndex })), 'Failed to search') .then(searchResults => { searcher.isProcessing = false; - vm.searchResults = searchResults + vm.searchResults = searchResults; + vm.searchResults.pageIndex = pageIndex; vm.searchResults.pageNumber = pageNumber ? pageNumber : 1; - //20 is page size - vm.searchResults.totalPages = Math.ceil(vm.searchResults.totalRecords / 20); + vm.searchResults.totalPages = Math.ceil(vm.searchResults.totalRecords / vm.searchResults.pageSize); // add URLs to edit well known entities _.each(vm.searchResults.results, function (result) { var section = result.values["__IndexType"][0]; diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html index 63ef3d9279..7e6557bbfb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html @@ -323,15 +323,32 @@ + ng-click="vm.showSearchResultDialog(result.values)"> + ({{ ::result.fieldCount }} fields) + - + {{ ::result.score | number:4 }} + + + + + No results were found + + + Showing %0% - %1% of %2% result(s) - Page %3% of %4% + + + + diff --git a/src/Umbraco.Web.UI.Client/src/views/memberTypes/views/design/design.html b/src/Umbraco.Web.UI.Client/src/views/memberTypes/views/design/design.html index 7cd40dd25e..f9350023dd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/memberTypes/views/design/design.html +++ b/src/Umbraco.Web.UI.Client/src/views/memberTypes/views/design/design.html @@ -1,5 +1,4 @@ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less index 4e5275d788..edfe60de3c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less @@ -20,18 +20,6 @@ .umb-block-grid__layout-item { position: relative; - &:hover { - - > ng-form > .umb-block-grid__block--context { - z-index: 4; - } - - > ng-form > .umb-block-grid__block--inline-create-button, - > ng-form > .umb-block-grid__block--validation-border, - > ng-form > .umb-block-grid__block--actions { - z-index: 3; - } - } } .umb-block-grid__block--validation-border { @@ -191,6 +179,7 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti } &:not(.--scale-mode) { > .umb-block-grid__block--actions { + z-index: 4; opacity: var(--umb-block-grid--block-ui-opacity); } diff --git a/src/Umbraco.Web.UI.Docs/package-lock.json b/src/Umbraco.Web.UI.Docs/package-lock.json index 4740b9ad1a..964baff61c 100644 --- a/src/Umbraco.Web.UI.Docs/package-lock.json +++ b/src/Umbraco.Web.UI.Docs/package-lock.json @@ -632,9 +632,9 @@ } }, "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "dev": true, "engines": { "node": ">=0.10" @@ -1657,10 +1657,13 @@ } }, "node_modules/gulp-ngdocs/node_modules/minimist": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.1.tgz", - "integrity": "sha512-GY8fANSrTMfBVfInqJAY41QkOM+upUTytK1jZ0c8+3HdHrJxBJ3rF5i9moClXTE8uUSnUo8cAsCoxDXvSY4DHg==", - "dev": true + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.4.tgz", + "integrity": "sha512-Pkrrm8NjyQ8yVt8Am9M+yUt74zE3iokhzbG1bFVNjLB92vwM71hf40RkEsryg98BujhVOncKm/C1xROxZ030LQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gulp-ngdocs/node_modules/readable-stream": { "version": "1.0.34", @@ -2843,10 +2846,13 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/mixin-deep": { "version": "1.3.2", @@ -3376,9 +3382,9 @@ "dev": true }, "node_modules/qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, "dependencies": { "side-channel": "^1.0.4" @@ -5192,9 +5198,9 @@ "dev": true }, "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "dev": true }, "defaults": { @@ -6019,9 +6025,9 @@ } }, "minimist": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.1.tgz", - "integrity": "sha512-GY8fANSrTMfBVfInqJAY41QkOM+upUTytK1jZ0c8+3HdHrJxBJ3rF5i9moClXTE8uUSnUo8cAsCoxDXvSY4DHg==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.4.tgz", + "integrity": "sha512-Pkrrm8NjyQ8yVt8Am9M+yUt74zE3iokhzbG1bFVNjLB92vwM71hf40RkEsryg98BujhVOncKm/C1xROxZ030LQ==", "dev": true }, "readable-stream": { @@ -7046,9 +7052,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true }, "mixin-deep": { @@ -7468,9 +7474,9 @@ "dev": true }, "qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, "requires": { "side-channel": "^1.0.4" diff --git a/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs b/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs index 897aca28bb..46a4bd20bd 100644 --- a/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs +++ b/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs @@ -60,7 +60,7 @@ public class UmbracoPageResult : IActionResult /// private async Task ExecuteControllerAction(IActionInvoker? actionInvoker) { - using (_profilingLogger.TraceDuration( + using (!_profilingLogger.IsEnabled(Core.Logging.LogLevel.Verbose) ? null : _profilingLogger.TraceDuration( "Executing Umbraco RouteDefinition controller", "Finished")) { diff --git a/src/Umbraco.Web.Website/Routing/PublicAccessRequestHandler.cs b/src/Umbraco.Web.Website/Routing/PublicAccessRequestHandler.cs index a33ac7bca2..b7c751d28c 100644 --- a/src/Umbraco.Web.Website/Routing/PublicAccessRequestHandler.cs +++ b/src/Umbraco.Web.Website/Routing/PublicAccessRequestHandler.cs @@ -50,7 +50,10 @@ public class PublicAccessRequestHandler : IPublicAccessRequestHandler PublicAccessStatus publicAccessStatus; do { - _logger.LogDebug(nameof(RewriteForPublishedContentAccessAsync) + ": Loop {LoopCounter}", i); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug(nameof(RewriteForPublishedContentAccessAsync) + ": Loop {LoopCounter}", i); + } IPublishedContent? publishedContent = routeValues.PublishedRequest?.PublishedContent; if (publishedContent == null) @@ -64,7 +67,10 @@ public class PublicAccessRequestHandler : IPublicAccessRequestHandler if (publicAccessAttempt.Success) { - _logger.LogDebug("EnsurePublishedContentAccess: Page is protected, check for access"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("EnsurePublishedContentAccess: Page is protected, check for access"); + } // manually authenticate the request AuthenticateResult authResult = @@ -87,7 +93,10 @@ public class PublicAccessRequestHandler : IPublicAccessRequestHandler // redirect if this is not the login page if (publicAccessAttempt.Result!.LoginNodeId != publishedContent.Id) { - _logger.LogDebug("EnsurePublishedContentAccess: Not logged in, redirect to login page"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("EnsurePublishedContentAccess: Not logged in, redirect to login page"); + } routeValues = await SetPublishedContentAsOtherPageAsync( httpContext, routeValues.PublishedRequest, publicAccessAttempt.Result!.LoginNodeId); } @@ -97,32 +106,47 @@ public class PublicAccessRequestHandler : IPublicAccessRequestHandler // Redirect if this is not the access denied page if (publicAccessAttempt.Result!.NoAccessNodeId != publishedContent.Id) { - _logger.LogDebug( + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug( "EnsurePublishedContentAccess: Current member has not access, redirect to error page"); + } routeValues = await SetPublishedContentAsOtherPageAsync( httpContext, routeValues.PublishedRequest, publicAccessAttempt.Result!.NoAccessNodeId); } break; case PublicAccessStatus.LockedOut: - _logger.LogDebug("Current member is locked out, redirect to error page"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Current member is locked out, redirect to error page"); + } routeValues = await SetPublishedContentAsOtherPageAsync( httpContext, routeValues.PublishedRequest, publicAccessAttempt.Result!.NoAccessNodeId); break; case PublicAccessStatus.NotApproved: - _logger.LogDebug("Current member is unapproved, redirect to error page"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Current member is unapproved, redirect to error page"); + } routeValues = await SetPublishedContentAsOtherPageAsync( httpContext, routeValues.PublishedRequest, publicAccessAttempt.Result!.NoAccessNodeId); break; case PublicAccessStatus.AccessAccepted: - _logger.LogDebug("Current member has access"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("Current member has access"); + } break; } } else { publicAccessStatus = PublicAccessStatus.AccessAccepted; - _logger.LogDebug("EnsurePublishedContentAccess: Page is not protected"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug("EnsurePublishedContentAccess: Page is not protected"); + } } // loop until we have access or reached max loops @@ -130,8 +154,11 @@ public class PublicAccessRequestHandler : IPublicAccessRequestHandler if (i == maxLoop) { - _logger.LogDebug(nameof(RewriteForPublishedContentAccessAsync) + + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + _logger.LogDebug(nameof(RewriteForPublishedContentAccessAsync) + ": Looks like we are running into an infinite loop, abort"); + } } return routeValues; diff --git a/templates/UmbracoPackage/buildTransitive/UmbracoPackage.targets b/templates/UmbracoPackage/buildTransitive/UmbracoPackage.targets index 2bd0156cca..4c376ac97b 100644 --- a/templates/UmbracoPackage/buildTransitive/UmbracoPackage.targets +++ b/templates/UmbracoPackage/buildTransitive/UmbracoPackage.targets @@ -3,7 +3,7 @@ $(MSBuildThisFileDirectory)..\App_Plugins\UmbracoPackage\**\*.* - + diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberRepositoryTest.cs index 034e076ae5..58dc4bb4b0 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberRepositoryTest.cs @@ -195,18 +195,25 @@ public class MemberRepositoryTest : UmbracoIntegrationTest repository.Save(member); var sut = repository.Get(member.Id); + var standardPropertiesCount = ConventionsHelper.GetStandardPropertyTypeStubs(ShortStringHelper).Count; - Assert.That(memberType.CompositionPropertyGroups.Count(), Is.EqualTo(2)); - Assert.That(memberType.CompositionPropertyTypes.Count(), Is.EqualTo(3 + ConventionsHelper.GetStandardPropertyTypeStubs(ShortStringHelper).Count)); - Assert.That(sut.Properties.Count(), Is.EqualTo(3 + ConventionsHelper.GetStandardPropertyTypeStubs(ShortStringHelper).Count)); + // if there are any standard properties, they all get added to a single group + var expectedGroupCount = standardPropertiesCount > 0 ? 2 : 1; + Assert.That(memberType.CompositionPropertyGroups.Count(), Is.EqualTo(expectedGroupCount)); + Assert.That(memberType.CompositionPropertyTypes.Count(), Is.EqualTo(3 + standardPropertiesCount)); + Assert.That(sut.Properties.Count(), Is.EqualTo(3 + standardPropertiesCount)); var grp = memberType.CompositionPropertyGroups.FirstOrDefault(x => x.Name == Constants.Conventions.Member.StandardPropertiesGroupName); - Assert.IsNotNull(grp); - var aliases = ConventionsHelper.GetStandardPropertyTypeStubs(ShortStringHelper).Select(x => x.Key) - .ToArray(); - foreach (var p in memberType.CompositionPropertyTypes.Where(x => aliases.Contains(x.Alias))) + if (grp != null) { - Assert.AreEqual(grp.Id, p.PropertyGroupId.Value); + var aliases = ConventionsHelper.GetStandardPropertyTypeStubs(ShortStringHelper).Select(x => x.Key) + .ToArray(); + + foreach (var p in memberType.CompositionPropertyTypes.Where(x => aliases.Contains(x.Alias))) + { + Assert.AreEqual(grp.Id, p.PropertyGroupId.Value); + } + } } } diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberTypeRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberTypeRepositoryTest.cs index 5ab4ef3e8b..27d0cce985 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberTypeRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberTypeRepositoryTest.cs @@ -44,8 +44,11 @@ public class MemberTypeRepositoryTest : UmbracoIntegrationTest var standardProps = ConventionsHelper.GetStandardPropertyTypeStubs(ShortStringHelper); + // if there are any standard properties, they all get added to a single group + var expectedGroupCount = standardProps.Count > 0 ? 2 : 1; + Assert.That(sut, Is.Not.Null); - Assert.That(sut.PropertyGroups.Count, Is.EqualTo(2)); + Assert.That(sut.PropertyGroups.Count, Is.EqualTo(expectedGroupCount)); Assert.That(sut.PropertyTypes.Count(), Is.EqualTo(3 + standardProps.Count)); Assert.That(sut.PropertyGroups.Any(x => x.HasIdentity == false || x.Id == 0), Is.False); @@ -227,111 +230,25 @@ public class MemberTypeRepositoryTest : UmbracoIntegrationTest [Test] public void Bug_Changing_Built_In_Member_Type_Property_Type_Aliases_Results_In_Exception() { - var stubs = ConventionsHelper.GetStandardPropertyTypeStubs(ShortStringHelper); - - var provider = ScopeProvider; - using (provider.CreateScope()) - { - var repository = CreateRepository(provider); - - IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType("mtype"); - - // created without the stub properties - Assert.AreEqual(1, memberType.PropertyGroups.Count); - Assert.AreEqual(3, memberType.PropertyTypes.Count()); - - // saving *new* member type adds the stub properties - repository.Save(memberType); - - // saving has added (and saved) the stub properties - Assert.AreEqual(2, memberType.PropertyGroups.Count); - Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count()); - - foreach (var stub in stubs) - { - var prop = memberType.PropertyTypes.First(x => x.Alias == stub.Key); - prop.Alias += "__0000"; - } - - // saving *existing* member type does *not* ensure stub properties - repository.Save(memberType); - - // therefore, nothing has changed - Assert.AreEqual(2, memberType.PropertyGroups.Count); - Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count()); - - // fetching ensures that the stub properties are there - memberType = repository.Get("mtype"); - Assert.IsNotNull(memberType); - - Assert.AreEqual(2, memberType.PropertyGroups.Count); - Assert.AreEqual(3 + (stubs.Count * 2), memberType.PropertyTypes.Count()); - } + // This test was initially deleted but that broke the build as it was marked as a breaking change + // https://github.com/umbraco/Umbraco-CMS/pull/14060 + // Easiest fix for now is to leave the test and just don't do anything } [Test] public void Built_In_Member_Type_Properties_Are_Automatically_Added_When_Creating() { - var stubs = ConventionsHelper.GetStandardPropertyTypeStubs(ShortStringHelper); - - var provider = ScopeProvider; - using (provider.CreateScope()) - { - var repository = CreateRepository(provider); - - IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType(); - - // created without the stub properties - Assert.AreEqual(1, memberType.PropertyGroups.Count); - Assert.AreEqual(3, memberType.PropertyTypes.Count()); - - // saving *new* member type adds the stub properties - repository.Save(memberType); - - // saving has added (and saved) the stub properties - Assert.AreEqual(2, memberType.PropertyGroups.Count); - Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count()); - - // getting with stub properties - memberType = repository.Get(memberType.Id); - - Assert.AreEqual(2, memberType.PropertyGroups.Count); - Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count()); - } + // This test was initially deleted but that broke the build as it was marked as a breaking change + // https://github.com/umbraco/Umbraco-CMS/pull/14060 + // Easiest fix for now is to leave the test and just don't do anything } [Test] public void Built_In_Member_Type_Properties_Missing_Are_Automatically_Added_When_Creating() { - var stubs = ConventionsHelper.GetStandardPropertyTypeStubs(ShortStringHelper); - - var provider = ScopeProvider; - using (provider.CreateScope()) - { - var repository = CreateRepository(provider); - - IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType(); - - // created without the stub properties - Assert.AreEqual(1, memberType.PropertyGroups.Count); - Assert.AreEqual(3, memberType.PropertyTypes.Count()); - - // add one stub property, others are still missing - memberType.AddPropertyType(stubs.First().Value, Constants.Conventions.Member.StandardPropertiesGroupAlias, Constants.Conventions.Member.StandardPropertiesGroupName); - - // saving *new* member type adds the (missing) stub properties - repository.Save(memberType); - - // saving has added (and saved) the (missing) stub properties - Assert.AreEqual(2, memberType.PropertyGroups.Count); - Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count()); - - // getting with stub properties - memberType = repository.Get(memberType.Id); - - Assert.AreEqual(2, memberType.PropertyGroups.Count); - Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count()); - } + // This test was initially deleted but that broke the build as it was marked as a breaking change + // https://github.com/umbraco/Umbraco-CMS/pull/14060 + // Easiest fix for now is to leave the test and just don't do anything } // This is to show that new properties are created for each member type - there was a bug before diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MemberServiceTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MemberServiceTests.cs index 5375d87686..64e741752d 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MemberServiceTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MemberServiceTests.cs @@ -685,36 +685,9 @@ public class MemberServiceTests : UmbracoIntegrationTest [Test] public void Tracks_Dirty_Changes() { - IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType(); - MemberTypeService.Save(memberType); - IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test"); - MemberService.Save(member); - - var resolved = MemberService.GetByEmail(member.Email); - - // NOTE: This will not trigger a property isDirty because this is not based on a 'Property', it is - // just a c# property of the Member object - resolved.Email = "changed@test.com"; - - // NOTE: This will not trigger a property isDirty for the same reason above, but this is a new change, so leave this to make sure. - resolved.FailedPasswordAttempts = 1234; - - // NOTE: this WILL trigger a property isDirty because setting this c# property actually sets a value of - // the underlying 'Property' - resolved.Comments = "This will make it dirty"; - - var dirtyMember = (ICanBeDirty)resolved; - var dirtyProperties = resolved.Properties.Where(x => x.IsDirty()).ToList(); - Assert.IsTrue(dirtyMember.IsDirty()); - Assert.AreEqual(1, dirtyProperties.Count); - - // Assert that email and failed password attempts is still set as dirty on the member it self - Assert.IsTrue(dirtyMember.IsPropertyDirty(nameof(resolved.Email))); - Assert.IsTrue(dirtyMember.IsPropertyDirty(nameof(resolved.FailedPasswordAttempts))); - - // Comment will also be marked as dirty on the member object because content base merges dirty properties. - Assert.IsTrue(dirtyMember.IsPropertyDirty(Constants.Conventions.Member.Comments)); - Assert.AreEqual(3, dirtyMember.GetDirtyProperties().Count()); + // This test was initially deleted but that broke the build as it was marked as a breaking change + // https://github.com/umbraco/Umbraco-CMS/pull/14060 + // Easiest fix for now is to leave the test and just don't do anything } [Test] diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs index 93107542c5..12dfb33fec 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs @@ -211,7 +211,7 @@ public class ImageCropperTest { var cropDataSet = CropperJson1.DeserializeImageCropperValue(); var urlString = cropDataSet.GetCropUrl("thumb", new TestImageUrlGenerator()); - Assert.AreEqual("?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&w=100&h=100", urlString); + Assert.AreEqual("/media/1005/img_0671.jpg?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&w=100&h=100", urlString); } ///