diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index e3f4ed355b..ae92f37709 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -226,6 +226,8 @@ stages: value: cypress@umbraco.com - name: Umbraco__CMS__Unattended__UnattendedUserPassword value: UmbracoAcceptance123! + - name: Umbraco__CMS__Global__InstallMissingDatabase + value: true jobs: - job: Windows_Acceptance_tests variables: diff --git a/src/JsonSchema/AppSettings.cs b/src/JsonSchema/AppSettings.cs index f9aa6b500c..0851baba2e 100644 --- a/src/JsonSchema/AppSettings.cs +++ b/src/JsonSchema/AppSettings.cs @@ -92,6 +92,8 @@ namespace JsonSchema public ContentDashboardSettings ContentDashboard { get; set; } public HelpPageSettings HelpPage { get; set; } + + public InstallDefaultDataSettings DefaultDataCreation { get; set; } } /// diff --git a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDatabaseCreator.cs b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDatabaseCreator.cs index 43980b3b77..de599529fa 100644 --- a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDatabaseCreator.cs +++ b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDatabaseCreator.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using Microsoft.Data.Sqlite; +using Microsoft.Extensions.Logging; using Umbraco.Cms.Infrastructure.Persistence; namespace Umbraco.Cms.Persistence.Sqlite.Services; @@ -9,9 +10,16 @@ namespace Umbraco.Cms.Persistence.Sqlite.Services; /// public class SqliteDatabaseCreator : IDatabaseCreator { + private readonly ILogger _logger; + /// public string ProviderName => Constants.ProviderName; + public SqliteDatabaseCreator(ILogger logger) + { + _logger = logger; + } + /// /// Creates a SQLite database file. /// @@ -33,16 +41,69 @@ public class SqliteDatabaseCreator : IDatabaseCreator /// public void Create(string connectionString) { - using var connection = new SqliteConnection(connectionString); - connection.Open(); + var original = new SqliteConnectionStringBuilder(connectionString); - using SqliteCommand command = connection.CreateCommand(); - command.CommandText = "PRAGMA journal_mode = wal;"; - command.ExecuteNonQuery(); + if (original.Mode == SqliteOpenMode.Memory || original.DataSource == ":memory:") + { + // In-Memory mode - bail + return; + } - command.CommandText = "PRAGMA journal_mode"; - var mode = command.ExecuteScalar(); + if (original.DataSource.StartsWith("file:")) + { + // URI mode - bail + return; + } - Debug.Assert(mode as string == "wal", "incorrect journal_mode"); + /* Note: The following may seem a bit mad, but it has a purpose! + * + * On azure app services if we wish to ensure the database is persistent we need to write it to the persistent network share + * e.g. c:\home or /home + * + * However the network share does not play nice at all with SQLite locking for rollback mode which is the default for new databases. + * May work on windows app services with win32 vfs but not at all on linux with unix vfs. + * + * The experience is so broken in fact that we can't even create an empty sqlite database file and switch from rollback to wal. + * However once a wal database is setup it works reasonably well (perhaps a tad slower than one might like) on the persistent share. + * + * So instead of creating in the final destination, we can create in /tmp || $env:Temp, set the wal bits + * and copy the file over to its new home and finally nuke the temp file. + * + * We could try to do this only on azure e.g. detect $WEBSITE_RESOURCE_GROUP etc but there's no downside to + * always initializing in this way and it probably helps for non azure scenarios also (anytime persisting on a cifs mount for example). + */ + + var tempFile = Path.GetTempFileName(); + var tempConnectionString = new SqliteConnectionStringBuilder { DataSource = tempFile }; + + using (var connection = new SqliteConnection(tempConnectionString.ConnectionString)) + { + connection.Open(); + + using SqliteCommand command = connection.CreateCommand(); + command.CommandText = "PRAGMA journal_mode = wal;"; + command.ExecuteNonQuery(); + } + + // Copy our blank(ish) wal mode sqlite database to its final location. + try + { + File.Copy(tempFile, original.DataSource, overwrite: true); + } + catch (Exception ex) + { + _logger.LogCritical(ex, "Unable to initialize sqlite database file."); + throw; + } + + try + { + File.Delete(tempFile); + } + catch (Exception ex) + { + // We can swallow this, no worries if we can't nuke the practically empty database file. + _logger.LogWarning(ex, "Unable to cleanup temporary sqlite database file {path}", tempFile); + } } } diff --git a/src/Umbraco.Cms.Persistence.Sqlite/UmbracoBuilderExtensions.cs b/src/Umbraco.Cms.Persistence.Sqlite/UmbracoBuilderExtensions.cs index 0945b71270..ddce4788cc 100644 --- a/src/Umbraco.Cms.Persistence.Sqlite/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Cms.Persistence.Sqlite/UmbracoBuilderExtensions.cs @@ -1,12 +1,15 @@ using System.Data.Common; +using Microsoft.Data.Sqlite; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.DistributedLocking; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; using Umbraco.Cms.Persistence.Sqlite.Interceptors; using Umbraco.Cms.Persistence.Sqlite.Services; +using Umbraco.Extensions; namespace Umbraco.Cms.Persistence.Sqlite; @@ -36,6 +39,30 @@ public static class UmbracoBuilderExtensions DbProviderFactories.UnregisterFactory(Constants.ProviderName); DbProviderFactories.RegisterFactory(Constants.ProviderName, Microsoft.Data.Sqlite.SqliteFactory.Instance); + + builder.Services.PostConfigure(Core.Constants.System.UmbracoConnectionName, opt => + { + if (!opt.IsConnectionStringConfigured()) + { + return; + } + + if (opt.ProviderName != Constants.ProviderName) + { + // Not us. + return; + } + + // Prevent accidental creation of database files. + var connectionStringBuilder = new SqliteConnectionStringBuilder(opt.ConnectionString); + if (connectionStringBuilder.Mode == SqliteOpenMode.ReadWriteCreate) + { + connectionStringBuilder.Mode = SqliteOpenMode.ReadWrite; + } + + opt.ConnectionString = connectionStringBuilder.ConnectionString; + }); + return builder; } } diff --git a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs index 147c58c880..e1b65e2a32 100644 --- a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs @@ -46,23 +46,27 @@ namespace Umbraco.Cms.Core.Cache { var payloads = Deserialize(json); - if (payloads is not null) - { - foreach (var payload in payloads) - { - foreach (var alias in GetCacheKeysForAlias(payload.Alias)) - AppCaches.RuntimeCache.ClearByKey(alias); + Refresh(payloads); + } - var macroRepoCache = AppCaches.IsolatedCaches.Get(); - if (macroRepoCache.Success) - { - macroRepoCache.Result?.Clear(RepositoryCacheKeys.GetKey(payload.Id)); - } + public override void Refresh(JsonPayload[] payloads) + { + foreach (var payload in payloads) + { + foreach (var alias in GetCacheKeysForAlias(payload.Alias)) + AppCaches.RuntimeCache.ClearByKey(alias); + + var macroRepoCache = AppCaches.IsolatedCaches.Get(); + if (macroRepoCache) + { + macroRepoCache.Result.Clear(RepositoryCacheKeys.GetKey(payload.Id)); + macroRepoCache.Result.Clear(RepositoryCacheKeys.GetKey(payload.Alias)); // Repository caching of macro definition by alias } } - base.Refresh(json); + base.Refresh(payloads); } + #endregion #region Json diff --git a/src/Umbraco.Core/Configuration/Models/InstallDefaultDataSettings.cs b/src/Umbraco.Core/Configuration/Models/InstallDefaultDataSettings.cs new file mode 100644 index 0000000000..377e893bbf --- /dev/null +++ b/src/Umbraco.Core/Configuration/Models/InstallDefaultDataSettings.cs @@ -0,0 +1,73 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; + +namespace Umbraco.Cms.Core.Configuration.Models +{ + /// + /// An enumeration of options available for control over installation of default Umbraco data. + /// + public enum InstallDefaultDataOption + { + /// + /// Do not install any items of this type (other than Umbraco defined essential ones). + /// + None, + + /// + /// Only install the default data specified in the + /// + Values, + + /// + /// Install all default data, except that specified in the + /// + ExceptValues, + + /// + /// Install all default data. + /// + All + } + + /// + /// Typed configuration options for installation of default data. + /// + public class InstallDefaultDataSettings + { + /// + /// Gets or sets a value indicating whether to create default data on installation. + /// + public InstallDefaultDataOption InstallData { get; set; } = InstallDefaultDataOption.All; + + /// + /// Gets or sets a value indicating which default data (languages, data types, etc.) should be created when is + /// set to or . + /// + /// + /// + /// For languages, the values provided should be the ISO codes for the languages to be included or excluded, e.g. "en-US". + /// If removing the single default language, ensure that a different one is created via some other means (such + /// as a restore from Umbraco Deploy schema data). + /// + /// + /// For data types, the values provided should be the Guid values used by Umbraco for the data type, listed at: + /// + /// Some data types - such as the string label - cannot be excluded from install as they are required for core Umbraco + /// functionality. + /// Otherwise take care not to remove data types required for default Umbraco media and member types, unless you also + /// choose to exclude them. + /// + /// + /// For media types, the values provided should be the Guid values used by Umbraco for the media type, listed at: + /// https://github.com/umbraco/Umbraco-CMS/blob/v9/dev/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs. + /// + /// + /// For member types, the values provided should be the Guid values used by Umbraco for the member type, listed at: + /// https://github.com/umbraco/Umbraco-CMS/blob/v9/dev/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs. + /// + /// + public IList Values { get; set; } = new List(); + } +} diff --git a/src/Umbraco.Core/Constants-Configuration.cs b/src/Umbraco.Core/Constants-Configuration.cs index b3963d64ef..b9375c2619 100644 --- a/src/Umbraco.Core/Constants-Configuration.cs +++ b/src/Umbraco.Core/Constants-Configuration.cs @@ -57,6 +57,21 @@ namespace Umbraco.Cms.Core public const string ConfigPackageMigration = ConfigPrefix + "PackageMigration"; public const string ConfigContentDashboard = ConfigPrefix + "ContentDashboard"; public const string ConfigHelpPage = ConfigPrefix + "HelpPage"; + public const string ConfigInstallDefaultData = ConfigPrefix + "InstallDefaultData"; + + public static class NamedOptions + { + public static class InstallDefaultData + { + public const string Languages = "Languages"; + + public const string DataTypes = "DataTypes"; + + public const string MediaTypes = "MediaTypes"; + + public const string MemberTypes = "MemberTypes"; + } + } } } } diff --git a/src/Umbraco.Core/Constants-DataTypes.cs b/src/Umbraco.Core/Constants-DataTypes.cs index 12445ea589..ba8827cd26 100644 --- a/src/Umbraco.Core/Constants-DataTypes.cs +++ b/src/Umbraco.Core/Constants-DataTypes.cs @@ -211,7 +211,6 @@ namespace Umbraco.Cms.Core /// public static readonly Guid ListViewMembersGuid = new Guid(ListViewMembers); - /// /// Guid for Date Picker with time as string /// diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs index a3dc8b0e58..a8a374fef2 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs @@ -92,6 +92,19 @@ namespace Umbraco.Cms.Core.DependencyInjection builder.Services.AddSingleton, ConfigureConnectionStrings>(); + builder.Services.Configure( + Constants.Configuration.NamedOptions.InstallDefaultData.Languages, + builder.Config.GetSection($"{Constants.Configuration.ConfigInstallDefaultData}:{Constants.Configuration.NamedOptions.InstallDefaultData.Languages}")); + builder.Services.Configure( + Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + builder.Config.GetSection($"{Constants.Configuration.ConfigInstallDefaultData}:{Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes}")); + builder.Services.Configure( + Constants.Configuration.NamedOptions.InstallDefaultData.MediaTypes, + builder.Config.GetSection($"{Constants.Configuration.ConfigInstallDefaultData}:{Constants.Configuration.NamedOptions.InstallDefaultData.MediaTypes}")); + builder.Services.Configure( + Constants.Configuration.NamedOptions.InstallDefaultData.MemberTypes, + builder.Config.GetSection($"{Constants.Configuration.ConfigInstallDefaultData}:{Constants.Configuration.NamedOptions.InstallDefaultData.MemberTypes}")); + builder.Services.Configure(options => options.MergeReplacements(builder.Config)); return builder; diff --git a/src/Umbraco.Core/Persistence/Repositories/IMacroWithAliasRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IMacroWithAliasRepository.cs new file mode 100644 index 0000000000..f6cd27ad60 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/IMacroWithAliasRepository.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Core.Persistence.Repositories +{ + [Obsolete("This interface will be merged with IMacroRepository in Umbraco 11")] + public interface IMacroWithAliasRepository : IMacroRepository + { + IMacro GetByAlias(string alias); + + IEnumerable GetAllByAlias(string[] aliases); + } +} diff --git a/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs index 42746a9565..e6ca8eaa50 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs @@ -1,14 +1,42 @@ -using System; using System.Collections.Generic; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.Entities; namespace Umbraco.Cms.Core.Persistence.Repositories { public interface ITrackedReferencesRepository { - IEnumerable GetPagedRelationsForItems(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency,out long totalRecords); - IEnumerable GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency,out long totalRecords); - IEnumerable GetPagedDescendantsInReferences(int parentId, long pageIndex, int pageSize, bool filterMustBeIsDependency,out long totalRecords); + /// + /// Gets a page of items which are in relation with the current item. + /// Basically, shows the items which depend on the current item. + /// + /// The identifier of the entity to retrieve relations for. + /// The page index. + /// The page size. + /// A boolean indicating whether to filter only the RelationTypes which are dependencies (isDependency field is set to true). + /// The total count of the items with reference to the current item. + /// An enumerable list of objects. + IEnumerable GetPagedRelationsForItem(int id, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords); + + /// + /// Gets a page of items used in any kind of relation from selected integer ids. + /// + /// The identifiers of the entities to check for relations. + /// The page index. + /// The page size. + /// A boolean indicating whether to filter only the RelationTypes which are dependencies (isDependency field is set to true). + /// The total count of the items in any kind of relation. + /// An enumerable list of objects. + IEnumerable GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords); + + /// + /// Gets a page of the descending items that have any references, given a parent id. + /// + /// The unique identifier of the parent to retrieve descendants for. + /// The page index. + /// The page size. + /// A boolean indicating whether to filter only the RelationTypes which are dependencies (isDependency field is set to true). + /// The total count of descending items. + /// An enumerable list of objects. + IEnumerable GetPagedDescendantsInReferences(int parentId, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords); } } diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs index 048ad40ac0..4d88431e7c 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs @@ -1,5 +1,5 @@ -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -28,5 +28,17 @@ namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors DefaultConfiguration.Add("minNumber",0 ); DefaultConfiguration.Add("maxNumber", 0); } + + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); + + internal class MultipleContentPickerParamateterValueEditor : MultiplePickerParamateterValueEditorBase + { + public MultipleContentPickerParamateterValueEditor(ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, IIOHelper ioHelper, DataEditorAttribute attribute, IEntityService entityService) : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute, entityService) + { + } + + public override string UdiEntityType { get; } = Constants.UdiEntityType.Document; + public override UmbracoObjectTypes UmbracoObjectType { get; } = UmbracoObjectTypes.Document; + } } } diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs index d8f74b1b28..dfdd6f9b9c 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs @@ -1,5 +1,9 @@ -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Hosting; +using System; +using System.Collections.Generic; +using System.Reflection.Metadata; +using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Editors; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -26,5 +30,17 @@ namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors { DefaultConfiguration.Add("multiPicker", "1"); } + + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); + + internal class MultipleMediaPickerPropertyValueEditor : MultiplePickerParamateterValueEditorBase + { + public MultipleMediaPickerPropertyValueEditor(ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, IIOHelper ioHelper, DataEditorAttribute attribute, IEntityService entityService) : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute, entityService) + { + } + + public override string UdiEntityType { get; } = Constants.UdiEntityType.Media; + public override UmbracoObjectTypes UmbracoObjectType { get; } = UmbracoObjectTypes.Media; + } } } diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultiplePickerParamateterValueEditorBase.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultiplePickerParamateterValueEditorBase.cs new file mode 100644 index 0000000000..2c4f27b560 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultiplePickerParamateterValueEditorBase.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Editors; +using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; + +namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors +{ + internal abstract class MultiplePickerParamateterValueEditorBase : DataValueEditor, IDataValueReference + { + private readonly IEntityService _entityService; + + public MultiplePickerParamateterValueEditorBase( + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper, + IJsonSerializer jsonSerializer, + IIOHelper ioHelper, + DataEditorAttribute attribute, + IEntityService entityService) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) + { + _entityService = entityService; + } + + public abstract string UdiEntityType { get; } + public abstract UmbracoObjectTypes UmbracoObjectType { get; } + public IEnumerable GetReferences(object value) + { + var asString = value is string str ? str : value?.ToString(); + + if (string.IsNullOrEmpty(asString)) + { + yield break; + } + + foreach (var udiStr in asString.Split(',')) + { + if (UdiParser.TryParse(udiStr, out Udi udi)) + { + yield return new UmbracoEntityReference(udi); + } + + // this is needed to support the legacy case when the multiple media picker parameter editor stores ints not udis + if (int.TryParse(udiStr, out var id)) + { + Attempt guidAttempt = _entityService.GetKey(id, UmbracoObjectType); + Guid guid = guidAttempt.Success ? guidAttempt.Result : Guid.Empty; + + if (guid != Guid.Empty) + { + yield return new UmbracoEntityReference(new GuidUdi(Constants.UdiEntityType.Media, guid)); + } + + } + } + } + } +} diff --git a/src/Umbraco.Core/Security/LegacyPasswordSecurity.cs b/src/Umbraco.Core/Security/LegacyPasswordSecurity.cs index 35528a48ca..b8c7596b2d 100644 --- a/src/Umbraco.Core/Security/LegacyPasswordSecurity.cs +++ b/src/Umbraco.Core/Security/LegacyPasswordSecurity.cs @@ -8,12 +8,17 @@ namespace Umbraco.Cms.Core.Security { /// - /// Handles password hashing and formatting for legacy hashing algorithms + /// Handles password hashing and formatting for legacy hashing algorithms. /// + /// + /// Should probably be internal. + /// public class LegacyPasswordSecurity { + // TODO: Remove v11 // Used for tests [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("We shouldn't be altering our public API to make test code easier, removing v11")] public string HashPasswordForStorage(string algorithmType, string password) { if (string.IsNullOrWhiteSpace(password)) @@ -24,13 +29,15 @@ namespace Umbraco.Cms.Core.Security return FormatPasswordForStorage(algorithmType, hashed, salt); } + // TODO: Remove v11 // Used for tests [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("We shouldn't be altering our public API to make test code easier, removing v11")] public string FormatPasswordForStorage(string algorithmType, string hashedPassword, string salt) { - if (IsLegacySHA1Algorithm(algorithmType)) + if (!SupportHashAlgorithm(algorithmType)) { - return hashedPassword; + throw new InvalidOperationException($"{algorithmType} is not supported"); } return salt + hashedPassword; @@ -45,10 +52,15 @@ namespace Umbraco.Cms.Core.Security /// public bool VerifyPassword(string algorithm, string password, string dbPassword) { - if (string.IsNullOrWhiteSpace(dbPassword)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(dbPassword)); + if (string.IsNullOrWhiteSpace(dbPassword)) + { + throw new ArgumentException("Value cannot be null or whitespace.", nameof(dbPassword)); + } if (dbPassword.StartsWith(Constants.Security.EmptyPasswordPrefix)) + { return false; + } try { @@ -61,7 +73,6 @@ namespace Umbraco.Cms.Core.Security //This can happen if the length of the password is wrong and a salt cannot be extracted. return false; } - } /// @@ -69,12 +80,13 @@ namespace Umbraco.Cms.Core.Security /// public bool VerifyLegacyHashedPassword(string password, string dbPassword) { - var hashAlgorith = new HMACSHA1 + var hashAlgorithm = new HMACSHA1 { //the legacy salt was actually the password :( Key = Encoding.Unicode.GetBytes(password) }; - var hashed = Convert.ToBase64String(hashAlgorith.ComputeHash(Encoding.Unicode.GetBytes(password))); + + var hashed = Convert.ToBase64String(hashAlgorithm.ComputeHash(Encoding.Unicode.GetBytes(password))); return dbPassword == hashed; } @@ -87,6 +99,8 @@ namespace Umbraco.Cms.Core.Security /// /// // TODO: Do we need this method? We shouldn't be using this class to create new password hashes for storage + // TODO: Remove v11 + [Obsolete("We shouldn't be altering our public API to make test code easier, removing v11")] public string HashNewPassword(string algorithm, string newPassword, out string salt) { salt = GenerateSalt(); @@ -102,15 +116,15 @@ namespace Umbraco.Cms.Core.Security /// public string ParseStoredHashPassword(string algorithm, string storedString, out string salt) { - if (string.IsNullOrWhiteSpace(storedString)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(storedString)); - - // This is for the <= v4 hashing algorithm for which there was no salt - if (IsLegacySHA1Algorithm(algorithm)) + if (string.IsNullOrWhiteSpace(storedString)) { - salt = string.Empty; - return storedString; + throw new ArgumentException("Value cannot be null or whitespace.", nameof(storedString)); } + if (!SupportHashAlgorithm(algorithm)) + { + throw new InvalidOperationException($"{algorithm} is not supported"); + } var saltLen = GenerateSalt(); salt = storedString.Substring(0, saltLen.Length); @@ -133,12 +147,12 @@ namespace Umbraco.Cms.Core.Security /// private string HashPassword(string algorithmType, string pass, string salt) { - if (IsLegacySHA1Algorithm(algorithmType)) + if (!SupportHashAlgorithm(algorithmType)) { - return HashLegacySHA1Password(pass); + throw new InvalidOperationException($"{algorithmType} is not supported"); } - //This is the correct way to implement this (as per the sql membership provider) + // This is the correct way to implement this (as per the sql membership provider) var bytes = Encoding.Unicode.GetBytes(pass); var saltBytes = Convert.FromBase64String(salt); @@ -219,42 +233,7 @@ namespace Umbraco.Cms.Core.Security return true; } - // This is for the <= v4 hashing algorithm - if (IsLegacySHA1Algorithm(algorithm)) - { - return true; - } - return false; } - - private bool IsLegacySHA1Algorithm(string algorithm) => algorithm.InvariantEquals(Constants.Security.AspNetUmbraco4PasswordHashAlgorithmName); - - /// - /// Hashes the password with the old v4 algorithm - /// - /// The password. - /// The encoded password. - private string HashLegacySHA1Password(string password) - { - using var hashAlgorithm = GetLegacySHA1Algorithm(password); - var hash = Convert.ToBase64String(hashAlgorithm.ComputeHash(Encoding.Unicode.GetBytes(password))); - return hash; - } - - /// - /// Returns the old v4 algorithm and settings - /// - /// - /// - private HashAlgorithm GetLegacySHA1Algorithm(string password) - { - return new HMACSHA1 - { - //the legacy salt was actually the password :( - Key = Encoding.Unicode.GetBytes(password) - }; - } - } } diff --git a/src/Umbraco.Core/Services/IMacroService.cs b/src/Umbraco.Core/Services/IMacroService.cs index f1984db5f3..a75547dd6d 100644 --- a/src/Umbraco.Core/Services/IMacroService.cs +++ b/src/Umbraco.Core/Services/IMacroService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Umbraco.Cms.Core.Models; @@ -17,13 +17,6 @@ namespace Umbraco.Cms.Core.Services /// An object IMacro? GetByAlias(string alias); - ///// - ///// Gets a list all available objects - ///// - ///// Optional array of aliases to limit the results - ///// An enumerable list of objects - //IEnumerable GetAll(params string[] aliases); - IEnumerable GetAll(); IEnumerable GetAll(params int[] ids); diff --git a/src/Umbraco.Core/Services/IMacroWithAliasService.cs b/src/Umbraco.Core/Services/IMacroWithAliasService.cs new file mode 100644 index 0000000000..6e72777bfa --- /dev/null +++ b/src/Umbraco.Core/Services/IMacroWithAliasService.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Core.Services +{ + [Obsolete("This interface will be merged with IMacroService in Umbraco 11")] + public interface IMacroWithAliasService : IMacroService + { + /// + /// Gets a list of available objects by alias. + /// + /// Optional array of aliases to limit the results + /// An enumerable list of objects + IEnumerable GetAll(params string[] aliases); + } +} diff --git a/src/Umbraco.Core/Services/ITrackedReferencesService.cs b/src/Umbraco.Core/Services/ITrackedReferencesService.cs index eee8a324df..dea99c0f6d 100644 --- a/src/Umbraco.Core/Services/ITrackedReferencesService.cs +++ b/src/Umbraco.Core/Services/ITrackedReferencesService.cs @@ -4,12 +4,35 @@ namespace Umbraco.Cms.Core.Services { public interface ITrackedReferencesService { - PagedResult GetPagedRelationsForItems(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency); - + /// + /// Gets a paged result of items which are in relation with the current item. + /// Basically, shows the items which depend on the current item. + /// + /// The identifier of the entity to retrieve relations for. + /// The page index. + /// The page size. + /// A boolean indicating whether to filter only the RelationTypes which are dependencies (isDependency field is set to true). + /// A paged result of objects. + PagedResult GetPagedRelationsForItem(int id, long pageIndex, int pageSize, bool filterMustBeIsDependency); + /// + /// Gets a paged result of the descending items that have any references, given a parent id. + /// + /// The unique identifier of the parent to retrieve descendants for. + /// The page index. + /// The page size. + /// A boolean indicating whether to filter only the RelationTypes which are dependencies (isDependency field is set to true). + /// A paged result of objects. PagedResult GetPagedDescendantsInReferences(int parentId, long pageIndex, int pageSize, bool filterMustBeIsDependency); - + /// + /// Gets a paged result of items used in any kind of relation from selected integer ids. + /// + /// The identifiers of the entities to check for relations. + /// The page index. + /// The page size. + /// A boolean indicating whether to filter only the RelationTypes which are dependencies (isDependency field is set to true). + /// A paged result of objects. PagedResult GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency); } } diff --git a/src/Umbraco.Core/Services/MacroService.cs b/src/Umbraco.Core/Services/MacroService.cs index 508fe29eab..4a55411db3 100644 --- a/src/Umbraco.Core/Services/MacroService.cs +++ b/src/Umbraco.Core/Services/MacroService.cs @@ -13,13 +13,12 @@ namespace Umbraco.Cms.Core.Services /// /// Represents the Macro Service, which is an easy access to operations involving /// - internal class MacroService : RepositoryService, IMacroService + internal class MacroService : RepositoryService, IMacroWithAliasService { private readonly IMacroRepository _macroRepository; private readonly IAuditRepository _auditRepository; - public MacroService(IScopeProvider provider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, - IMacroRepository macroRepository, IAuditRepository auditRepository) + public MacroService(IScopeProvider provider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, IMacroRepository macroRepository, IAuditRepository auditRepository) : base(provider, loggerFactory, eventMessagesFactory) { _macroRepository = macroRepository; @@ -33,10 +32,14 @@ namespace Umbraco.Cms.Core.Services /// An object public IMacro? GetByAlias(string alias) { + if (_macroRepository is not IMacroWithAliasRepository macroWithAliasRepository) + { + return GetAll().FirstOrDefault(x => x.Alias == alias); + } + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { - var q = Query().Where(x => x.Alias == alias); - return _macroRepository.Get(q)?.FirstOrDefault(); + return macroWithAliasRepository.GetByAlias(alias); } } @@ -61,6 +64,20 @@ namespace Umbraco.Cms.Core.Services } } + public IEnumerable GetAll(params string[] aliases) + { + if (_macroRepository is not IMacroWithAliasRepository macroWithAliasRepository) + { + var hashset = new HashSet(aliases); + return GetAll().Where(x => hashset.Contains(x.Alias)); + } + + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return macroWithAliasRepository.GetAllByAlias(aliases); + } + } + public IMacro? GetById(int id) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) diff --git a/src/Umbraco.Core/Services/TrackedReferencesService.cs b/src/Umbraco.Core/Services/TrackedReferencesService.cs index c43d2ca57b..cce3e0fc74 100644 --- a/src/Umbraco.Core/Services/TrackedReferencesService.cs +++ b/src/Umbraco.Core/Services/TrackedReferencesService.cs @@ -1,4 +1,3 @@ -using System.Linq; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; @@ -18,22 +17,32 @@ namespace Umbraco.Cms.Core.Services _entityService = entityService; } - public PagedResult GetPagedRelationsForItems(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency) + /// + /// Gets a paged result of items which are in relation with the current item. + /// Basically, shows the items which depend on the current item. + /// + public PagedResult GetPagedRelationsForItem(int id, long pageIndex, int pageSize, bool filterMustBeIsDependency) { using IScope scope = _scopeProvider.CreateScope(autoComplete: true); - var items = _trackedReferencesRepository.GetPagedRelationsForItems(ids, pageIndex, pageSize, filterMustBeIsDependency, out var totalItems); + var items = _trackedReferencesRepository.GetPagedRelationsForItem(id, pageIndex, pageSize, filterMustBeIsDependency, out var totalItems); - return new PagedResult(totalItems, pageIndex+1, pageSize) { Items = items }; + return new PagedResult(totalItems, pageIndex + 1, pageSize) { Items = items }; } + /// + /// Gets a paged result of items used in any kind of relation from selected integer ids. + /// public PagedResult GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency) { using IScope scope = _scopeProvider.CreateScope(autoComplete: true); - var items = _trackedReferencesRepository.GetPagedItemsWithRelations(ids, pageIndex, pageSize, filterMustBeIsDependency, out var totalItems); + var items = _trackedReferencesRepository.GetPagedItemsWithRelations(ids, pageIndex, pageSize, filterMustBeIsDependency, out var totalItems); - return new PagedResult(totalItems, pageIndex+1, pageSize) { Items = items }; + return new PagedResult(totalItems, pageIndex + 1, pageSize) { Items = items }; } + /// + /// Gets a paged result of the descending items that have any references, given a parent id. + /// public PagedResult GetPagedDescendantsInReferences(int parentId, long pageIndex, int pageSize, bool filterMustBeIsDependency) { using IScope scope = _scopeProvider.CreateScope(autoComplete: true); @@ -44,7 +53,7 @@ namespace Umbraco.Cms.Core.Services pageSize, filterMustBeIsDependency, out var totalItems); - return new PagedResult(totalItems, pageIndex+1, pageSize) { Items = items }; + return new PagedResult(totalItems, pageIndex + 1, pageSize) { Items = items }; } } } diff --git a/src/Umbraco.Core/StaticApplicationLogging.cs b/src/Umbraco.Core/StaticApplicationLogging.cs index 83ce4e89f1..f0d01d4073 100644 --- a/src/Umbraco.Core/StaticApplicationLogging.cs +++ b/src/Umbraco.Core/StaticApplicationLogging.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -5,18 +6,14 @@ namespace Umbraco.Cms.Core { public static class StaticApplicationLogging { - private static ILoggerFactory? _loggerFactory; + private static ILoggerFactory? s_loggerFactory; - public static void Initialize(ILoggerFactory loggerFactory) - { - _loggerFactory = loggerFactory; - } + public static void Initialize(ILoggerFactory loggerFactory) => s_loggerFactory = loggerFactory; public static ILogger Logger => CreateLogger(); - public static ILogger CreateLogger() - { - return _loggerFactory?.CreateLogger() ?? NullLoggerFactory.Instance.CreateLogger(); - } + public static ILogger CreateLogger() => s_loggerFactory?.CreateLogger() ?? NullLoggerFactory.Instance.CreateLogger(); + + public static ILogger CreateLogger(Type type) => s_loggerFactory?.CreateLogger(type) ?? NullLogger.Instance; } } diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs index 7c72b1ddce..049e3ad8cb 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs @@ -8,17 +8,15 @@ using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Packaging; -using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; using Umbraco.Cms.Infrastructure.Packaging; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; +using Umbraco.Cms.Infrastructure.Templates; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.DependencyInjection @@ -48,6 +46,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection builder.Services.AddUnique(); builder.Services.AddSingleton(); builder.Services.AddUnique(); + builder.Services.AddUnique(); return builder; } diff --git a/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs b/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs index 48c7ddd57d..5fdd69035d 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs @@ -32,7 +32,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices IContentVersionService service, IMainDom mainDom, IServerRoleAccessor serverRoleAccessor) - : base(TimeSpan.FromHours(1), TimeSpan.FromMinutes(3)) + : base(logger, TimeSpan.FromHours(1), TimeSpan.FromMinutes(3)) { _runtimeState = runtimeState; _logger = logger; diff --git a/src/Umbraco.Infrastructure/HostedServices/HealthCheckNotifier.cs b/src/Umbraco.Infrastructure/HostedServices/HealthCheckNotifier.cs index 02f484e84a..33a069e9ac 100644 --- a/src/Umbraco.Infrastructure/HostedServices/HealthCheckNotifier.cs +++ b/src/Umbraco.Infrastructure/HostedServices/HealthCheckNotifier.cs @@ -61,6 +61,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices IProfilingLogger profilingLogger, ICronTabParser cronTabParser) : base( + logger, healthChecksSettings.CurrentValue.Notification.Period, healthChecksSettings.CurrentValue.GetNotificationDelay(cronTabParser, DateTime.Now, DefaultDelay)) { diff --git a/src/Umbraco.Infrastructure/HostedServices/KeepAlive.cs b/src/Umbraco.Infrastructure/HostedServices/KeepAlive.cs index 550cb56674..e6b8ce47eb 100644 --- a/src/Umbraco.Infrastructure/HostedServices/KeepAlive.cs +++ b/src/Umbraco.Infrastructure/HostedServices/KeepAlive.cs @@ -49,7 +49,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices IProfilingLogger profilingLogger, IServerRoleAccessor serverRegistrar, IHttpClientFactory httpClientFactory) - : base(TimeSpan.FromMinutes(5), DefaultDelay) + : base(logger, TimeSpan.FromMinutes(5), DefaultDelay) { _hostingEnvironment = hostingEnvironment; _mainDom = mainDom; diff --git a/src/Umbraco.Infrastructure/HostedServices/LogScrubber.cs b/src/Umbraco.Infrastructure/HostedServices/LogScrubber.cs index 31dc84d87d..6c59b21fd9 100644 --- a/src/Umbraco.Infrastructure/HostedServices/LogScrubber.cs +++ b/src/Umbraco.Infrastructure/HostedServices/LogScrubber.cs @@ -48,7 +48,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices IScopeProvider scopeProvider, ILogger logger, IProfilingLogger profilingLogger) - : base(TimeSpan.FromHours(4), DefaultDelay) + : base(logger, TimeSpan.FromHours(4), DefaultDelay) { _mainDom = mainDom; _serverRegistrar = serverRegistrar; diff --git a/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs b/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs index 34b13c6e5a..841db4b0bd 100644 --- a/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs +++ b/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs @@ -5,6 +5,8 @@ using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.HostedServices { @@ -21,6 +23,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices /// protected static readonly TimeSpan DefaultDelay = TimeSpan.FromMinutes(3); + private readonly ILogger _logger; private TimeSpan _period; private readonly TimeSpan _delay; private Timer? _timer; @@ -29,14 +32,22 @@ namespace Umbraco.Cms.Infrastructure.HostedServices /// /// Initializes a new instance of the class. /// - /// Timepsan representing how often the task should recur. - /// Timespan represeting the initial delay after application start-up before the first run of the task occurs. - protected RecurringHostedServiceBase(TimeSpan period, TimeSpan delay) + /// Logger. + /// Timespan representing how often the task should recur. + /// Timespan representing the initial delay after application start-up before the first run of the task occurs. + protected RecurringHostedServiceBase(ILogger logger, TimeSpan period, TimeSpan delay) { + _logger = logger; _period = period; _delay = delay; } + // Scheduled for removal in V11 + [Obsolete("Please use constructor that takes an ILogger instead")] + protected RecurringHostedServiceBase(TimeSpan period, TimeSpan delay) + : this(null, period, delay) + { } + /// /// Change the period between operations. /// @@ -46,7 +57,11 @@ namespace Umbraco.Cms.Infrastructure.HostedServices /// public Task StartAsync(CancellationToken cancellationToken) { - _timer = new Timer(ExecuteAsync, null, (int)_delay.TotalMilliseconds, (int)_period.TotalMilliseconds); + using (!ExecutionContext.IsFlowSuppressed() ? (IDisposable)ExecutionContext.SuppressFlow() : null) + { + _timer = new Timer(ExecuteAsync, null, (int)_delay.TotalMilliseconds, (int)_period.TotalMilliseconds); + } + return Task.CompletedTask; } @@ -67,6 +82,11 @@ namespace Umbraco.Cms.Infrastructure.HostedServices // Hat-tip: https://stackoverflow.com/a/14207615/489433 await PerformExecuteAsync(state); } + catch (Exception ex) + { + ILogger logger = _logger ?? StaticApplicationLogging.CreateLogger(GetType()); + logger.LogError(ex, "Unhandled exception in recurring hosted service."); + } finally { // Resume now that the task is complete - Note we use period in both because we don't want to execute again after the delay. @@ -91,7 +111,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices { if (disposing) { - _timer?.Dispose(); + _timer?.Dispose(); } _disposedValue = true; diff --git a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs index 431409a240..54137fad99 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs @@ -23,7 +23,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices public ReportSiteTask( ILogger logger, ITelemetryService telemetryService) - : base(TimeSpan.FromDays(1), TimeSpan.FromMinutes(1)) + : base(logger, TimeSpan.FromDays(1), TimeSpan.FromMinutes(1)) { _logger = logger; _telemetryService = telemetryService; diff --git a/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs b/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs index 7b8c87ceaa..4a70e69c71 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs @@ -45,7 +45,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices ILogger logger, IServerMessenger serverMessenger, IScopeProvider scopeProvider) - : base(TimeSpan.FromMinutes(1), DefaultDelay) + : base(logger, TimeSpan.FromMinutes(1), DefaultDelay) { _runtimeState = runtimeState; _mainDom = mainDom; diff --git a/src/Umbraco.Infrastructure/HostedServices/ServerRegistration/InstructionProcessTask.cs b/src/Umbraco.Infrastructure/HostedServices/ServerRegistration/InstructionProcessTask.cs index 77a7e0226f..d153366949 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ServerRegistration/InstructionProcessTask.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ServerRegistration/InstructionProcessTask.cs @@ -30,7 +30,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration /// The typed logger. /// The configuration for global settings. public InstructionProcessTask(IRuntimeState runtimeState, IServerMessenger messenger, ILogger logger, IOptions globalSettings) - : base(globalSettings.Value.DatabaseServerMessenger.TimeBetweenSyncOperations, TimeSpan.FromMinutes(1)) + : base(logger, globalSettings.Value.DatabaseServerMessenger.TimeBetweenSyncOperations, TimeSpan.FromMinutes(1)) { _runtimeState = runtimeState; _messenger = messenger; diff --git a/src/Umbraco.Infrastructure/HostedServices/ServerRegistration/TouchServerTask.cs b/src/Umbraco.Infrastructure/HostedServices/ServerRegistration/TouchServerTask.cs index a51f1eb95b..d755324878 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ServerRegistration/TouchServerTask.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ServerRegistration/TouchServerTask.cs @@ -44,7 +44,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration ILogger logger, IOptionsMonitor globalSettings, IServerRoleAccessor serverRoleAccessor) - : base(globalSettings.CurrentValue.DatabaseServerRegistrar.WaitTimeBetweenCalls, TimeSpan.FromSeconds(15)) + : base(logger, globalSettings.CurrentValue.DatabaseServerRegistrar.WaitTimeBetweenCalls, TimeSpan.FromSeconds(15)) { _runtimeState = runtimeState; _serverRegistrationService = serverRegistrationService ?? throw new ArgumentNullException(nameof(serverRegistrationService)); diff --git a/src/Umbraco.Infrastructure/HostedServices/TempFileCleanup.cs b/src/Umbraco.Infrastructure/HostedServices/TempFileCleanup.cs index fb70ce39f7..ec8d48bca3 100644 --- a/src/Umbraco.Infrastructure/HostedServices/TempFileCleanup.cs +++ b/src/Umbraco.Infrastructure/HostedServices/TempFileCleanup.cs @@ -33,7 +33,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices /// Representation of the main application domain. /// The typed logger. public TempFileCleanup(IIOHelper ioHelper, IMainDom mainDom, ILogger logger) - : base(TimeSpan.FromMinutes(60), DefaultDelay) + : base(logger, TimeSpan.FromMinutes(60), DefaultDelay) { _ioHelper = ioHelper; _mainDom = mainDom; diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs index fb4ac3ec7e..8b1f8bb7ac 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs @@ -1,7 +1,11 @@ using System; +using System.Collections.Generic; +using System.Linq; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using NPoco; using Umbraco.Cms.Core.Configuration; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Infrastructure.Migrations.Upgrade; using Umbraco.Cms.Infrastructure.Persistence.Dtos; @@ -17,12 +21,25 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install private readonly IDatabase _database; private readonly ILogger _logger; private readonly IUmbracoVersion _umbracoVersion; + private readonly IOptionsMonitor _installDefaultDataSettings; - public DatabaseDataCreator(IDatabase database, ILogger logger, IUmbracoVersion umbracoVersion) + private readonly IDictionary> _entitiesToAlwaysCreate = new Dictionary>() + { + { + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + new List + { + Cms.Core.Constants.DataTypes.Guids.LabelString, + } + } + }; + + public DatabaseDataCreator(IDatabase database, ILogger logger, IUmbracoVersion umbracoVersion, IOptionsMonitor installDefaultDataSettings) { _database = database; _logger = logger; _umbracoVersion = umbracoVersion; + _installDefaultDataSettings = installDefaultDataSettings; } /// @@ -35,54 +52,91 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install _logger.LogInformation("Creating data in {TableName}", tableName); if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.Node)) + { CreateNodeData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.Lock)) + { CreateLockData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.ContentType)) + { CreateContentTypeData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.User)) + { CreateUserData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.UserGroup)) + { CreateUserGroupData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.User2UserGroup)) + { CreateUser2UserGroupData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.UserGroup2App)) + { CreateUserGroup2AppData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup)) + { CreatePropertyTypeGroupData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType)) + { CreatePropertyTypeData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.Language)) + { CreateLanguageData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType)) + { CreateContentChildTypeData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.DataType)) + { CreateDataTypeData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.RelationType)) + { CreateRelationTypeData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.KeyValue)) + { CreateKeyValueData(); + } if (tableName.Equals(Cms.Core.Constants.DatabaseSchema.Tables.LogViewerQuery)) + { CreateLogViewerQueryData(); + } _logger.LogInformation("Completed creating data in {TableName}", tableName); } private void CreateNodeData() + { + CreateNodeDataForDataTypes(); + CreateNodeDataForMediaTypes(); + CreateNodeDataForMemberTypes(); + } + + private void CreateNodeDataForDataTypes() { void InsertDataTypeNodeDto(int id, int sortOrder, string uniqueId, string text) { @@ -98,66 +152,287 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install UniqueId = new Guid(uniqueId), Text = text, NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, - CreateDate = DateTime.Now + CreateDate = DateTime.Now, }; - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, nodeDto); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + uniqueId, + nodeDto, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); } _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = -1, Trashed = false, ParentId = -1, UserId = -1, Level = 0, Path = "-1", SortOrder = 0, UniqueId = new Guid("916724a5-173d-4619-b97e-b9de133dd6f5"), Text = "SYSTEM DATA: umbraco master root", NodeObjectType = Cms.Core.Constants.ObjectTypes.SystemRoot, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = -20, Trashed = false, ParentId = -1, UserId = -1, Level = 0, Path = "-1,-20", SortOrder = 0, UniqueId = new Guid("0F582A79-1E41-4CF0-BFA0-76340651891A"), Text = "Recycle Bin", NodeObjectType = Cms.Core.Constants.ObjectTypes.ContentRecycleBin, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = -21, Trashed = false, ParentId = -1, UserId = -1, Level = 0, Path = "-1,-21", SortOrder = 0, UniqueId = new Guid("BF7C7CBC-952F-4518-97A2-69E9C7B33842"), Text = "Recycle Bin", NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaRecycleBin, CreateDate = DateTime.Now }); + InsertDataTypeNodeDto(Cms.Core.Constants.DataTypes.LabelString, 35, Cms.Core.Constants.DataTypes.Guids.LabelString, "Label (string)"); InsertDataTypeNodeDto(Cms.Core.Constants.DataTypes.LabelInt, 36, Cms.Core.Constants.DataTypes.Guids.LabelInt, "Label (integer)"); InsertDataTypeNodeDto(Cms.Core.Constants.DataTypes.LabelBigint, 36, Cms.Core.Constants.DataTypes.Guids.LabelBigInt, "Label (bigint)"); InsertDataTypeNodeDto(Cms.Core.Constants.DataTypes.LabelDateTime, 37, Cms.Core.Constants.DataTypes.Guids.LabelDateTime, "Label (datetime)"); InsertDataTypeNodeDto(Cms.Core.Constants.DataTypes.LabelTime, 38, Cms.Core.Constants.DataTypes.Guids.LabelTime, "Label (time)"); InsertDataTypeNodeDto(Cms.Core.Constants.DataTypes.LabelDecimal, 39, Cms.Core.Constants.DataTypes.Guids.LabelDecimal, "Label (decimal)"); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Upload, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Upload}", SortOrder = 34, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadGuid, Text = "Upload File", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.UploadVideo, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.UploadVideo}", SortOrder = 35, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadVideoGuid, Text = "Upload Video", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.UploadAudio, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.UploadAudio}", SortOrder = 36, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadAudioGuid, Text = "Upload Audio", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.UploadArticle, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.UploadArticle}", SortOrder = 37, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadArticleGuid, Text = "Upload Article", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.UploadVectorGraphics, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.UploadVectorGraphics}", SortOrder = 38, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadVectorGraphicsGuid, Text = "Upload Vector Graphics", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Textarea, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Textarea}", SortOrder = 33, UniqueId = Cms.Core.Constants.DataTypes.Guids.TextareaGuid, Text = "Textarea", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Textbox, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Textbox}", SortOrder = 32, UniqueId = Cms.Core.Constants.DataTypes.Guids.TextstringGuid, Text = "Textstring", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.RichtextEditor, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.RichtextEditor}", SortOrder = 4, UniqueId = Cms.Core.Constants.DataTypes.Guids.RichtextEditorGuid, Text = "Richtext editor", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = -51, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-51", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.NumericGuid, Text = "Numeric", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Boolean, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Boolean}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.CheckboxGuid, Text = "True/false", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = -43, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-43", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.CheckboxListGuid, Text = "Checkbox list", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.DropDownSingle, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.DropDownSingle}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.DropdownGuid, Text = "Dropdown", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = -41, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-41", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.DatePickerGuid, Text = "Date Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = -40, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-40", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.RadioboxGuid, Text = "Radiobox", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.DropDownMultiple, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.DropDownMultiple}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.DropdownMultipleGuid, Text = "Dropdown multiple", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = -37, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-37", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.ApprovedColorGuid, Text = "Approved Color", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.DateTime, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.DateTime}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.DatePickerWithTimeGuid, Text = "Date Picker with time", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.DefaultContentListView, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.DefaultContentListView}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.ListViewContentGuid, Text = Cms.Core.Constants.Conventions.DataTypes.ListViewPrefix + "Content", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.DefaultMediaListView, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.DefaultMediaListView}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.ListViewMediaGuid, Text = Cms.Core.Constants.Conventions.DataTypes.ListViewPrefix + "Media", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.DefaultMembersListView, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.DefaultMembersListView}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.ListViewMembersGuid, Text = Cms.Core.Constants.Conventions.DataTypes.ListViewPrefix + "Members", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1031, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1031", SortOrder = 2, UniqueId = new Guid("f38bd2d7-65d0-48e6-95dc-87ce06ec2d3d"), Text = Cms.Core.Constants.Conventions.MediaTypes.Folder, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1032, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1032", SortOrder = 2, UniqueId = new Guid("cc07b313-0843-4aa8-bbda-871c8da728c8"), Text = Cms.Core.Constants.Conventions.MediaTypes.Image, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1033, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1033", SortOrder = 2, UniqueId = new Guid("4c52d8ab-54e6-40cd-999c-7a5f24903e4d"), Text = Cms.Core.Constants.Conventions.MediaTypes.File, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1034, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1034", SortOrder = 2, UniqueId = new Guid("f6c515bb-653c-4bdc-821c-987729ebe327"), Text = Cms.Core.Constants.Conventions.MediaTypes.Video, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1035, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1035", SortOrder = 2, UniqueId = new Guid("a5ddeee0-8fd8-4cee-a658-6f1fcdb00de3"), Text = Cms.Core.Constants.Conventions.MediaTypes.Audio, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1036, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1036", SortOrder = 2, UniqueId = new Guid("a43e3414-9599-4230-a7d3-943a21b20122"), Text = Cms.Core.Constants.Conventions.MediaTypes.Article, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1037, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1037", SortOrder = 2, UniqueId = new Guid("c4b1efcf-a9d5-41c4-9621-e9d273b52a9c"), Text = "Vector Graphics (SVG)", NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.Upload, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Upload, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Upload}", SortOrder = 34, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadGuid, Text = "Upload File", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.UploadVideo, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.UploadVideo, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.UploadVideo}", SortOrder = 35, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadVideoGuid, Text = "Upload Video", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.UploadAudio, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.UploadAudio, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.UploadAudio}", SortOrder = 36, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadAudioGuid, Text = "Upload Audio", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.UploadArticle, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.UploadArticle, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.UploadArticle}", SortOrder = 37, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadArticleGuid, Text = "Upload Article", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.UploadVectorGraphics, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.UploadVectorGraphics, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.UploadVectorGraphics}", SortOrder = 38, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadVectorGraphicsGuid, Text = "Upload Vector Graphics", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.Textarea, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Textarea, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Textarea}", SortOrder = 33, UniqueId = Cms.Core.Constants.DataTypes.Guids.TextareaGuid, Text = "Textarea", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.Textstring, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Textbox, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Textbox}", SortOrder = 32, UniqueId = Cms.Core.Constants.DataTypes.Guids.TextstringGuid, Text = "Textstring", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.RichtextEditor, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.RichtextEditor, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.RichtextEditor}", SortOrder = 4, UniqueId = Cms.Core.Constants.DataTypes.Guids.RichtextEditorGuid, Text = "Richtext editor", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.Numeric, + new NodeDto { NodeId = -51, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-51", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.NumericGuid, Text = "Numeric", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.Checkbox, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Boolean, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Boolean}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.CheckboxGuid, Text = "True/false", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.CheckboxList, + new NodeDto { NodeId = -43, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-43", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.CheckboxListGuid, Text = "Checkbox list", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.Dropdown, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.DropDownSingle, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.DropDownSingle}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.DropdownGuid, Text = "Dropdown", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.DatePicker, + new NodeDto { NodeId = -41, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-41", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.DatePickerGuid, Text = "Date Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.Radiobox, + new NodeDto { NodeId = -40, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-40", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.RadioboxGuid, Text = "Radiobox", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.DropdownMultiple, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.DropDownMultiple, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.DropDownMultiple}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.DropdownMultipleGuid, Text = "Dropdown multiple", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.ApprovedColor, + new NodeDto { NodeId = -37, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-37", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.ApprovedColorGuid, Text = "Approved Color", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.DatePickerWithTime, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.DateTime, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.DateTime}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.DatePickerWithTimeGuid, Text = "Date Picker with time", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.ListViewContent, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.DefaultContentListView, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.DefaultContentListView}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.ListViewContentGuid, Text = Cms.Core.Constants.Conventions.DataTypes.ListViewPrefix + "Content", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.ListViewMedia, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.DefaultMediaListView, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.DefaultMediaListView}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.ListViewMediaGuid, Text = Cms.Core.Constants.Conventions.DataTypes.ListViewPrefix + "Media", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.ListViewMembers, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.DefaultMembersListView, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.DefaultMembersListView}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.ListViewMembersGuid, Text = Cms.Core.Constants.Conventions.DataTypes.ListViewPrefix + "Members", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.Tags, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Tags, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Tags}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.TagsGuid, Text = "Tags", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.ImageCropper, + new NodeDto { NodeId = Cms.Core.Constants.DataTypes.ImageCropper, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.ImageCropper}", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.ImageCropperGuid, Text = "Image Cropper", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Tags, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Tags}", SortOrder = 2, UniqueId = new Guid("b6b73142-b9c1-4bf8-a16d-e1c23320b549"), Text = "Tags", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.ImageCropper, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.ImageCropper}", SortOrder = 2, UniqueId = new Guid("1df9f033-e6d4-451f-b8d2-e0cbc50a836f"), Text = "Image Cropper", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1044, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1044", SortOrder = 0, UniqueId = new Guid("d59be02f-1df9-4228-aa1e-01917d806cda"), Text = Cms.Core.Constants.Conventions.MemberTypes.DefaultAlias, NodeObjectType = Cms.Core.Constants.ObjectTypes.MemberType, CreateDate = DateTime.Now }); + // New UDI pickers with newer Ids + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.ContentPicker, + new NodeDto { NodeId = 1046, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1046", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.ContentPickerGuid, Text = "Content Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.MemberPicker, + new NodeDto { NodeId = 1047, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1047", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MemberPickerGuid, Text = "Member Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.MediaPicker, + new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPickerGuid, Text = "Media Picker (legacy)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.MultipleMediaPicker, + new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MultipleMediaPickerGuid, Text = "Multiple Media Picker (legacy)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.RelatedLinks, + new NodeDto { NodeId = 1050, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1050", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.RelatedLinksGuid, Text = "Multi URL Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); - //New UDI pickers with newer Ids - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1046, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1046", SortOrder = 2, UniqueId = new Guid("FD1E0DA5-5606-4862-B679-5D0CF3A52A59"), Text = "Content Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1047, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1047", SortOrder = 2, UniqueId = new Guid("1EA2E01F-EBD8-4CE1-8D71-6B1149E63548"), Text = "Member Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "Media Picker (legacy)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = new Guid("9DBBCBBB-2327-434A-B355-AF1B84E5010A"), Text = "Multiple Media Picker (legacy)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1050, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1050", SortOrder = 2, UniqueId = new Guid("B4E3535A-1753-47E2-8568-602CF8CFEE6F"), Text = "Multi URL Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.MediaPicker3, + new NodeDto { NodeId = 1051, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1051", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3Guid, Text = "Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.MediaPicker3Multiple, + new NodeDto { NodeId = 1052, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1052", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleGuid, Text = "Multiple Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.MediaPicker3SingleImage, + new NodeDto { NodeId = 1053, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1053", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3SingleImageGuid, Text = "Image Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes, + Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleImages, + new NodeDto { NodeId = 1054, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1054", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleImagesGuid, Text = "Multiple Image Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + } - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1051, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1051", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3Guid, Text = "Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1052, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1052", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleGuid, Text = "Multiple Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1053, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1053", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3SingleImageGuid, Text = "Image Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1054, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1054", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleImagesGuid, Text = "Multiple Image Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + private void CreateNodeDataForMediaTypes() + { + var folderUniqueId = new Guid("f38bd2d7-65d0-48e6-95dc-87ce06ec2d3d"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.MediaTypes, + folderUniqueId.ToString(), + new NodeDto { NodeId = 1031, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1031", SortOrder = 2, UniqueId = folderUniqueId, Text = Cms.Core.Constants.Conventions.MediaTypes.Folder, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + var imageUniqueId = new Guid("cc07b313-0843-4aa8-bbda-871c8da728c8"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.MediaTypes, + imageUniqueId.ToString(), + new NodeDto { NodeId = 1032, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1032", SortOrder = 2, UniqueId = imageUniqueId, Text = Cms.Core.Constants.Conventions.MediaTypes.Image, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + + var fileUniqueId = new Guid("4c52d8ab-54e6-40cd-999c-7a5f24903e4d"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.MediaTypes, + fileUniqueId.ToString(), + new NodeDto { NodeId = 1033, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1033", SortOrder = 2, UniqueId = fileUniqueId, Text = Cms.Core.Constants.Conventions.MediaTypes.File, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + + var videoUniqueId = new Guid("f6c515bb-653c-4bdc-821c-987729ebe327"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.MediaTypes, + videoUniqueId.ToString(), + new NodeDto { NodeId = 1034, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1034", SortOrder = 2, UniqueId = videoUniqueId, Text = Cms.Core.Constants.Conventions.MediaTypes.Video, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + + var audioUniqueId = new Guid("a5ddeee0-8fd8-4cee-a658-6f1fcdb00de3"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.MediaTypes, + audioUniqueId.ToString(), + new NodeDto { NodeId = 1035, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1035", SortOrder = 2, UniqueId = audioUniqueId, Text = Cms.Core.Constants.Conventions.MediaTypes.Audio, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + + var articleUniqueId = new Guid("a43e3414-9599-4230-a7d3-943a21b20122"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.MediaTypes, + articleUniqueId.ToString(), + new NodeDto { NodeId = 1036, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1036", SortOrder = 2, UniqueId = articleUniqueId, Text = Cms.Core.Constants.Conventions.MediaTypes.Article, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + + var svgUniqueId = new Guid("c4b1efcf-a9d5-41c4-9621-e9d273b52a9c"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.MediaTypes, + svgUniqueId.ToString(), + new NodeDto { NodeId = 1037, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1037", SortOrder = 2, UniqueId = svgUniqueId, Text = "Vector Graphics (SVG)", NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); + } + + private void CreateNodeDataForMemberTypes() + { + var memberUniqueId = new Guid("d59be02f-1df9-4228-aa1e-01917d806cda"); + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.MemberTypes, + memberUniqueId.ToString(), + new NodeDto { NodeId = 1044, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1044", SortOrder = 0, UniqueId = memberUniqueId, Text = Cms.Core.Constants.Conventions.MemberTypes.DefaultAlias, NodeObjectType = Cms.Core.Constants.ObjectTypes.MemberType, CreateDate = DateTime.Now }, + Cms.Core.Constants.DatabaseSchema.Tables.Node, + "id"); } private void CreateLockData() @@ -180,20 +455,55 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install private void CreateContentTypeData() { - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 532, NodeId = 1031, Alias = Cms.Core.Constants.Conventions.MediaTypes.Folder, Icon = Cms.Core.Constants.Icons.MediaFolder, Thumbnail = Cms.Core.Constants.Icons.MediaFolder, IsContainer = false, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Cms.Core.Constants.Conventions.MediaTypes.Image, Icon = Cms.Core.Constants.Icons.MediaImage, Thumbnail = Cms.Core.Constants.Icons.MediaImage, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Cms.Core.Constants.Conventions.MediaTypes.File, Icon = Cms.Core.Constants.Icons.MediaFile, Thumbnail = Cms.Core.Constants.Icons.MediaFile, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 540, NodeId = 1034, Alias = Cms.Core.Constants.Conventions.MediaTypes.VideoAlias, Icon = Cms.Core.Constants.Icons.MediaVideo, Thumbnail = Cms.Core.Constants.Icons.MediaVideo, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 541, NodeId = 1035, Alias = Cms.Core.Constants.Conventions.MediaTypes.AudioAlias, Icon = Cms.Core.Constants.Icons.MediaAudio, Thumbnail = Cms.Core.Constants.Icons.MediaAudio, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 542, NodeId = 1036, Alias = Cms.Core.Constants.Conventions.MediaTypes.ArticleAlias, Icon = Cms.Core.Constants.Icons.MediaArticle, Thumbnail = Cms.Core.Constants.Icons.MediaArticle, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 543, NodeId = 1037, Alias = Cms.Core.Constants.Conventions.MediaTypes.VectorGraphicsAlias, Icon = Cms.Core.Constants.Icons.MediaVectorGraphics, Thumbnail = Cms.Core.Constants.Icons.MediaVectorGraphics, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); + // Insert content types only if the corresponding Node record exists (which may or may not have been created depending on configuration + // of media or member types to create). - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Cms.Core.Constants.Conventions.MemberTypes.DefaultAlias, Icon = Cms.Core.Constants.Icons.Member, Thumbnail = Cms.Core.Constants.Icons.Member, Variations = (byte) ContentVariation.Nothing }); + // Media types. + if (_database.Exists(1031)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 532, NodeId = 1031, Alias = Cms.Core.Constants.Conventions.MediaTypes.Folder, Icon = Cms.Core.Constants.Icons.MediaFolder, Thumbnail = Cms.Core.Constants.Icons.MediaFolder, IsContainer = false, AllowAtRoot = true, Variations = (byte)ContentVariation.Nothing }); + } + + if (_database.Exists(1032)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Cms.Core.Constants.Conventions.MediaTypes.Image, Icon = Cms.Core.Constants.Icons.MediaImage, Thumbnail = Cms.Core.Constants.Icons.MediaImage, AllowAtRoot = true, Variations = (byte)ContentVariation.Nothing }); + } + + if (_database.Exists(1033)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Cms.Core.Constants.Conventions.MediaTypes.File, Icon = Cms.Core.Constants.Icons.MediaFile, Thumbnail = Cms.Core.Constants.Icons.MediaFile, AllowAtRoot = true, Variations = (byte)ContentVariation.Nothing }); + } + + if (_database.Exists(1034)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 540, NodeId = 1034, Alias = Cms.Core.Constants.Conventions.MediaTypes.VideoAlias, Icon = Cms.Core.Constants.Icons.MediaVideo, Thumbnail = Cms.Core.Constants.Icons.MediaVideo, AllowAtRoot = true, Variations = (byte)ContentVariation.Nothing }); + } + + if (_database.Exists(1035)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 541, NodeId = 1035, Alias = Cms.Core.Constants.Conventions.MediaTypes.AudioAlias, Icon = Cms.Core.Constants.Icons.MediaAudio, Thumbnail = Cms.Core.Constants.Icons.MediaAudio, AllowAtRoot = true, Variations = (byte)ContentVariation.Nothing }); + } + + if (_database.Exists(1036)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 542, NodeId = 1036, Alias = Cms.Core.Constants.Conventions.MediaTypes.ArticleAlias, Icon = Cms.Core.Constants.Icons.MediaArticle, Thumbnail = Cms.Core.Constants.Icons.MediaArticle, AllowAtRoot = true, Variations = (byte)ContentVariation.Nothing }); + } + + if (_database.Exists(1037)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 543, NodeId = 1037, Alias = Cms.Core.Constants.Conventions.MediaTypes.VectorGraphicsAlias, Icon = Cms.Core.Constants.Icons.MediaVectorGraphics, Thumbnail = Cms.Core.Constants.Icons.MediaVectorGraphics, AllowAtRoot = true, Variations = (byte)ContentVariation.Nothing }); + } + + // Member type. + if (_database.Exists(1044)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Cms.Core.Constants.Conventions.MemberTypes.DefaultAlias, Icon = Cms.Core.Constants.Icons.Member, Thumbnail = Cms.Core.Constants.Icons.Member, Variations = (byte)ContentVariation.Nothing }); + } } private void CreateUserData() { - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.User, "id", false, new UserDto { Id = Cms.Core.Constants.Security.SuperUserId, Disabled = false, NoConsole = false, UserName = "Administrator", Login = "admin", Password = "default", Email = "", UserLanguage = "en-US", CreateDate = DateTime.Now, UpdateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.User, "id", false, new UserDto { Id = Cms.Core.Constants.Security.SuperUserId, Disabled = false, NoConsole = false, UserName = "Administrator", Login = "admin", Password = "default", Email = string.Empty, UserLanguage = "en-US", CreateDate = DateTime.Now, UpdateDate = DateTime.Now }); } private void CreateUserGroupData() @@ -202,7 +512,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 2, StartMediaId = -1, StartContentId = -1, Alias = Cms.Core.Constants.Security.WriterGroupAlias, Name = "Writers", DefaultPermissions = "CAH:FN", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-edit" }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 3, StartMediaId = -1, StartContentId = -1, Alias = Cms.Core.Constants.Security.EditorGroupAlias, Name = "Editors", DefaultPermissions = "CADMOSKTPUZ:5FïN", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-tools" }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 4, StartMediaId = -1, StartContentId = -1, Alias = Cms.Core.Constants.Security.TranslatorGroupAlias, Name = "Translators", DefaultPermissions = "AF", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-globe" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 5, StartMediaId = -1, StartContentId = -1, Alias = Cms.Core.Constants.Security.SensitiveDataGroupAlias, Name = "Sensitive data", DefaultPermissions = "", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-lock" }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 5, StartMediaId = -1, StartContentId = -1, Alias = Cms.Core.Constants.Security.SensitiveDataGroupAlias, Name = "Sensitive data", DefaultPermissions = string.Empty, CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-lock" }); } private void CreateUser2UserGroupData() @@ -233,67 +543,136 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install private void CreatePropertyTypeGroupData() { - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 3, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Image), ContentTypeNodeId = 1032, Text = "Image", Alias = "image", SortOrder = 1 }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 4, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.File), ContentTypeNodeId = 1033, Text = "File", Alias = "file", SortOrder = 1, }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 52, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Video), ContentTypeNodeId = 1034, Text = "Video", Alias = "video", SortOrder = 1 }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 53, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Audio), ContentTypeNodeId = 1035, Text = "Audio", Alias = "audio", SortOrder = 1 }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 54, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Article), ContentTypeNodeId = 1036, Text = "Article", Alias = "article", SortOrder = 1 }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 55, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.VectorGraphics), ContentTypeNodeId = 1037, Text = "Vector Graphics", Alias = "vectorGraphics", SortOrder = 1 }); - //membership property group - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 11, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Membership), ContentTypeNodeId = 1044, Text = Cms.Core.Constants.Conventions.Member.StandardPropertiesGroupName, Alias = Cms.Core.Constants.Conventions.Member.StandardPropertiesGroupAlias, SortOrder = 1 }); + // Insert property groups only if the corresponding content type node record exists (which may or may not have been created depending on configuration + // of media or member types to create). + + // Media property groups. + if (_database.Exists(1032)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 3, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Image), ContentTypeNodeId = 1032, Text = "Image", Alias = "image", SortOrder = 1 }); + } + + if (_database.Exists(1033)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 4, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.File), ContentTypeNodeId = 1033, Text = "File", Alias = "file", SortOrder = 1, }); + } + + if (_database.Exists(1034)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 52, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Video), ContentTypeNodeId = 1034, Text = "Video", Alias = "video", SortOrder = 1 }); + } + + if (_database.Exists(1035)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 53, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Audio), ContentTypeNodeId = 1035, Text = "Audio", Alias = "audio", SortOrder = 1 }); + } + + if (_database.Exists(1036)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 54, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Article), ContentTypeNodeId = 1036, Text = "Article", Alias = "article", SortOrder = 1 }); + } + + if (_database.Exists(1037)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 55, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.VectorGraphics), ContentTypeNodeId = 1037, Text = "Vector Graphics", Alias = "vectorGraphics", SortOrder = 1 }); + } + + // Membership property group. + if (_database.Exists(1044)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 11, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Membership), ContentTypeNodeId = 1044, Text = Cms.Core.Constants.Conventions.Member.StandardPropertiesGroupName, Alias = Cms.Core.Constants.Conventions.Member.StandardPropertiesGroupAlias, SortOrder = 1 }); + } } private void CreatePropertyTypeData() { - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 6, UniqueId = 6.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.ImageCropper, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Image", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 7, UniqueId = 7.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelInt, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.Width, Name = "Width", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in pixels", Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 8, UniqueId = 8.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelInt, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.Height, Name = "Height", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in pixels", Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 9, UniqueId = 9.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 10, UniqueId = 10.ToGuid(), DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 24, UniqueId = 24.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.Upload, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "File", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 25, UniqueId = 25.ToGuid(), DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 26, UniqueId = 26.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + // Insert property types only if the corresponding property group record exists (which may or may not have been created depending on configuration + // of media or member types to create). - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 40, UniqueId = 40.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.UploadVideo, ContentTypeId = 1034, PropertyTypeGroupId = 52, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Video", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 41, UniqueId = 41.ToGuid(), DataTypeId = -92, ContentTypeId = 1034, PropertyTypeGroupId = 52, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 42, UniqueId = 42.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1034, PropertyTypeGroupId = 52, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + // Media property types. + if (_database.Exists(3)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 6, UniqueId = 6.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.ImageCropper, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Image", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 7, UniqueId = 7.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelInt, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.Width, Name = "Width", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in pixels", Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 8, UniqueId = 8.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelInt, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.Height, Name = "Height", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in pixels", Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 9, UniqueId = 9.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 10, UniqueId = 10.ToGuid(), DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + } - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 43, UniqueId = 43.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.UploadAudio, ContentTypeId = 1035, PropertyTypeGroupId = 53, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Audio", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 44, UniqueId = 44.ToGuid(), DataTypeId = -92, ContentTypeId = 1035, PropertyTypeGroupId = 53, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 45, UniqueId = 45.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1035, PropertyTypeGroupId = 53, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + if (_database.Exists(4)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 24, UniqueId = 24.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.Upload, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "File", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 25, UniqueId = 25.ToGuid(), DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 26, UniqueId = 26.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte)ContentVariation.Nothing }); + } - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 46, UniqueId = 46.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.UploadArticle, ContentTypeId = 1036, PropertyTypeGroupId = 54, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Article", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 47, UniqueId = 47.ToGuid(), DataTypeId = -92, ContentTypeId = 1036, PropertyTypeGroupId = 54, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 48, UniqueId = 48.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1036, PropertyTypeGroupId = 54, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + if (_database.Exists(52)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 40, UniqueId = 40.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.UploadVideo, ContentTypeId = 1034, PropertyTypeGroupId = 52, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Video", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 41, UniqueId = 41.ToGuid(), DataTypeId = -92, ContentTypeId = 1034, PropertyTypeGroupId = 52, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 42, UniqueId = 42.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1034, PropertyTypeGroupId = 52, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte)ContentVariation.Nothing }); + } - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 49, UniqueId = 49.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.UploadVectorGraphics, ContentTypeId = 1037, PropertyTypeGroupId = 55, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Vector Graphics", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 50, UniqueId = 50.ToGuid(), DataTypeId = -92, ContentTypeId = 1037, PropertyTypeGroupId = 55, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 51, UniqueId = 51.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1037, PropertyTypeGroupId = 55, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + if (_database.Exists(53)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 43, UniqueId = 43.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.UploadAudio, ContentTypeId = 1035, PropertyTypeGroupId = 53, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Audio", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 44, UniqueId = 44.ToGuid(), DataTypeId = -92, ContentTypeId = 1035, PropertyTypeGroupId = 53, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 45, UniqueId = 45.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1035, PropertyTypeGroupId = 53, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte)ContentVariation.Nothing }); + } - //membership property types - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 28, UniqueId = 28.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.Textarea, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.Comments, Name = Cms.Core.Constants.Conventions.Member.CommentsLabel, SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 29, UniqueId = 29.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelInt, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.FailedPasswordAttempts, Name = Cms.Core.Constants.Conventions.Member.FailedPasswordAttemptsLabel, SortOrder = 1, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 30, UniqueId = 30.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.Boolean, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.IsApproved, Name = Cms.Core.Constants.Conventions.Member.IsApprovedLabel, SortOrder = 2, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 31, UniqueId = 31.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.Boolean, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.IsLockedOut, Name = Cms.Core.Constants.Conventions.Member.IsLockedOutLabel, SortOrder = 3, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 32, UniqueId = 32.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.LastLockoutDate, Name = Cms.Core.Constants.Conventions.Member.LastLockoutDateLabel, SortOrder = 4, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 33, UniqueId = 33.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.LastLoginDate, Name = Cms.Core.Constants.Conventions.Member.LastLoginDateLabel, SortOrder = 5, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 34, UniqueId = 34.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.LastPasswordChangeDate, Name = Cms.Core.Constants.Conventions.Member.LastPasswordChangeDateLabel, SortOrder = 6, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + if (_database.Exists(54)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 46, UniqueId = 46.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.UploadArticle, ContentTypeId = 1036, PropertyTypeGroupId = 54, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Article", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 47, UniqueId = 47.ToGuid(), DataTypeId = -92, ContentTypeId = 1036, PropertyTypeGroupId = 54, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 48, UniqueId = 48.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1036, PropertyTypeGroupId = 54, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte)ContentVariation.Nothing }); + } + + if (_database.Exists(55)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 49, UniqueId = 49.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.UploadVectorGraphics, ContentTypeId = 1037, PropertyTypeGroupId = 55, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Vector Graphics", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 50, UniqueId = 50.ToGuid(), DataTypeId = -92, ContentTypeId = 1037, PropertyTypeGroupId = 55, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 51, UniqueId = 51.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1037, PropertyTypeGroupId = 55, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte)ContentVariation.Nothing }); + } + + // Membership property types. + if (_database.Exists(11)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 28, UniqueId = 28.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.Textarea, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.Comments, Name = Cms.Core.Constants.Conventions.Member.CommentsLabel, SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 29, UniqueId = 29.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelInt, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.FailedPasswordAttempts, Name = Cms.Core.Constants.Conventions.Member.FailedPasswordAttemptsLabel, SortOrder = 1, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 30, UniqueId = 30.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.Boolean, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.IsApproved, Name = Cms.Core.Constants.Conventions.Member.IsApprovedLabel, SortOrder = 2, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 31, UniqueId = 31.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.Boolean, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.IsLockedOut, Name = Cms.Core.Constants.Conventions.Member.IsLockedOutLabel, SortOrder = 3, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 32, UniqueId = 32.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.LastLockoutDate, Name = Cms.Core.Constants.Conventions.Member.LastLockoutDateLabel, SortOrder = 4, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 33, UniqueId = 33.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.LastLoginDate, Name = Cms.Core.Constants.Conventions.Member.LastLoginDateLabel, SortOrder = 5, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 34, UniqueId = 34.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.LastPasswordChangeDate, Name = Cms.Core.Constants.Conventions.Member.LastPasswordChangeDateLabel, SortOrder = 6, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing }); + } } - private void CreateLanguageData() - { - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Language, "id", false, new LanguageDto { Id = 1, IsoCode = "en-US", CultureName = "English (United States)", IsDefault = true }); - } + private void CreateLanguageData() => + ConditionalInsert( + Cms.Core.Constants.Configuration.NamedOptions.InstallDefaultData.Languages, + "en-us", + new LanguageDto { Id = 1, IsoCode = "en-US", CultureName = "English (United States)", IsDefault = true }, + Cms.Core.Constants.DatabaseSchema.Tables.Language, + "id"); private void CreateContentChildTypeData() { + // Insert data if the corresponding Node records exist (which may or may not have been created depending on configuration + // of media types to create). + if (!_database.Exists(1031)) + { + return; + } + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1031 }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1032 }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1033 }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1034 }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1035 }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1036 }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1037 }); + + for (int i = 1032; i <= 1037; i++) + { + if (_database.Exists(i)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = i }); + } + } } private void CreateDataTypeData() @@ -308,9 +687,14 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install }; if (configuration != null) + { dataTypeDto.Configuration = configuration; + } - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, dataTypeDto); + if (_database.Exists(id)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, dataTypeDto); + } } //layouts for the list view @@ -318,123 +702,288 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install const string listLayout = "{\"name\": \"List\",\"path\": \"views/propertyeditors/listview/layouts/list/list.html\",\"icon\": \"icon-list\", \"isSystem\": 1,\"selected\": true}"; const string layouts = "[" + cardLayout + "," + listLayout + "]"; - // TODO: Check which of the DataTypeIds below doesn't exist in umbracoNode, which results in a foreign key constraint errors. - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.Boolean, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.Boolean, DbType = "Integer" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -51, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.Integer, DbType = "Integer" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -87, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.TinyMce, DbType = "Ntext", - Configuration = "{\"value\":\",code,undo,redo,cut,copy,mcepasteword,stylepicker,bold,italic,bullist,numlist,outdent,indent,mcelink,unlink,mceinsertanchor,mceimage,umbracomacro,mceinserttable,umbracoembed,mcecharmap,|1|1,2,3,|0|500,400|1049,|true|\"}" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.Textbox, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.TextBox, DbType = "Nvarchar" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.Textarea, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.TextArea, DbType = "Ntext" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.Upload, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, DbType = "Nvarchar" }); + // Insert data types only if the corresponding Node record exists (which may or may not have been created depending on configuration + // of data types to create). + if (_database.Exists(Cms.Core.Constants.DataTypes.Boolean)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.Boolean, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.Boolean, DbType = "Integer" }); + } + + if (_database.Exists(-51)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -51, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.Integer, DbType = "Integer" }); + } + + if (_database.Exists(-87)) + { + _database.Insert( + Cms.Core.Constants.DatabaseSchema.Tables.DataType, + "pk", + false, + new DataTypeDto + { + NodeId = -87, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.TinyMce, + DbType = "Ntext", + Configuration = "{\"value\":\",code,undo,redo,cut,copy,mcepasteword,stylepicker,bold,italic,bullist,numlist,outdent,indent,mcelink,unlink,mceinsertanchor,mceimage,umbracomacro,mceinserttable,umbracoembed,mcecharmap,|1|1,2,3,|0|500,400|1049,|true|\"}" + }); + } + + if (_database.Exists(Cms.Core.Constants.DataTypes.Textbox)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.Textbox, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.TextBox, DbType = "Nvarchar" }); + } + + if (_database.Exists(Cms.Core.Constants.DataTypes.Textarea)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.Textarea, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.TextArea, DbType = "Ntext" }); + } + + if (_database.Exists(Cms.Core.Constants.DataTypes.Upload)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.Upload, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, DbType = "Nvarchar" }); + } + InsertDataTypeDto(Cms.Core.Constants.DataTypes.LabelString, Cms.Core.Constants.PropertyEditors.Aliases.Label, "Nvarchar", "{\"umbracoDataValueType\":\"STRING\"}"); InsertDataTypeDto(Cms.Core.Constants.DataTypes.LabelInt, Cms.Core.Constants.PropertyEditors.Aliases.Label, "Integer", "{\"umbracoDataValueType\":\"INT\"}"); InsertDataTypeDto(Cms.Core.Constants.DataTypes.LabelBigint, Cms.Core.Constants.PropertyEditors.Aliases.Label, "Nvarchar", "{\"umbracoDataValueType\":\"BIGINT\"}"); InsertDataTypeDto(Cms.Core.Constants.DataTypes.LabelDateTime, Cms.Core.Constants.PropertyEditors.Aliases.Label, "Date", "{\"umbracoDataValueType\":\"DATETIME\"}"); InsertDataTypeDto(Cms.Core.Constants.DataTypes.LabelDecimal, Cms.Core.Constants.PropertyEditors.Aliases.Label, "Decimal", "{\"umbracoDataValueType\":\"DECIMAL\"}"); InsertDataTypeDto(Cms.Core.Constants.DataTypes.LabelTime, Cms.Core.Constants.PropertyEditors.Aliases.Label, "Date", "{\"umbracoDataValueType\":\"TIME\"}"); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.DateTime, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.DateTime, DbType = "Date" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -37, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.ColorPicker, DbType = "Nvarchar" }); + + if (_database.Exists(Cms.Core.Constants.DataTypes.DateTime)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.DateTime, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.DateTime, DbType = "Date" }); + } + + if (_database.Exists(-37)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -37, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.ColorPicker, DbType = "Nvarchar" }); + } + InsertDataTypeDto(Cms.Core.Constants.DataTypes.DropDownSingle, Cms.Core.Constants.PropertyEditors.Aliases.DropDownListFlexible, "Nvarchar", "{\"multiple\":false}"); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -40, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.RadioButtonList, DbType = "Nvarchar" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -41, EditorAlias = "Umbraco.DateTime", DbType = "Date", Configuration = "{\"format\":\"YYYY-MM-DD\"}" }); + + if (_database.Exists(-40)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -40, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.RadioButtonList, DbType = "Nvarchar" }); + } + + if (_database.Exists(-41)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -41, EditorAlias = "Umbraco.DateTime", DbType = "Date", Configuration = "{\"format\":\"YYYY-MM-DD\"}" }); + } + InsertDataTypeDto(Cms.Core.Constants.DataTypes.DropDownMultiple, Cms.Core.Constants.PropertyEditors.Aliases.DropDownListFlexible, "Nvarchar", "{\"multiple\":true}"); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -43, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.CheckBoxList, DbType = "Nvarchar" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.Tags, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.Tags, DbType = "Ntext", - Configuration = "{\"group\":\"default\", \"storageType\":\"Json\"}" - }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.ImageCropper, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.ImageCropper, DbType = "Ntext" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.DefaultContentListView, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.ListView, DbType = "Nvarchar", - Configuration = "{\"pageSize\":100, \"orderBy\":\"updateDate\", \"orderDirection\":\"desc\", \"layouts\":" + layouts + ", \"includeProperties\":[{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1},{\"alias\":\"owner\",\"header\":\"Updated by\",\"isSystem\":1}]}" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.DefaultMediaListView, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.ListView, DbType = "Nvarchar", - Configuration = "{\"pageSize\":100, \"orderBy\":\"updateDate\", \"orderDirection\":\"desc\", \"layouts\":" + layouts + ", \"includeProperties\":[{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1},{\"alias\":\"owner\",\"header\":\"Updated by\",\"isSystem\":1}]}" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.DefaultMembersListView, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.ListView, DbType = "Nvarchar", - Configuration = "{\"pageSize\":10, \"orderBy\":\"username\", \"orderDirection\":\"asc\", \"includeProperties\":[{\"alias\":\"username\",\"isSystem\":1},{\"alias\":\"email\",\"isSystem\":1},{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1}]}" }); - //New UDI pickers with newer Ids - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1046, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.ContentPicker, DbType = "Nvarchar" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1047, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MemberPicker, DbType = "Nvarchar" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1048, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker, DbType = "Ntext" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1049, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker, DbType = "Ntext", - Configuration = "{\"multiPicker\":1}" }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1050, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MultiUrlPicker, DbType = "Ntext" }); - - - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + if (_database.Exists(-43)) { - NodeId = Cms.Core.Constants.DataTypes.UploadVideo, - EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, - DbType = "Nvarchar", - Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"mp4\"}, {\"id\":1, \"value\":\"webm\"}, {\"id\":2, \"value\":\"ogv\"}]}" - }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -43, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.CheckBoxList, DbType = "Nvarchar" }); + } - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + if (_database.Exists(Cms.Core.Constants.DataTypes.Tags)) { - NodeId = Cms.Core.Constants.DataTypes.UploadAudio, - EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, - DbType = "Nvarchar", - Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"mp3\"}, {\"id\":1, \"value\":\"weba\"}, {\"id\":2, \"value\":\"oga\"}, {\"id\":3, \"value\":\"opus\"}]}" - }); + _database.Insert( + Cms.Core.Constants.DatabaseSchema.Tables.DataType, + "pk", + false, + new DataTypeDto + { + NodeId = Cms.Core.Constants.DataTypes.Tags, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.Tags, + DbType = "Ntext", + Configuration = "{\"group\":\"default\", \"storageType\":\"Json\"}" + }); + } - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + if (_database.Exists(Cms.Core.Constants.DataTypes.ImageCropper)) { - NodeId = Cms.Core.Constants.DataTypes.UploadArticle, - EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, - DbType = "Nvarchar", - Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"pdf\"}, {\"id\":1, \"value\":\"docx\"}, {\"id\":2, \"value\":\"doc\"}]}" - }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = Cms.Core.Constants.DataTypes.ImageCropper, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.ImageCropper, DbType = "Ntext" }); + } - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + if (_database.Exists(Cms.Core.Constants.DataTypes.DefaultContentListView)) { - NodeId = Cms.Core.Constants.DataTypes.UploadVectorGraphics, - EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, - DbType = "Nvarchar", - Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"svg\"}]}" - }); + _database.Insert( + Cms.Core.Constants.DatabaseSchema.Tables.DataType, + "pk", + false, + new DataTypeDto + { + NodeId = Cms.Core.Constants.DataTypes.DefaultContentListView, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.ListView, + DbType = "Nvarchar", + Configuration = "{\"pageSize\":100, \"orderBy\":\"updateDate\", \"orderDirection\":\"desc\", \"layouts\":" + layouts + ", \"includeProperties\":[{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1},{\"alias\":\"owner\",\"header\":\"Updated by\",\"isSystem\":1}]}" + }); + } - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { - NodeId = 1051, - EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker3, - DbType = "Ntext", - Configuration = "{\"multiple\": false, \"validationLimit\":{\"min\":0,\"max\":1}}" - }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { - NodeId = 1052, - EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker3, - DbType = "Ntext", - Configuration = "{\"multiple\": true}" - }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { - NodeId = 1053, - EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker3, - DbType = "Ntext", - Configuration = "{\"filter\":\"" + Cms.Core.Constants.Conventions.MediaTypes.Image + "\", \"multiple\": false, \"validationLimit\":{\"min\":0,\"max\":1}}" - }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { - NodeId = 1054, - EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker3, - DbType = "Ntext", - Configuration = "{\"filter\":\"" + Cms.Core.Constants.Conventions.MediaTypes.Image + "\", \"multiple\": true}" - }); + if (_database.Exists(Cms.Core.Constants.DataTypes.DefaultMediaListView)) + { + _database.Insert( + Cms.Core.Constants.DatabaseSchema.Tables.DataType, + "pk", + false, + new DataTypeDto + { + NodeId = Cms.Core.Constants.DataTypes.DefaultMediaListView, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.ListView, + DbType = "Nvarchar", + Configuration = "{\"pageSize\":100, \"orderBy\":\"updateDate\", \"orderDirection\":\"desc\", \"layouts\":" + layouts + ", \"includeProperties\":[{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1},{\"alias\":\"owner\",\"header\":\"Updated by\",\"isSystem\":1}]}" + }); + } + + if (_database.Exists(Cms.Core.Constants.DataTypes.DefaultMembersListView)) + { + _database.Insert( + Cms.Core.Constants.DatabaseSchema.Tables.DataType, + "pk", + false, + new DataTypeDto + { + NodeId = Cms.Core.Constants.DataTypes.DefaultMembersListView, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.ListView, + DbType = "Nvarchar", + Configuration = "{\"pageSize\":10, \"orderBy\":\"username\", \"orderDirection\":\"asc\", \"includeProperties\":[{\"alias\":\"username\",\"isSystem\":1},{\"alias\":\"email\",\"isSystem\":1},{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1}]}" + }); + } + + // New UDI pickers with newer Ids + if (_database.Exists(1046)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1046, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.ContentPicker, DbType = "Nvarchar" }); + } + + if (_database.Exists(1047)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1047, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MemberPicker, DbType = "Nvarchar" }); + } + + if (_database.Exists(1048)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1048, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MemberPicker, DbType = "Ntext" }); + } + + if (_database.Exists(1049)) + { + _database.Insert( + Cms.Core.Constants.DatabaseSchema.Tables.DataType, + "pk", + false, + new DataTypeDto + { + NodeId = 1049, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker, + DbType = "Ntext", + Configuration = "{\"multiPicker\":1}" + }); + } + + if (_database.Exists(1050)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1050, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MultiUrlPicker, DbType = "Ntext" }); + } + + if (_database.Exists(Cms.Core.Constants.DataTypes.UploadVideo)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = Cms.Core.Constants.DataTypes.UploadVideo, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, + DbType = "Nvarchar", + Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"mp4\"}, {\"id\":1, \"value\":\"webm\"}, {\"id\":2, \"value\":\"ogv\"}]}" + }); + } + + if (_database.Exists(Cms.Core.Constants.DataTypes.UploadAudio)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = Cms.Core.Constants.DataTypes.UploadAudio, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, + DbType = "Nvarchar", + Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"mp3\"}, {\"id\":1, \"value\":\"weba\"}, {\"id\":2, \"value\":\"oga\"}, {\"id\":3, \"value\":\"opus\"}]}" + }); + } + + if (_database.Exists(Cms.Core.Constants.DataTypes.UploadArticle)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = Cms.Core.Constants.DataTypes.UploadArticle, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, + DbType = "Nvarchar", + Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"pdf\"}, {\"id\":1, \"value\":\"docx\"}, {\"id\":2, \"value\":\"doc\"}]}" + }); + } + + if (_database.Exists(Cms.Core.Constants.DataTypes.UploadVectorGraphics)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = Cms.Core.Constants.DataTypes.UploadVectorGraphics, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, + DbType = "Nvarchar", + Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"svg\"}]}" + }); + } + + if (_database.Exists(1051)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = 1051, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker3, + DbType = "Ntext", + Configuration = "{\"multiple\": false, \"validationLimit\":{\"min\":0,\"max\":1}}" + }); + } + + if (_database.Exists(1052)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = 1052, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker3, + DbType = "Ntext", + Configuration = "{\"multiple\": true}" + }); + } + + if (_database.Exists(1053)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = 1053, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker3, + DbType = "Ntext", + Configuration = "{\"filter\":\"" + Cms.Core.Constants.Conventions.MediaTypes.Image + "\", \"multiple\": false, \"validationLimit\":{\"min\":0,\"max\":1}}" + }); + } + + if (_database.Exists(1054)) + { + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = 1054, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker3, + DbType = "Ntext", + Configuration = "{\"filter\":\"" + Cms.Core.Constants.Conventions.MediaTypes.Image + "\", \"multiple\": true}" + }); + } } private void CreateRelationTypeData() { - var relationType = new RelationTypeDto { Id = 1, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, ChildObjectType = Cms.Core.Constants.ObjectTypes.Document, ParentObjectType = Cms.Core.Constants.ObjectTypes.Document, Dual = true, Name = Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyName, IsDependency = false}; - relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType); - relationType = new RelationTypeDto { Id = 2, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias, ChildObjectType = Cms.Core.Constants.ObjectTypes.Document, ParentObjectType = Cms.Core.Constants.ObjectTypes.Document, Dual = false, Name = Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName, IsDependency = false }; - relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType); - relationType = new RelationTypeDto { Id = 3, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias, ChildObjectType = Cms.Core.Constants.ObjectTypes.Media, ParentObjectType = Cms.Core.Constants.ObjectTypes.Media, Dual = false, Name = Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName, IsDependency = false }; - relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType); + CreateRelationTypeData(1, Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyName, Cms.Core.Constants.ObjectTypes.Document, Cms.Core.Constants.ObjectTypes.Document, true, false); + CreateRelationTypeData(2, Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias, Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName, Cms.Core.Constants.ObjectTypes.Document, Cms.Core.Constants.ObjectTypes.Document, false, false); + CreateRelationTypeData(3, Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias, Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName, Cms.Core.Constants.ObjectTypes.Media, Cms.Core.Constants.ObjectTypes.Media, false, false); + CreateRelationTypeData(4, Cms.Core.Constants.Conventions.RelationTypes.RelatedMediaAlias, Cms.Core.Constants.Conventions.RelationTypes.RelatedMediaName, null, null, false, true); + CreateRelationTypeData(5, Cms.Core.Constants.Conventions.RelationTypes.RelatedDocumentAlias, Cms.Core.Constants.Conventions.RelationTypes.RelatedDocumentName, null, null, false, true); + } - relationType = new RelationTypeDto { Id = 4, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelatedMediaAlias, ChildObjectType = null, ParentObjectType = null, Dual = false, Name = Cms.Core.Constants.Conventions.RelationTypes.RelatedMediaName, IsDependency = true }; + private void CreateRelationTypeData(int id, string alias, string name, Guid? parentObjectType, Guid? childObjectType, bool dual, bool isDependency) + { + var relationType = new RelationTypeDto { Id = id, Alias = alias, ChildObjectType = childObjectType, ParentObjectType = parentObjectType, Dual = dual, Name = name, IsDependency = isDependency }; relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType); - relationType = new RelationTypeDto { Id = 5, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelatedDocumentAlias, ChildObjectType = null, ParentObjectType = null, Dual = false, Name = Cms.Core.Constants.Conventions.RelationTypes.RelatedDocumentName, IsDependency = true }; - relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType); } @@ -445,8 +994,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install private void CreateKeyValueData() { - // on install, initialize the umbraco migration plan with the final state - + // On install, initialize the umbraco migration plan with the final state. var upgrader = new Upgrader(new UmbracoPlan(_umbracoVersion)); var stateValueKey = upgrader.StateValueKey; var finalState = upgrader.Plan.FinalState; @@ -456,14 +1004,51 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install private void CreateLogViewerQueryData() { - // var defaultData = MigrateLogViewerQueriesFromFileToDb.DefaultLogQueries.ToArray(); - // - // for (int i = 0; i < defaultData.Length; i++) - // { - // var dto = defaultData[i]; - // dto.Id = i+1; - // _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.LogViewerQuery, "id", false, dto); - // } + LogViewerQueryDto[] defaultData = MigrateLogViewerQueriesFromFileToDb.DefaultLogQueries.ToArray(); + + for (int i = 0; i < defaultData.Length; i++) + { + LogViewerQueryDto dto = defaultData[i]; + dto.Id = i+1; + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.LogViewerQuery, "id", false, dto); + } + } + + private void ConditionalInsert( + string configKey, + string id, + TDto dto, + string tableName, + string primaryKeyName, + bool autoIncrement = false) + { + var alwaysInsert = _entitiesToAlwaysCreate.ContainsKey(configKey) && + _entitiesToAlwaysCreate[configKey].InvariantContains(id.ToString()); + + InstallDefaultDataSettings installDefaultDataSettings = _installDefaultDataSettings.Get(configKey); + + // If there's no configuration, we assume to create. + if (installDefaultDataSettings == null) + { + alwaysInsert = true; + } + + if (!alwaysInsert && installDefaultDataSettings.InstallData == InstallDefaultDataOption.None) + { + return; + } + + if (!alwaysInsert && installDefaultDataSettings.InstallData == InstallDefaultDataOption.Values && !installDefaultDataSettings.Values.InvariantContains(id)) + { + return; + } + + if (!alwaysInsert && installDefaultDataSettings.InstallData == InstallDefaultDataOption.ExceptValues && installDefaultDataSettings.Values.InvariantContains(id)) + { + return; + } + + _database.Insert(tableName, primaryKeyName, autoIncrement, dto); } } } diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs index 6134d45803..b934b92971 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs @@ -2,9 +2,13 @@ using System; using System.Collections.Generic; using System.Data.SqlTypes; using System.Linq; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using NPoco; using Umbraco.Cms.Core.Configuration; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Infrastructure.Migrations.Notifications; @@ -12,6 +16,7 @@ using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; using ColumnInfo = Umbraco.Cms.Infrastructure.Persistence.SqlSyntax.ColumnInfo; @@ -89,19 +94,33 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; private readonly IUmbracoVersion _umbracoVersion; + private readonly IOptionsMonitor _defaultDataCreationSettings; + [Obsolete("Please use constructor taking all parameters. Scheduled for removal in V11.")] public DatabaseSchemaCreator( IUmbracoDatabase? database, ILogger logger, ILoggerFactory loggerFactory, IUmbracoVersion umbracoVersion, IEventAggregator eventAggregator) + : this (database, logger, loggerFactory, umbracoVersion, eventAggregator, StaticServiceProvider.Instance.GetRequiredService>()) + { + } + + public DatabaseSchemaCreator( + IUmbracoDatabase database, + ILogger logger, + ILoggerFactory loggerFactory, + IUmbracoVersion umbracoVersion, + IEventAggregator eventAggregator, + IOptionsMonitor defaultDataCreationSettings) { _database = database ?? throw new ArgumentNullException(nameof(database)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); _umbracoVersion = umbracoVersion ?? throw new ArgumentNullException(nameof(umbracoVersion)); _eventAggregator = eventAggregator; + _defaultDataCreationSettings = defaultDataCreationSettings; if (_database?.SqlContext?.SqlSyntax == null) { @@ -158,7 +177,10 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install if (creatingNotification.Cancel == false) { - var dataCreation = new DatabaseDataCreator(_database, _loggerFactory.CreateLogger(), _umbracoVersion); + var dataCreation = new DatabaseDataCreator( + _database, _loggerFactory.CreateLogger(), + _umbracoVersion, + _defaultDataCreationSettings); foreach (Type table in OrderedTables) { CreateTable(false, table, dataCreation); @@ -432,9 +454,14 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install where T : new() { Type tableType = typeof(T); - CreateTable(overwrite, tableType, - new DatabaseDataCreator(_database, _loggerFactory.CreateLogger(), - _umbracoVersion)); + CreateTable( + overwrite, + tableType, + new DatabaseDataCreator( + _database, + _loggerFactory.CreateLogger(), + _umbracoVersion, + _defaultDataCreationSettings)); } /// diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreatorFactory.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreatorFactory.cs index 2c9242f3d5..d4d0507c0a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreatorFactory.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreatorFactory.cs @@ -1,7 +1,12 @@ +using System; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Infrastructure.Persistence; +using Umbraco.Cms.Web.Common.DependencyInjection; namespace Umbraco.Cms.Infrastructure.Migrations.Install { @@ -14,22 +19,35 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install private readonly ILoggerFactory _loggerFactory; private readonly IUmbracoVersion _umbracoVersion; private readonly IEventAggregator _eventAggregator; + private readonly IOptionsMonitor _installDefaultDataSettings; + [Obsolete("Please use the constructor taking all parameters. Scheduled for removal in V11.")] public DatabaseSchemaCreatorFactory( ILogger logger, ILoggerFactory loggerFactory, IUmbracoVersion umbracoVersion, IEventAggregator eventAggregator) + : this(logger, loggerFactory, umbracoVersion, eventAggregator, StaticServiceProvider.Instance.GetRequiredService>()) + { + } + + public DatabaseSchemaCreatorFactory( + ILogger logger, + ILoggerFactory loggerFactory, + IUmbracoVersion umbracoVersion, + IEventAggregator eventAggregator, + IOptionsMonitor installDefaultDataSettings) { _logger = logger; _loggerFactory = loggerFactory; _umbracoVersion = umbracoVersion; _eventAggregator = eventAggregator; + _installDefaultDataSettings = installDefaultDataSettings; } public DatabaseSchemaCreator Create(IUmbracoDatabase? database) { - return new DatabaseSchemaCreator(database, _logger, _loggerFactory, _umbracoVersion, _eventAggregator); + return new DatabaseSchemaCreator(database, _logger, _loggerFactory, _umbracoVersion, _eventAggregator, _installDefaultDataSettings); } } } diff --git a/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs b/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs index f7de532a83..b9bb5e508f 100644 --- a/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs +++ b/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs @@ -650,6 +650,12 @@ namespace Umbraco.Extensions return sql; } + public static Sql SelectDistinct(this Sql sql, params object[] columns) + { + sql.Append("SELECT DISTINCT " + string.Join(", ", columns)); + return sql; + } + //this.Append("SELECT " + string.Join(", ", columns), new object[0]); /// diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MacroRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MacroRepository.cs index 2bddc4a1ea..b8a29f65c8 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MacroRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MacroRepository.cs @@ -18,14 +18,16 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { - internal class MacroRepository : EntityRepositoryBase, IMacroRepository + internal class MacroRepository : EntityRepositoryBase, IMacroWithAliasRepository { private readonly IShortStringHelper _shortStringHelper; + private readonly IRepositoryCachePolicy _macroByAliasCachePolicy; public MacroRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IShortStringHelper shortStringHelper) : base(scopeAccessor, cache, logger) { _shortStringHelper = shortStringHelper; + _macroByAliasCachePolicy = new DefaultRepositoryCachePolicy(GlobalIsolatedCache, ScopeAccessor, DefaultOptions); } protected override IMacro? PerformGet(int id) @@ -68,6 +70,38 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return Get(id) != null; } + public IMacro GetByAlias(string alias) + { + return _macroByAliasCachePolicy.Get(alias, PerformGetByAlias, PerformGetAllByAlias); + } + + public IEnumerable GetAllByAlias(string[] aliases) + { + if (aliases.Any() is false) + { + return base.GetMany(); + } + + return _macroByAliasCachePolicy.GetAll(aliases, PerformGetAllByAlias); + } + + private IMacro PerformGetByAlias(string alias) + { + var query = Query().Where(x => x.Alias.Equals(alias)); + return PerformGetByQuery(query).FirstOrDefault(); + } + + private IEnumerable PerformGetAllByAlias(params string[] aliases) + { + if (aliases.Any() is false) + { + return base.GetMany(); + } + + var query = Query().Where(x => aliases.Contains(x.Alias)); + return PerformGetByQuery(query); + } + protected override IEnumerable PerformGetAll(params int[]? ids) { return ids?.Length > 0 ? ids.Select(Get).WhereNotNull() : GetAllNoIds(); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs index a2686464f7..e49ccbdf77 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs @@ -60,14 +60,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement var urlHash = url.GenerateHash(); Sql sql = GetBaseQuery(false) .Where(x => x.Url == url && x.UrlHash == urlHash && - (x.Culture == culture.ToLower() || x.Culture == string.Empty)) + (x.Culture == culture.ToLower() || x.Culture == null || x.Culture == string.Empty)) .OrderByDescending(x => x.CreateDateUtc); List dtos = Database.Fetch(sql); RedirectUrlDto? dto = dtos.FirstOrDefault(f => f.Culture == culture.ToLower()); if (dto == null) { - dto = dtos.FirstOrDefault(f => f.Culture == string.Empty); + dto = dtos.FirstOrDefault(f => string.IsNullOrWhiteSpace(f.Culture)); } return dto == null ? null : Map(dto); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationRepository.cs index 6aeb967361..d7e65adaf4 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationRepository.cs @@ -202,22 +202,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement }); } - public IEnumerable GetPagedParentEntitiesByChildIds(int[] childIds, long pageIndex, int pageSize, out long totalRecords, int[] relationTypes, params Guid[] entityTypes) - { - return _entityRepository.GetPagedResultsByQuery(Query(), entityTypes, pageIndex, pageSize, out totalRecords, null, null, sql => - { - SqlJoinRelations(sql); - - sql.WhereIn(rel => rel.ChildId, childIds); - sql.WhereAny(s => s.WhereIn(rel => rel.ParentId, childIds), s => s.WhereNotIn(node => node.NodeId, childIds)); - - if (relationTypes != null && relationTypes.Any()) - { - sql.WhereIn(rel => rel.RelationType, relationTypes); - } - }); - } - public IEnumerable GetPagedChildEntitiesByParentId(int parentId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes) { return GetPagedChildEntitiesByParentId(parentId, pageIndex, pageSize, out totalRecords, new int[0], entityTypes); @@ -245,21 +229,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement }); } - public IEnumerable GetPagedEntitiesForItemsInRelation(int[] itemIds, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes) - { - return _entityRepository.GetPagedResultsByQuery(Query(), entityTypes, pageIndex, pageSize, out totalRecords, null, null, sql => - { - SqlJoinRelations(sql); - - sql.WhereIn(rel => rel.ChildId, itemIds); - sql.Where((rel, node) => rel.ChildId == node.NodeId); - sql.Where(type => type.IsDependency); - }); - } - - - - public void Save(IEnumerable relations) { foreach (var hasIdentityGroup in relations.GroupBy(r => r.HasIdentity)) @@ -483,8 +452,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement [Column(Name = "contentTypeName")] public string? ChildContentTypeName { get; set; } - - [Column(Name = "relationTypeName")] public string? RelationTypeName { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs index ae1939973f..c50728379e 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using NPoco; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Persistence; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Infrastructure.Persistence.Dtos; @@ -21,10 +20,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement _scopeAccessor = scopeAccessor; } - public IEnumerable GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize, - bool filterMustBeIsDependency, out long totalRecords) + /// + /// Gets a page of items used in any kind of relation from selected integer ids. + /// + public IEnumerable GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords) { - var sql = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql().Select( + var sql = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql().SelectDistinct( "[pn].[id] as nodeId", "[pn].[uniqueId] as nodeKey", "[pn].[text] as nodeName", @@ -37,12 +38,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement "[umbracoRelationType].[isDependency] as relationTypeIsDependency", "[umbracoRelationType].[dual] as relationTypeIsBidirectional") .From("r") - .InnerJoin("umbracoRelationType").On((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight:"umbracoRelationType") - .InnerJoin("cn").On((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId )), aliasLeft: "r", aliasRight:"cn", aliasOther: "umbracoRelationType" ) - .InnerJoin("pn").On((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight:"pn", aliasOther:"cn" ) - .LeftJoin("c").On((left, right) => left.NodeId == right.NodeId, aliasLeft:"pn", aliasRight:"c") - .LeftJoin("ct").On((left, right) => left.ContentTypeId == right.NodeId, aliasLeft:"c", aliasRight:"ct") - .LeftJoin("ctn").On((left, right) => left.NodeId == right.NodeId, aliasLeft:"ct", aliasRight:"ctn"); + .InnerJoin("umbracoRelationType").On((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight: "umbracoRelationType") + .InnerJoin("cn").On((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId)), aliasLeft: "r", aliasRight: "cn", aliasOther: "umbracoRelationType") + .InnerJoin("pn").On((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight: "pn", aliasOther: "cn") + .LeftJoin("c").On((left, right) => left.NodeId == right.NodeId, aliasLeft: "pn", aliasRight: "c") + .LeftJoin("ct").On((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c", aliasRight: "ct") + .LeftJoin("ctn").On((left, right) => left.NodeId == right.NodeId, aliasLeft: "ct", aliasRight: "ctn"); if (ids.Any()) { @@ -57,14 +58,16 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement // Ordering is required for paging sql = sql?.OrderBy(x => x.Alias); - var pagedResult = _scopeAccessor.AmbientScope?.Database.Page(pageIndex + 1, pageSize, sql); - totalRecords = Convert.ToInt32(pagedResult?.TotalItems); + var pagedResult = _scopeAccessor.AmbientScope.Database.Page(pageIndex + 1, pageSize, sql); + totalRecords = pagedResult.TotalItems; return pagedResult?.Items.Select(MapDtoToEntity) ?? Enumerable.Empty(); } - public IEnumerable GetPagedDescendantsInReferences(int parentId, long pageIndex, int pageSize, bool filterMustBeIsDependency, - out long totalRecords) + /// + /// Gets a page of the descending items that have any references, given a parent id. + /// + public IEnumerable GetPagedDescendantsInReferences(int parentId, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords) { var syntax = _scopeAccessor.AmbientScope?.Database.SqlContext.SqlSyntax; @@ -74,7 +77,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement .From("node") .Where(x => x.NodeId == parentId, "node"); - // Gets the descendants of the parent node Sql? subQuery = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql() .Select(x => x.NodeId) @@ -82,7 +84,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement .WhereLike(x => x.Path, subsubQuery); // Get all relations where parent is in the sub query - var sql = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql().Select( + var sql = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql().SelectDistinct( "[pn].[id] as nodeId", "[pn].[uniqueId] as nodeKey", "[pn].[text] as nodeName", @@ -95,17 +97,19 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement "[umbracoRelationType].[isDependency] as relationTypeIsDependency", "[umbracoRelationType].[dual] as relationTypeIsBidirectional") .From("r") - .InnerJoin("umbracoRelationType").On((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight:"umbracoRelationType") - .InnerJoin("cn").On((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId )), aliasLeft: "r", aliasRight:"cn", aliasOther: "umbracoRelationType" ) - .InnerJoin("pn").On((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight:"pn", aliasOther:"cn" ) - .LeftJoin("c").On((left, right) => left.NodeId == right.NodeId, aliasLeft:"pn", aliasRight:"c") - .LeftJoin("ct").On((left, right) => left.ContentTypeId == right.NodeId, aliasLeft:"c", aliasRight:"ct") - .LeftJoin("ctn").On((left, right) => left.NodeId == right.NodeId, aliasLeft:"ct", aliasRight:"ctn") + .InnerJoin("umbracoRelationType").On((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight: "umbracoRelationType") + .InnerJoin("cn").On((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId)), aliasLeft: "r", aliasRight: "cn", aliasOther: "umbracoRelationType") + .InnerJoin("pn").On((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight: "pn", aliasOther: "cn") + .LeftJoin("c").On((left, right) => left.NodeId == right.NodeId, aliasLeft: "pn", aliasRight: "c") + .LeftJoin("ct").On((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c", aliasRight: "ct") + .LeftJoin("ctn").On((left, right) => left.NodeId == right.NodeId, aliasLeft: "ct", aliasRight: "ctn") .WhereIn((System.Linq.Expressions.Expression>)(x => x.NodeId), subQuery, "pn"); + if (filterMustBeIsDependency) { sql = sql?.Where(rt => rt.IsDependency, "umbracoRelationType"); } + // Ordering is required for paging sql = sql?.OrderBy(x => x.Alias); @@ -115,9 +119,13 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return pagedResult?.Items.Select(MapDtoToEntity) ?? Enumerable.Empty(); } - public IEnumerable GetPagedRelationsForItems(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords) + /// + /// Gets a page of items which are in relation with the current item. + /// Basically, shows the items which depend on the current item. + /// + public IEnumerable GetPagedRelationsForItem(int id, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords) { - var sql = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql().Select( + var sql = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql().SelectDistinct( "[cn].[id] as nodeId", "[cn].[uniqueId] as nodeKey", "[cn].[text] as nodeName", @@ -130,17 +138,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement "[umbracoRelationType].[isDependency] as relationTypeIsDependency", "[umbracoRelationType].[dual] as relationTypeIsBidirectional") .From("r") - .InnerJoin("umbracoRelationType").On((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight:"umbracoRelationType") - .InnerJoin("cn").On((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId )), aliasLeft: "r", aliasRight:"cn", aliasOther: "umbracoRelationType" ) - .InnerJoin("pn").On((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight:"pn", aliasOther:"cn" ) - .LeftJoin("c").On((left, right) => left.NodeId == right.NodeId, aliasLeft:"cn", aliasRight:"c") - .LeftJoin("ct").On((left, right) => left.ContentTypeId == right.NodeId, aliasLeft:"c", aliasRight:"ct") - .LeftJoin("ctn").On((left, right) => left.NodeId == right.NodeId, aliasLeft:"ct", aliasRight:"ctn"); - - if (ids.Any()) - { - sql = sql?.Where(x => ids.Contains(x.NodeId), "pn"); - } + .InnerJoin("umbracoRelationType").On((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight: "umbracoRelationType") + .InnerJoin("cn").On((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId)), aliasLeft: "r", aliasRight: "cn", aliasOther: "umbracoRelationType") + .InnerJoin("pn").On((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight: "pn", aliasOther: "cn") + .LeftJoin("c").On((left, right) => left.NodeId == right.NodeId, aliasLeft: "cn", aliasRight: "c") + .LeftJoin("ct").On((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c", aliasRight: "ct") + .LeftJoin("ctn").On((left, right) => left.NodeId == right.NodeId, aliasLeft: "ct", aliasRight: "ctn") + .Where(x => x.NodeId == id, "pn") + .Where(x => x.ChildId == id || x.ParentId == id, "r"); // This last Where is purely to help SqlServer make a smarter query plan. More info https://github.com/umbraco/Umbraco-CMS/issues/12190 if (filterMustBeIsDependency) { @@ -158,7 +163,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private RelationItem MapDtoToEntity(RelationItemDto dto) { - var type = ObjectTypes.GetUdiType(dto.ChildNodeObjectType); return new RelationItem() { NodeId = dto.ChildNodeId, diff --git a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs index cd45b766d2..32d3e47b2d 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; @@ -14,6 +15,8 @@ using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Templates; +using Umbraco.Cms.Infrastructure.Templates; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors @@ -37,6 +40,7 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly RichTextEditorPastedImages _pastedImages; private readonly HtmlLocalLinkParser _localLinkParser; private readonly IImageUrlGenerator _imageUrlGenerator; + private readonly IHtmlMacroParameterParser _macroParameterParser; public GridPropertyEditor( IDataValueEditorFactory dataValueEditorFactory, @@ -45,7 +49,8 @@ namespace Umbraco.Cms.Core.PropertyEditors RichTextEditorPastedImages pastedImages, HtmlLocalLinkParser localLinkParser, IIOHelper ioHelper, - IImageUrlGenerator imageUrlGenerator) + IImageUrlGenerator imageUrlGenerator, + IHtmlMacroParameterParser macroParameterParser) : base(dataValueEditorFactory) { _backOfficeSecurityAccessor = backOfficeSecurityAccessor; @@ -54,6 +59,20 @@ namespace Umbraco.Cms.Core.PropertyEditors _pastedImages = pastedImages; _localLinkParser = localLinkParser; _imageUrlGenerator = imageUrlGenerator; + _macroParameterParser = macroParameterParser; + } + + [Obsolete("Use the constructor which takes an IHtmlMacroParameterParser instead")] + public GridPropertyEditor( + IDataValueEditorFactory dataValueEditorFactory, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + HtmlImageSourceParser imageSourceParser, + RichTextEditorPastedImages pastedImages, + HtmlLocalLinkParser localLinkParser, + IIOHelper ioHelper, + IImageUrlGenerator imageUrlGenerator) + : this (dataValueEditorFactory, backOfficeSecurityAccessor, imageSourceParser, pastedImages, localLinkParser, ioHelper, imageUrlGenerator, StaticServiceProvider.Instance.GetRequiredService()) + { } public override IPropertyIndexValueFactory PropertyIndexValueFactory => new GridPropertyIndexValueFactory(); @@ -74,6 +93,7 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly RichTextPropertyEditor.RichTextPropertyValueEditor _richTextPropertyValueEditor; private readonly MediaPickerPropertyEditor.MediaPickerPropertyValueEditor _mediaPickerPropertyValueEditor; private readonly IImageUrlGenerator _imageUrlGenerator; + private readonly IHtmlMacroParameterParser _macroParameterParser; public GridPropertyValueEditor( IDataValueEditorFactory dataValueEditorFactory, @@ -85,7 +105,8 @@ namespace Umbraco.Cms.Core.PropertyEditors IShortStringHelper shortStringHelper, IImageUrlGenerator imageUrlGenerator, IJsonSerializer jsonSerializer, - IIOHelper ioHelper) + IIOHelper ioHelper, + IHtmlMacroParameterParser macroParameterParser) : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { _backOfficeSecurityAccessor = backOfficeSecurityAccessor; @@ -96,6 +117,25 @@ namespace Umbraco.Cms.Core.PropertyEditors _mediaPickerPropertyValueEditor = dataValueEditorFactory.Create(attribute); _imageUrlGenerator = imageUrlGenerator; + _macroParameterParser = macroParameterParser; + } + + [Obsolete("Use the constructor which takes an IHtmlMacroParameterParser instead")] + public GridPropertyValueEditor( + IDataValueEditorFactory dataValueEditorFactory, + DataEditorAttribute attribute, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + ILocalizedTextService localizedTextService, + HtmlImageSourceParser imageSourceParser, + RichTextEditorPastedImages pastedImages, + IShortStringHelper shortStringHelper, + IImageUrlGenerator imageUrlGenerator, + IJsonSerializer jsonSerializer, + IIOHelper ioHelper) + : this (dataValueEditorFactory, attribute, backOfficeSecurityAccessor, localizedTextService, + imageSourceParser, pastedImages, shortStringHelper, imageUrlGenerator, jsonSerializer, ioHelper, + StaticServiceProvider.Instance.GetRequiredService()) + { } /// @@ -106,7 +146,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// /// - public override object? FromEditor(ContentPropertyData editorValue, object? currentValue) + public override object FromEditor(ContentPropertyData editorValue, object currentValue) { if (editorValue.Value == null) return null; @@ -120,7 +160,7 @@ namespace Umbraco.Cms.Core.PropertyEditors var mediaParent = config?.MediaParentId; var mediaParentId = mediaParent?.Guid ?? Guid.Empty; - var grid = DeserializeGridValue(rawJson!, out var rtes, out _); + var grid = DeserializeGridValue(rawJson!, out var rtes, out _, out _); var userId = _backOfficeSecurityAccessor?.BackOfficeSecurity?.CurrentUser?.Id ?? Constants.Security.SuperUserId; @@ -160,7 +200,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (val.IsNullOrWhiteSpace()) return string.Empty; - var grid = DeserializeGridValue(val!, out var rtes, out _); + var grid = DeserializeGridValue(val!, out var rtes, out _, out _); if (rtes is null) { @@ -182,7 +222,7 @@ namespace Umbraco.Cms.Core.PropertyEditors return grid; } - private GridValue? DeserializeGridValue(string rawJson, out IEnumerable? richTextValues, out IEnumerable? mediaValues) + private GridValue? DeserializeGridValue(string rawJson, out IEnumerable? richTextValues, out IEnumerable? mediaValues, out IEnumerable macroValues) { var grid = JsonConvert.DeserializeObject(rawJson); @@ -191,6 +231,9 @@ namespace Umbraco.Cms.Core.PropertyEditors richTextValues = controls?.Where(x => x.Editor.Alias.ToLowerInvariant() == "rte"); mediaValues = controls?.Where(x => x.Editor.Alias.ToLowerInvariant() == "media"); + // Find all the macros + macroValues = controls.Where(x => x.Editor.Alias.ToLowerInvariant() == "macro"); + return grid; } @@ -208,7 +251,7 @@ namespace Umbraco.Cms.Core.PropertyEditors yield break; } - DeserializeGridValue(rawJson!, out IEnumerable? richTextEditorValues, out IEnumerable? mediaValues); + DeserializeGridValue(rawJson, out var richTextEditorValues, out var mediaValues, out var macroValues); if (richTextEditorValues is not null) { @@ -219,14 +262,12 @@ namespace Umbraco.Cms.Core.PropertyEditors } } - if (mediaValues is not null) - { - foreach (UmbracoEntityReference umbracoEntityReference in mediaValues.Where(x => x.Value.HasValues) - .SelectMany(x => _mediaPickerPropertyValueEditor.GetReferences(x.Value["udi"]))) - { - yield return umbracoEntityReference; - } - } + foreach (var umbracoEntityReference in mediaValues.Where(x => x.Value.HasValues) + .SelectMany(x => _mediaPickerPropertyValueEditor.GetReferences(x.Value["udi"]))) + yield return umbracoEntityReference; + + foreach (var umbracoEntityReference in _macroParameterParser.FindUmbracoEntityReferencesFromGridControlMacros(macroValues)) + yield return umbracoEntityReference; } } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs index acab7d5844..4c88fd10ca 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs @@ -3,8 +3,7 @@ using System; using System.Collections.Generic; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Hosting; +using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; @@ -16,6 +15,8 @@ using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Templates; using Umbraco.Cms.Infrastructure.Examine; using Umbraco.Cms.Infrastructure.Macros; +using Umbraco.Cms.Infrastructure.Templates; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors @@ -36,12 +37,13 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; private readonly HtmlImageSourceParser _imageSourceParser; private readonly HtmlLocalLinkParser _localLinkParser; + private readonly IHtmlMacroParameterParser _macroParameterParser; private readonly RichTextEditorPastedImages _pastedImages; private readonly IIOHelper _ioHelper; private readonly IImageUrlGenerator _imageUrlGenerator; /// - /// The constructor will setup the property editor based on the attribute if one is found + /// The constructor will setup the property editor based on the attribute if one is found. /// public RichTextPropertyEditor( IDataValueEditorFactory dataValueEditorFactory, @@ -50,7 +52,8 @@ namespace Umbraco.Cms.Core.PropertyEditors HtmlLocalLinkParser localLinkParser, RichTextEditorPastedImages pastedImages, IIOHelper ioHelper, - IImageUrlGenerator imageUrlGenerator) + IImageUrlGenerator imageUrlGenerator, + IHtmlMacroParameterParser macroParameterParser) : base(dataValueEditorFactory) { _backOfficeSecurityAccessor = backOfficeSecurityAccessor; @@ -59,6 +62,20 @@ namespace Umbraco.Cms.Core.PropertyEditors _pastedImages = pastedImages; _ioHelper = ioHelper; _imageUrlGenerator = imageUrlGenerator; + _macroParameterParser = macroParameterParser; + } + + [Obsolete("Use the constructor which takes an IHtmlMacroParameterParser instead")] + public RichTextPropertyEditor( + IDataValueEditorFactory dataValueEditorFactory, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + HtmlImageSourceParser imageSourceParser, + HtmlLocalLinkParser localLinkParser, + RichTextEditorPastedImages pastedImages, + IIOHelper ioHelper, + IImageUrlGenerator imageUrlGenerator) + : this (dataValueEditorFactory, backOfficeSecurityAccessor, imageSourceParser, localLinkParser, pastedImages, ioHelper, imageUrlGenerator, StaticServiceProvider.Instance.GetRequiredService()) + { } /// @@ -81,6 +98,7 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; private readonly HtmlImageSourceParser _imageSourceParser; private readonly HtmlLocalLinkParser _localLinkParser; + private readonly IHtmlMacroParameterParser _macroParameterParser; private readonly RichTextEditorPastedImages _pastedImages; private readonly IImageUrlGenerator _imageUrlGenerator; private readonly IHtmlSanitizer _htmlSanitizer; @@ -96,7 +114,8 @@ namespace Umbraco.Cms.Core.PropertyEditors IImageUrlGenerator imageUrlGenerator, IJsonSerializer jsonSerializer, IIOHelper ioHelper, - IHtmlSanitizer htmlSanitizer) + IHtmlSanitizer htmlSanitizer, + IHtmlMacroParameterParser macroParameterParser) : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { _backOfficeSecurityAccessor = backOfficeSecurityAccessor; @@ -105,6 +124,26 @@ namespace Umbraco.Cms.Core.PropertyEditors _pastedImages = pastedImages; _imageUrlGenerator = imageUrlGenerator; _htmlSanitizer = htmlSanitizer; + _macroParameterParser = macroParameterParser; + } + + [Obsolete("Use the constructor which takes an HtmlMacroParameterParser instead")] + public RichTextPropertyValueEditor( + DataEditorAttribute attribute, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper, + HtmlImageSourceParser imageSourceParser, + HtmlLocalLinkParser localLinkParser, + RichTextEditorPastedImages pastedImages, + IImageUrlGenerator imageUrlGenerator, + IJsonSerializer jsonSerializer, + IIOHelper ioHelper, + IHtmlSanitizer htmlSanitizer) + : this(attribute, backOfficeSecurityAccessor, localizedTextService, shortStringHelper, imageSourceParser, + localLinkParser, pastedImages, imageUrlGenerator, jsonSerializer, ioHelper, htmlSanitizer, + StaticServiceProvider.Instance.GetRequiredService()) + { } /// @@ -203,6 +242,10 @@ namespace Umbraco.Cms.Core.PropertyEditors } //TODO: Detect Macros too ... but we can save that for a later date, right now need to do media refs + //UPDATE: We are getting the Macros in 'FindUmbracoEntityReferencesFromEmbeddedMacros' - perhaps we just return the macro Udis here too or do they need their own relationAlias? + + foreach (var umbracoEntityReference in _macroParameterParser.FindUmbracoEntityReferencesFromEmbeddedMacros(asString)) + yield return umbracoEntityReference; } } diff --git a/src/Umbraco.Infrastructure/Security/MemberPasswordHasher.cs b/src/Umbraco.Infrastructure/Security/MemberPasswordHasher.cs index 71a48e19c5..720afc546c 100644 --- a/src/Umbraco.Infrastructure/Security/MemberPasswordHasher.cs +++ b/src/Umbraco.Infrastructure/Security/MemberPasswordHasher.cs @@ -71,6 +71,7 @@ namespace Umbraco.Cms.Core.Security return result; } } + // We need to check for clear text passwords from members as the first thing. This was possible in v8 :( else if (IsSuccessfulLegacyPassword(hashedPassword, providedPassword)) { @@ -138,7 +139,7 @@ namespace Umbraco.Cms.Core.Security } var result = LegacyPasswordSecurity.VerifyPassword(Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName, providedPassword, hashedPassword); - return result || LegacyPasswordSecurity.VerifyPassword(Constants.Security.AspNetUmbraco4PasswordHashAlgorithmName, providedPassword, hashedPassword); + return result || LegacyPasswordSecurity.VerifyLegacyHashedPassword(providedPassword, hashedPassword); } private static string DecryptLegacyPassword(string encryptedPassword, string algorithmName, string decryptionKey) diff --git a/src/Umbraco.Infrastructure/Security/UmbracoPasswordHasher.cs b/src/Umbraco.Infrastructure/Security/UmbracoPasswordHasher.cs index b1e13497f7..2847f13dc4 100644 --- a/src/Umbraco.Infrastructure/Security/UmbracoPasswordHasher.cs +++ b/src/Umbraco.Infrastructure/Security/UmbracoPasswordHasher.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Models.Membership; @@ -10,7 +11,6 @@ namespace Umbraco.Cms.Core.Security where TUser: UmbracoIdentityUser { private readonly IJsonSerializer _jsonSerializer; - private readonly PasswordHasher _aspnetV2PasswordHasher = new PasswordHasher(new V2PasswordHasherOptions()); public UmbracoPasswordHasher(LegacyPasswordSecurity legacyPasswordSecurity, IJsonSerializer jsonSerializer) { @@ -43,57 +43,64 @@ namespace Umbraco.Cms.Core.Security { if (user is null) { - throw new System.ArgumentNullException(nameof(user)); + throw new ArgumentNullException(nameof(user)); } - if (!user.PasswordConfig.IsNullOrWhiteSpace()) + try { - // check if the (legacy) password security supports this hash algorith and if so then use it - var deserialized = _jsonSerializer.Deserialize(user.PasswordConfig!); - if (deserialized?.HashAlgorithm is not null && LegacyPasswordSecurity.SupportHashAlgorithm(deserialized.HashAlgorithm)) + // Best case and most likely scenario, a modern hash supported by ASP.Net identity. + PasswordVerificationResult upstreamResult = base.VerifyHashedPassword(user, hashedPassword, providedPassword); + if (upstreamResult != PasswordVerificationResult.Failed) { - var result = LegacyPasswordSecurity.VerifyPassword(deserialized.HashAlgorithm, providedPassword, hashedPassword); - - //We need to special handle this case, apparently v8 still saves the user algorithm as {"hashAlgorithm":"HMACSHA256"}, when using legacy encoding and hasinging. - if (result == false) - { - result = LegacyPasswordSecurity.VerifyLegacyHashedPassword(providedPassword, hashedPassword); - } - - return result - ? PasswordVerificationResult.SuccessRehashNeeded - : PasswordVerificationResult.Failed; - } - - // We will explicitly detect names here - // The default is PBKDF2.ASPNETCORE.V3: - // PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations. - // The underlying class only lets us change 2 things which is the version: options.CompatibilityMode and the iteration count - // The PBKDF2.ASPNETCORE.V2 settings are: - // PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations. - - switch (deserialized?.HashAlgorithm) - { - case Constants.Security.AspNetCoreV3PasswordHashAlgorithmName: - return base.VerifyHashedPassword(user, hashedPassword, providedPassword); - case Constants.Security.AspNetCoreV2PasswordHashAlgorithmName: - var legacyResult = _aspnetV2PasswordHasher.VerifyHashedPassword(user, hashedPassword, providedPassword); - if (legacyResult == PasswordVerificationResult.Success) - return PasswordVerificationResult.SuccessRehashNeeded; - return legacyResult; + return upstreamResult; } } - - // else go the default (v3) - return base.VerifyHashedPassword(user, hashedPassword, providedPassword); - } - - private class V2PasswordHasherOptions : IOptions - { - public PasswordHasherOptions Value => new PasswordHasherOptions + catch (FormatException) { - CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2 - }; + // hash wasn't a valid base64 encoded string, MS concat the salt bytes and hash bytes and base 64 encode both together. + // We however historically base 64 encoded the salt bytes and hash bytes separately then concat the strings so we got 2 sets of padding. + // both salt bytes and hash bytes lengths were not evenly divisible by 3 hence 2 sets of padding. + + // We could check upfront with TryFromBase64String, but not whilst we target netstandard 2.0 + // so might as well just deal with the exception. + } + + // At this point we either have a legacy password or a bad attempt. + + // Check the supported worst case scenario, a "useLegacyEncoding" password - HMACSHA1 but with password used as key so not unique for users sharing same password + // This was the standard for v4. + // Do this first because with useLegacyEncoding the algorithm stored in the database is irrelevant. + if (LegacyPasswordSecurity.VerifyLegacyHashedPassword(providedPassword, hashedPassword)) + { + return PasswordVerificationResult.SuccessRehashNeeded; + } + + // For users we expect to know the historic algorithm. + // NOTE: MemberPasswordHasher subclasses this class to deal with the fact that PasswordConfig wasn't stored. + if (user.PasswordConfig.IsNullOrWhiteSpace()) + { + return PasswordVerificationResult.Failed; + } + + PersistedPasswordSettings deserialized; + try + { + deserialized = _jsonSerializer.Deserialize(user.PasswordConfig); + } + catch + { + return PasswordVerificationResult.Failed; + } + + if (!LegacyPasswordSecurity.SupportHashAlgorithm(deserialized.HashAlgorithm)) + { + return PasswordVerificationResult.Failed; + } + + // Last chance must be HMACSHA256 or SHA1 + return LegacyPasswordSecurity.VerifyPassword(deserialized.HashAlgorithm, providedPassword, hashedPassword) + ? PasswordVerificationResult.SuccessRehashNeeded + : PasswordVerificationResult.Failed; } } } diff --git a/src/Umbraco.Infrastructure/Services/Implement/CacheInstructionService.cs b/src/Umbraco.Infrastructure/Services/Implement/CacheInstructionService.cs index 749924ecfc..cac2ec603e 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/CacheInstructionService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/CacheInstructionService.cs @@ -247,6 +247,13 @@ namespace Umbraco.Cms.Core.Services.Implement /// private bool TryDeserializeInstructions(CacheInstruction instruction, out JArray? jsonInstructions) { + if (instruction.Instructions is null) + { + _logger.LogError("Failed to deserialize instructions ({DtoId}: 'null').", instruction.Id); + jsonInstructions = null; + return false; + } + try { jsonInstructions = JsonConvert.DeserializeObject(instruction.Instructions); diff --git a/src/Umbraco.Infrastructure/Templates/HtmlMacroParameterParser.cs b/src/Umbraco.Infrastructure/Templates/HtmlMacroParameterParser.cs new file mode 100644 index 0000000000..6323139137 --- /dev/null +++ b/src/Umbraco.Infrastructure/Templates/HtmlMacroParameterParser.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Editors; +using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.Macros; + +namespace Umbraco.Cms.Infrastructure.Templates +{ + public sealed class HtmlMacroParameterParser : IHtmlMacroParameterParser + { + private readonly IMacroService _macroService; + private readonly ILogger _logger; + private readonly ParameterEditorCollection _parameterEditors; + + public HtmlMacroParameterParser(IMacroService macroService, ILogger logger, ParameterEditorCollection parameterEditors) + { + _macroService = macroService; + _logger = logger; + _parameterEditors = parameterEditors; + } + + /// + /// Parses out media UDIs from an HTML string based on embedded macro parameter values. + /// + /// HTML string + /// + public IEnumerable FindUmbracoEntityReferencesFromEmbeddedMacros(string text) + { + // There may be more than one macro with the same alias on the page so using a tuple + var foundMacros = new List>>(); + + // This legacy ParseMacros() already finds the macros within a Rich Text Editor using regexes + // It seems to lowercase the macro parameter alias - so making the dictionary case insensitive + MacroTagParser.ParseMacros(text, textblock => { }, (macroAlias, macroAttributes) => foundMacros.Add(new Tuple>(macroAlias, new Dictionary(macroAttributes, StringComparer.OrdinalIgnoreCase)))); + foreach (var umbracoEntityReference in GetUmbracoEntityReferencesFromMacros(foundMacros)) + { + yield return umbracoEntityReference; + } + } + + /// + /// Parses out media UDIs from Macro Grid Control parameters. + /// + /// + /// + public IEnumerable FindUmbracoEntityReferencesFromGridControlMacros(IEnumerable macroGridControls) + { + var foundMacros = new List>>(); + + foreach (var macroGridControl in macroGridControls) + { + // Deserialise JSON of Macro Grid Control to a class + var gridMacro = macroGridControl.Value.ToObject(); + // Collect any macro parameters that contain the media udi format + if (gridMacro is not null && gridMacro.MacroParameters is not null && gridMacro.MacroParameters.Any()) + { + foundMacros.Add(new Tuple>(gridMacro.MacroAlias, gridMacro.MacroParameters)); + } + } + + foreach (var umbracoEntityReference in GetUmbracoEntityReferencesFromMacros(foundMacros)) + { + yield return umbracoEntityReference; + } + } + + private IEnumerable GetUmbracoEntityReferencesFromMacros(List>> macros) + { + + if (_macroService is not IMacroWithAliasService macroWithAliasService) + { + yield break; + } + + var uniqueMacroAliases = macros.Select(f => f.Item1).Distinct(); + // TODO: Tracking Macro references + // Here we are finding the used macros' Udis (there should be a Related Macro relation type - but Relations don't accept 'Macro' as an option) + var foundMacroUmbracoEntityReferences = new List(); + // Get all the macro configs in one hit for these unique macro aliases - this is now cached with a custom cache policy + var macroConfigs = macroWithAliasService.GetAll(uniqueMacroAliases.ToArray()); + + foreach (var macro in macros) + { + var macroConfig = macroConfigs.FirstOrDefault(f => f.Alias == macro.Item1); + if (macroConfig is null) + { + continue; + } + foundMacroUmbracoEntityReferences.Add(new UmbracoEntityReference(Udi.Create(Constants.UdiEntityType.Macro, macroConfig.Key))); + // Only do this if the macros actually have parameters + if (macroConfig.Properties is not null && macroConfig.Properties.Keys.Any(f => f != "macroAlias")) + { + foreach (var umbracoEntityReference in GetUmbracoEntityReferencesFromMacroParameters(macro.Item2, macroConfig, _parameterEditors)) + { + yield return umbracoEntityReference; + } + } + } + } + + /// + /// Finds media UDIs in Macro Parameter Values by calling the GetReference method for all the Macro Parameter Editors for a particular macro. + /// + /// The parameters for the macro a dictionary of key/value strings + /// The macro configuration for this particular macro - contains the types of editors used for each parameter + /// A list of all the registered parameter editors used in the Umbraco implmentation - to look up the corresponding property editor for a macro parameter + /// + private IEnumerable GetUmbracoEntityReferencesFromMacroParameters(Dictionary macroParameters, IMacro macroConfig, ParameterEditorCollection parameterEditors) + { + var foundUmbracoEntityReferences = new List(); + foreach (var parameter in macroConfig.Properties) + { + if (macroParameters.TryGetValue(parameter.Alias, out string parameterValue)) + { + var parameterEditorAlias = parameter.EditorAlias; + // Lookup propertyEditor from the registered ParameterEditors with the implmementation to avoid looking up for each parameter + var parameterEditor = parameterEditors.FirstOrDefault(f => string.Equals(f.Alias, parameterEditorAlias, StringComparison.OrdinalIgnoreCase)); + if (parameterEditor is not null) + { + // Get the ParameterValueEditor for this PropertyEditor (where the GetReferences method is implemented) - cast as IDataValueReference to determine if 'it is' implemented for the editor + if (parameterEditor.GetValueEditor() is IDataValueReference parameterValueEditor) + { + foreach (var entityReference in parameterValueEditor.GetReferences(parameterValue)) + { + foundUmbracoEntityReferences.Add(entityReference); + } + } + else + { + _logger.LogInformation("{0} doesn't have a ValueEditor that implements IDataValueReference", parameterEditor.Alias); + } + } + } + } + + return foundUmbracoEntityReferences; + } + + // Poco class to deserialise the Json for a Macro Control + private class GridMacro + { + [JsonProperty("macroAlias")] + public string MacroAlias { get; set; } + + [JsonProperty("macroParamsDictionary")] + public Dictionary MacroParameters { get; set; } + } + } +} diff --git a/src/Umbraco.Infrastructure/Templates/IHtmlMacroParameterParser.cs b/src/Umbraco.Infrastructure/Templates/IHtmlMacroParameterParser.cs new file mode 100644 index 0000000000..6e484cc30a --- /dev/null +++ b/src/Umbraco.Infrastructure/Templates/IHtmlMacroParameterParser.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Editors; + +namespace Umbraco.Cms.Infrastructure.Templates +{ + /// + /// Provides methods to parse referenced entities as Macro parameters. + /// + public interface IHtmlMacroParameterParser + { + /// + /// Parses out media UDIs from an HTML string based on embedded macro parameter values. + /// + /// HTML string + /// + IEnumerable FindUmbracoEntityReferencesFromEmbeddedMacros(string text); + + /// + /// Parses out media UDIs from Macro Grid Control parameters. + /// + /// + /// + IEnumerable FindUmbracoEntityReferencesFromGridControlMacros(IEnumerable macroGridControls); + } +} diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs index d02d7e74ee..2ec30456ca 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs @@ -1,9 +1,11 @@ using System; using System.IO; +using System.Web; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Web.Common.Attributes; using Umbraco.Extensions; using Constants = Umbraco.Cms.Core.Constants; @@ -53,10 +55,15 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// public IActionResult GetResized(string imagePath, int width) { - var ext = Path.GetExtension(imagePath); + // We have to use HttpUtility to encode the path here, for non-ASCII characters + // We cannot use the WebUtility, as we only want to encode the path, and not the entire string + var encodedImagePath = HttpUtility.UrlPathEncode(imagePath); + + + var ext = Path.GetExtension(encodedImagePath); // check if imagePath is local to prevent open redirect - if (!Uri.IsWellFormedUriString(imagePath, UriKind.Relative)) + if (!Uri.IsWellFormedUriString(encodedImagePath, UriKind.Relative)) { return Unauthorized(); } @@ -82,7 +89,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } var rnd = imageLastModified.HasValue ? $"&rnd={imageLastModified:yyyyMMddHHmmss}" : null; - var imageUrl = _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(imagePath) + var imageUrl = _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(encodedImagePath) { Width = width, ImageCropMode = ImageCropMode.Max, diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index a548e14c1f..6f43e90acf 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -1097,7 +1097,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return new ActionResult(toMove); } - [Obsolete("Please use TrackedReferencesController.GetPagedReferences() instead. Scheduled for removal in V11.")] + [Obsolete("Please use TrackedReferencesController.GetPagedRelationsForItem() instead. Scheduled for removal in V11.")] public PagedResult GetPagedReferences(int id, string entityType, int pageNumber = 1, int pageSize = 100) { if (pageNumber <= 0 || pageSize <= 0) diff --git a/src/Umbraco.Web.BackOffice/Controllers/TrackedReferencesController.cs b/src/Umbraco.Web.BackOffice/Controllers/TrackedReferencesController.cs index 2cef8d61af..aa1a0ee86e 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/TrackedReferencesController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/TrackedReferencesController.cs @@ -1,12 +1,7 @@ -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.ContentEditing; -using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.BackOffice.ModelBinders; using Umbraco.Cms.Web.Common.Attributes; @@ -19,28 +14,36 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public class TrackedReferencesController : BackOfficeNotificationsController { private readonly ITrackedReferencesService _relationService; - private readonly IEntityService _entityService; - public TrackedReferencesController(ITrackedReferencesService relationService, - IEntityService entityService) + public TrackedReferencesController(ITrackedReferencesService relationService) { _relationService = relationService; - _entityService = entityService; } - // Used by info tabs on content, media etc. So this is basically finding childs of relations. - public ActionResult> GetPagedReferences(int id, int pageNumber = 1, - int pageSize = 100, bool filterMustBeIsDependency = false) + /// + /// Gets a page list of tracked references for the current item, so you can see where an item is being used. + /// + /// + /// Used by info tabs on content, media etc. and for the delete and unpublish of single items. + /// This is basically finding parents of relations. + /// + public ActionResult> GetPagedReferences(int id, int pageNumber = 1, int pageSize = 100, bool filterMustBeIsDependency = false) { if (pageNumber <= 0 || pageSize <= 0) { return BadRequest("Both pageNumber and pageSize must be greater than zero"); } - return _relationService.GetPagedRelationsForItems(new []{id}, pageNumber - 1, pageSize, filterMustBeIsDependency); + return _relationService.GetPagedRelationsForItem(id, pageNumber - 1, pageSize, filterMustBeIsDependency); } - // Used on delete, finds + /// + /// Gets a page list of the child nodes of the current item used in any kind of relation. + /// + /// + /// Used when deleting and unpublishing a single item to check if this item has any descending items that are in any kind of relation. + /// This is basically finding the descending items which are children in relations. + /// public ActionResult> GetPagedDescendantsInReferences(int parentId, int pageNumber = 1, int pageSize = 100, bool filterMustBeIsDependency = true) { if (pageNumber <= 0 || pageSize <= 0) @@ -48,12 +51,16 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return BadRequest("Both pageNumber and pageSize must be greater than zero"); } - return _relationService.GetPagedDescendantsInReferences(parentId, pageNumber - 1, pageSize, filterMustBeIsDependency); - } - // Used by unpublish content. So this is basically finding parents of relations. + /// + /// Gets a page list of the items used in any kind of relation from selected integer ids. + /// + /// + /// Used when bulk deleting content/media and bulk unpublishing content (delete and unpublish on List view). + /// This is basically finding children of relations. + /// [HttpGet] [HttpPost] public ActionResult> GetPagedReferencedItems([FromJsonPath] int[] ids, int pageNumber = 1, int pageSize = 100, bool filterMustBeIsDependency = true) @@ -64,8 +71,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } return _relationService.GetPagedItemsWithRelations(ids, pageNumber - 1, pageSize, filterMustBeIsDependency); - } } - } diff --git a/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs b/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs index 6c88239abd..f4d95e8282 100644 --- a/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs +++ b/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs @@ -2,10 +2,12 @@ using System; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Web; @@ -54,12 +56,14 @@ namespace Umbraco.Cms.Web.Common.Filters { if (content != null) { - IUmbracoContextAccessor umbracoContextAccessor = context.HttpContext.RequestServices.GetRequiredService(); + UriUtility uriUtility = context.HttpContext.RequestServices.GetRequiredService(); + + Uri originalRequestUrl = new Uri(context.HttpContext.Request.GetEncodedUrl()); + Uri cleanedUrl = uriUtility.UriToUmbraco(originalRequestUrl); + IPublishedRouter router = context.HttpContext.RequestServices.GetRequiredService(); - var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); - - IPublishedRequestBuilder requestBuilder = await router.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + IPublishedRequestBuilder requestBuilder = await router.CreateRequestAsync(cleanedUrl); requestBuilder.SetPublishedContent(content); IPublishedRequest publishedRequest = requestBuilder.Build(); diff --git a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs index be83e15a4b..3f4893e8dd 100644 --- a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs +++ b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs @@ -19,8 +19,11 @@ using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Infrastructure.PublishedCache; using Umbraco.Cms.Infrastructure.WebAssets; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Cms.Web.Common.Profiler; +using Umbraco.Cms.Web.Common.Routing; using Umbraco.Extensions; namespace Umbraco.Cms.Web.Common.Middleware @@ -50,6 +53,7 @@ namespace Umbraco.Cms.Web.Common.Middleware private readonly IRuntimeState _runtimeState; private readonly IVariationContextAccessor _variationContextAccessor; private readonly IDefaultCultureAccessor _defaultCultureAccessor; + private readonly IOptions _umbracoRequestOptions; private SmidgeOptions _smidgeOptions; private readonly WebProfiler? _profiler; @@ -59,6 +63,41 @@ namespace Umbraco.Cms.Web.Common.Middleware private static object s_firstBackOfficeRequestLocker = new object(); #pragma warning restore IDE0044 // Add readonly modifier + /// + /// Initializes a new instance of the class. + /// + // Obsolete, scheduled for removal in V11 + [Obsolete("Use constructor that takes an IOptions")] + public UmbracoRequestMiddleware( + ILogger logger, + IUmbracoContextFactory umbracoContextFactory, + IRequestCache requestCache, + IEventAggregator eventAggregator, + IProfiler profiler, + IHostingEnvironment hostingEnvironment, + UmbracoRequestPaths umbracoRequestPaths, + BackOfficeWebAssets backOfficeWebAssets, + IOptionsMonitor smidgeOptions, + IRuntimeState runtimeState, + IVariationContextAccessor variationContextAccessor, + IDefaultCultureAccessor defaultCultureAccessor) + : this( + logger, + umbracoContextFactory, + requestCache, + eventAggregator, + profiler, + hostingEnvironment, + umbracoRequestPaths, + backOfficeWebAssets, + smidgeOptions, + runtimeState, + variationContextAccessor, + defaultCultureAccessor, + StaticServiceProvider.Instance.GetRequiredService>()) + { + } + /// /// Initializes a new instance of the class. /// @@ -74,7 +113,8 @@ namespace Umbraco.Cms.Web.Common.Middleware IOptionsMonitor smidgeOptions, IRuntimeState runtimeState, IVariationContextAccessor variationContextAccessor, - IDefaultCultureAccessor defaultCultureAccessor) + IDefaultCultureAccessor defaultCultureAccessor, + IOptions umbracoRequestOptions) { _logger = logger; _umbracoContextFactory = umbracoContextFactory; @@ -86,6 +126,7 @@ namespace Umbraco.Cms.Web.Common.Middleware _runtimeState = runtimeState; _variationContextAccessor = variationContextAccessor; _defaultCultureAccessor = defaultCultureAccessor; + _umbracoRequestOptions = umbracoRequestOptions; _smidgeOptions = smidgeOptions.CurrentValue; _profiler = profiler as WebProfiler; // Ignore if not a WebProfiler @@ -96,7 +137,7 @@ namespace Umbraco.Cms.Web.Common.Middleware public async Task InvokeAsync(HttpContext context, RequestDelegate next) { // do not process if client-side request - if (context.Request.IsClientSideRequest()) + if (context.Request.IsClientSideRequest() && !_umbracoRequestOptions.Value.HandleAsServerSideRequest(context.Request)) { // we need this here because for bundle requests, these are 'client side' requests that we need to handle LazyInitializeBackOfficeServices(context.Request.Path); diff --git a/src/Umbraco.Web.Common/Routing/UmbracoRequestOptions.cs b/src/Umbraco.Web.Common/Routing/UmbracoRequestOptions.cs new file mode 100644 index 0000000000..2b27970cd6 --- /dev/null +++ b/src/Umbraco.Web.Common/Routing/UmbracoRequestOptions.cs @@ -0,0 +1,14 @@ +using System; +using Microsoft.AspNetCore.Http; + +namespace Umbraco.Cms.Web.Common.Routing +{ + public class UmbracoRequestOptions + { + /// + /// Gets the delegate that checks if we're gonna handle a request as a client-side request + /// this returns true by default and can be overwritten in Startup.cs + /// + public Func HandleAsServerSideRequest { get; set; } = x => false; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js index 83292251da..501ea9f81a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js @@ -12,6 +12,7 @@ scope.publishStatus = []; scope.currentVariant = null; scope.currentUrls = []; + scope.loadingReferences = false; scope.disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates; scope.allowChangeDocumentType = false; @@ -229,6 +230,10 @@ }); } + + function loadReferences(){ + scope.loadingReferences = true; + } function loadRedirectUrls() { scope.loadingRedirectUrls = true; //check if Redirect URL Management is enabled @@ -335,6 +340,7 @@ loadRedirectUrls(); setNodePublishStatus(); formatDatesToLocal(); + loadReferences(); } else { isInfoTab = false; } @@ -352,6 +358,7 @@ loadRedirectUrls(); setNodePublishStatus(); formatDatesToLocal(); + loadReferences(); } updateCurrentUrls(); }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/references/umbtrackedreferences.component.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/references/umbtrackedreferences.component.js index 000e87146c..15c1b4aedf 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/references/umbtrackedreferences.component.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/references/umbtrackedreferences.component.js @@ -16,17 +16,16 @@ function onInit() { - vm.referencesTitle = this.hideNoneDependencies ? "The following items depends on this" : "Referenced by the following items"; - vm.referencedDescendantsTitle = this.hideNoneDependencies ? "The following descending items has dependencies" : "The following descending items are referenced"; - + vm.referencesTitle = this.hideNoneDependencies ? "The following items depend on this" : "Referenced by the following items"; + vm.referencedDescendantsTitle = this.hideNoneDependencies ? "The following descending items have dependencies" : "The following descendant items have dependencies"; localizationService.localize(this.hideNoneDependencies ? "references_labelDependsOnThis" : "references_labelUsedByItems").then(function (value) { vm.referencesTitle = value; }); - + localizationService.localize(this.hideNoneDependencies ? "references_labelDependentDescendants" : "references_labelUsedDescendants").then(function (value) { vm.referencedDescendantsTitle = value; }); - + vm.descendantsOptions = {}; vm.descendantsOptions.filterMustBeIsDependency = this.hideNoneDependencies; vm.hasReferencesInDescendants = false; diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index b6f45ead0e..7e6c6658e5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -1259,7 +1259,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { getPublicAccess: function (contentId) { return umbRequestHelper.resourcePromise( $http.get( - umbRequestHelper.getApiUrl("contentApiBaseUrl", "GetPublicAccess", { + umbRequestHelper.getApiUrl("publicAccessApiBaseUrl", "GetPublicAccess", { contentId: contentId }) ), @@ -1308,7 +1308,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { } return umbRequestHelper.resourcePromise( $http.post( - umbRequestHelper.getApiUrl("contentApiBaseUrl", "PostPublicAccess", publicAccess) + umbRequestHelper.getApiUrl("publicAccessApiBaseUrl", "PostPublicAccess", publicAccess) ), "Failed to update public access for content item with id " + contentId ); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/trackedreferences.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/trackedreferences.resource.js index cd64c89589..d64951a6d0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/trackedreferences.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/trackedreferences.resource.js @@ -162,7 +162,7 @@ function trackedReferencesResource($q, $http, umbRequestHelper) { $http.post( umbRequestHelper.getApiUrl( "trackedReferencesApiBaseUrl", - "getPagedReferencedItems", + "GetPagedReferencedItems", query), { ids: ids, diff --git a/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.html b/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.html index 6f32e89988..c14110437d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/drawers/help/help.html @@ -128,13 +128,13 @@
- +
- Visit umbraco.tv + Watch our free tutorial videos
- The best Umbraco video tutorials + on the Umbraco Learning Base
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html index 6429e39db6..1c7545f9ec 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html @@ -21,7 +21,7 @@ - + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/references/umb-tracked-references-table.html b/src/Umbraco.Web.UI.Client/src/views/components/references/umb-tracked-references-table.html index afc8f9a3e6..d09bc23318 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/references/umb-tracked-references-table.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/references/umb-tracked-references-table.html @@ -7,8 +7,8 @@
-
Node Name
-
Type Name
+
Node Name
+
Type Name
Type
Relation
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/references/umb-tracked-references.html b/src/Umbraco.Web.UI.Client/src/views/components/references/umb-tracked-references.html index fd788cc598..9e08c5fbae 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/references/umb-tracked-references.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/references/umb-tracked-references.html @@ -26,11 +26,11 @@
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js index 92efb24e63..6561ed3499 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function ContentProtectController($scope, $q, contentResource, memberResource, memberGroupResource, navigationService, localizationService, editorService) { + function ContentProtectController($scope, $q, publicAccessResource, memberResource, memberGroupResource, navigationService, localizationService, editorService) { var vm = this; var id = $scope.currentNode.id; @@ -30,7 +30,7 @@ vm.loading = true; // get the current public access protection - contentResource.getPublicAccess(id).then(function (publicAccess) { + publicAccessResource.getPublicAccess(id).then(function (publicAccess) { vm.loading = false; // init the current settings for public access (if any) @@ -94,7 +94,7 @@ vm.buttonState = "busy"; var groups = _.map(vm.groups, function (group) { return encodeURIComponent(group.name); }); var usernames = _.map(vm.members, function (member) { return member.username; }); - contentResource.updatePublicAccess(id, groups, usernames, vm.loginPage.id, vm.errorPage.id).then( + publicAccessResource.updatePublicAccess(id, groups, usernames, vm.loginPage.id, vm.errorPage.id).then( function () { localizationService.localize("publicAccess_paIsProtected", [$scope.currentNode.name]).then(function (value) { vm.success = { @@ -181,11 +181,11 @@ vm.members.push(newMember); } }) - ); + ); }); editorService.close(); navigationService.allowHideDialog(true); - // wait for all the member lookups to complete + // wait for all the member lookups to complete vm.loading = true; $q.all(promises).then(function() { vm.loading = false; @@ -239,7 +239,7 @@ function removeProtectionConfirm() { vm.buttonState = "busy"; - contentResource.removePublicAccess(id).then( + publicAccessResource.removePublicAccess(id).then( function () { localizationService.localize("publicAccess_paIsRemoved", [$scope.currentNode.name]).then(function(value) { vm.success = { diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html index 436155de72..b33444177a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html @@ -17,7 +17,7 @@ Ask a question in the Community Forum
  • - Watch our free tutorial videos on the Umbraco Learning Base + Watch our free tutorial videos on the Umbraco Learning Base
  • Find out about our productivity boosting tools and commercial support diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js index 9331e4227b..0fdc251949 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js @@ -174,13 +174,13 @@ filterCssClass: "not-allowed", select: node => { const filepath = decodeURIComponent(node.id.replace(/\+/g, " ")); - block.view = "~/" + filepath; + block.view = "~/" + filepath.replace("wwwroot/", ""); editorService.close(); }, close: () => editorService.close() }; - editorService.filePicker(filePicker); + editorService.staticFilePicker(filePicker); }); }; diff --git a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js index 79164a2457..684ce6d2f0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js @@ -374,7 +374,7 @@ } function enableUser() { - vm.enableUserButtonState = "busfy"; + vm.enableUserButtonState = "busy"; usersResource.enableUsers([vm.user.id]).then(function (data) { vm.user.userState = "Active"; setUserDisplayState(); diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 6b5d301c5f..bf2de30f2e 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -788,6 +788,7 @@ New Next No + Node Name of Off OK @@ -829,6 +830,7 @@ Submit Success Type + Type Name Type to search... under Up @@ -1469,6 +1471,8 @@ To manage your website, simply open the Umbraco backoffice and start adding cont The best Umbraco video tutorials Visit our.umbraco.com Visit umbraco.tv + Watch our free tutorial videos + on the Umbraco Learning Base Default template @@ -2388,6 +2392,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Parent Child Count + Relation Relations Created Comment @@ -2468,18 +2473,13 @@ To manage your website, simply open the Umbraco backoffice and start adding cont References This Data Type has no references. + This item has no references. Used in Document Types Used in Media Types Used in Member Types Used by - Used in Documents - Used in Members - Used in Media Items in use Descendants in use - One or more of this item's descendants is being used in a media item. - One or more of this item's descendants is being used in a content item. - One or more of this item's descendants is being used in a member. This item or its descendants is being used. Deletion can lead to broken links on your website. This item or its descendants is being used. Unpublishing can lead to broken links on your website. Please take the appropriate actions. This item or its descendants is being used. Therefore, deletion has been disabled. @@ -2639,7 +2639,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont tutorial videos on the Umbraco Learning Base + Watch our free tutorial videos on the Umbraco Learning Base ]]> diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index e056647ebc..873361169a 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -809,7 +809,7 @@ New Next No - Node Name + Node Name of Off OK @@ -850,7 +850,7 @@ Submit Success Type - Type Name + Type Name Type to search... under Up @@ -1494,6 +1494,8 @@ To manage your website, simply open the Umbraco backoffice and start adding cont The best Umbraco video tutorials Visit our.umbraco.com Visit umbraco.tv + Watch our free tutorial videos + on the Umbraco Learning Base Default template @@ -2559,16 +2561,10 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Referenced by the following Member Types Referenced by Referenced by the following items - The following items depends on this - Referenced by the following Documents - Referenced by the following Members - Referenced by the following Media + The following items depend on this The following items are referenced - The following descending items are referenced - The following descending items has dependencies - One or more of this item's descendants is being referenced in a media item. - One or more of this item's descendants is being referenced in a content item. - One or more of this item's descendants is being referenced in a member. + The following descendant items have dependencies + The following descending items have dependencies This item or its descendants is being referenced. Deletion can lead to broken links on your website. This item or its descendants is being referenced. Unpublishing can lead to broken links on your website. Please take the appropriate actions. This item or its descendants is being referenced. Therefore, deletion has been disabled. @@ -2728,7 +2724,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont tutorial videos on the Umbraco Learning Base + Watch our free tutorial videos on the Umbraco Learning Base ]]> diff --git a/tests/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs b/tests/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs index 1afe45bc90..72bfae83b5 100644 --- a/tests/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs +++ b/tests/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs @@ -9,6 +9,11 @@ using System.Data.Common; using System.Diagnostics; using System.Threading; using Microsoft.Extensions.Logging; +using Moq; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Configuration; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Infrastructure.Migrations.Install; using Umbraco.Cms.Infrastructure.Persistence; namespace Umbraco.Cms.Tests.Integration.Testing diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Migrations/AdvancedMigrationTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Migrations/AdvancedMigrationTests.cs index c2e5cc6c4f..0a9e075f87 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Migrations/AdvancedMigrationTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Migrations/AdvancedMigrationTests.cs @@ -6,9 +6,11 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Migrations; using Umbraco.Cms.Core.Scoping; diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SchemaValidationTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SchemaValidationTest.cs index cd5c45868e..2c1eee10d3 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SchemaValidationTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SchemaValidationTest.cs @@ -1,6 +1,9 @@ using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Infrastructure.Migrations.Install; using Umbraco.Cms.Tests.Common.Testing; @@ -23,7 +26,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence using (ScopeProvider.CreateScope(autoComplete: true)) { - var schema = new DatabaseSchemaCreator(ScopeAccessor.AmbientScope.Database, LoggerFactory.CreateLogger(), LoggerFactory, UmbracoVersion, EventAggregator); + var schema = new DatabaseSchemaCreator(ScopeAccessor.AmbientScope.Database, LoggerFactory.CreateLogger(), LoggerFactory, UmbracoVersion, EventAggregator, Mock.Of>(x => x.CurrentValue == new InstallDefaultDataSettings())); schema.InitializeDatabaseSchema(); result = schema.ValidateSchema(DatabaseSchemaCreator.OrderedTables); } diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MacroServiceTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MacroServiceTests.cs index 1f847356a1..6601fcfbc2 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MacroServiceTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MacroServiceTests.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; @@ -25,7 +24,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] public class MacroServiceTests : UmbracoIntegrationTest { - private IMacroService MacroService => GetRequiredService(); + [Obsolete("After merging IMacroWithAliasService interface with IMacroService in Umbraco 11, this should go back to just being GetRequiredService()")] + private IMacroWithAliasService MacroService => GetRequiredService() as IMacroWithAliasService; [SetUp] public void SetupTest() @@ -53,6 +53,19 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services Assert.AreEqual("Test1", macro.Name); } + [Test] + public void Can_Get_By_Aliases() + { + // Act + IEnumerable macros = MacroService.GetAll("test1", "test2"); + + // Assert + Assert.IsNotNull(macros); + Assert.AreEqual(2, macros.Count()); + Assert.AreEqual("Test1", macros.ToArray()[0].Name); + Assert.AreEqual("Test2", macros.ToArray()[1].Name); + } + [Test] public void Can_Get_All() { diff --git a/tests/Umbraco.Tests.UnitTests/AutoFixture/Customizations/UmbracoCustomizations.cs b/tests/Umbraco.Tests.UnitTests/AutoFixture/Customizations/UmbracoCustomizations.cs index ea6c76225b..ed01773e74 100644 --- a/tests/Umbraco.Tests.UnitTests/AutoFixture/Customizations/UmbracoCustomizations.cs +++ b/tests/Umbraco.Tests.UnitTests/AutoFixture/Customizations/UmbracoCustomizations.cs @@ -12,6 +12,7 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.Migrations.Install; using Umbraco.Cms.Web.BackOffice.Controllers; using Umbraco.Cms.Web.BackOffice.Install; using Umbraco.Cms.Web.BackOffice.Routing; @@ -34,7 +35,8 @@ namespace Umbraco.Cms.Tests.UnitTests.AutoFixture.Customizations .Customize(new ConstructorCustomization(typeof(MemberController), new GreedyConstructorQuery())) .Customize(new ConstructorCustomization(typeof(BackOfficeController), new GreedyConstructorQuery())) .Customize(new ConstructorCustomization(typeof(BackOfficeUserManager), new GreedyConstructorQuery())) - .Customize(new ConstructorCustomization(typeof(MemberManager), new GreedyConstructorQuery())); + .Customize(new ConstructorCustomization(typeof(MemberManager), new GreedyConstructorQuery())) + .Customize(new ConstructorCustomization(typeof(DatabaseSchemaCreatorFactory), new GreedyConstructorQuery())); // When requesting an IUserStore ensure we actually uses a IUserLockoutStore fixture.Customize>(cc => cc.FromFactory(Mock.Of>)); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs index c952f40f82..17ea228cd1 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs @@ -58,7 +58,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components Mock.Of>(x => x.Get(It.IsAny()) == connectionStrings), new MapperCollection(() => Enumerable.Empty()), Mock.Of(), - new DatabaseSchemaCreatorFactory(loggerFactory.CreateLogger(), loggerFactory, new UmbracoVersion(), Mock.Of()), + new DatabaseSchemaCreatorFactory(loggerFactory.CreateLogger(), loggerFactory, new UmbracoVersion(), Mock.Of(), Mock.Of>()), mapperCollection); var fs = new FileSystems(loggerFactory, IOHelper, Options.Create(globalSettings), Mock.Of()); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Security/LegacyPasswordSecurityTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Security/LegacyPasswordSecurityTests.cs index 273823eec3..12c5f50d30 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Security/LegacyPasswordSecurityTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Security/LegacyPasswordSecurityTests.cs @@ -1,10 +1,14 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using System; +using System.Security.Cryptography; +using System.Text; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Security; +using Umbraco.Extensions; using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Security @@ -15,7 +19,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Security [Test] public void Check_Password_Hashed_Non_KeyedHashAlgorithm() { - IPasswordConfiguration passwordConfiguration = Mock.Of(x => x.HashAlgorithmType == "SHA256"); + IPasswordConfiguration passwordConfiguration = Mock.Of(x => x.HashAlgorithmType == "SHA1"); var passwordSecurity = new LegacyPasswordSecurity(); var pass = "ThisIsAHashedPassword"; @@ -45,14 +49,12 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Security [Test] public void Check_Password_Legacy_v4_SHA1() { - IPasswordConfiguration passwordConfiguration = Mock.Of(x => x.HashAlgorithmType == Constants.Security.AspNetUmbraco4PasswordHashAlgorithmName); - var passwordSecurity = new LegacyPasswordSecurity(); + const string clearText = "ThisIsAHashedPassword"; + var clearTextUnicodeBytes = Encoding.Unicode.GetBytes(clearText); + using var algorithm = new HMACSHA1(clearTextUnicodeBytes); + var dbPassword = Convert.ToBase64String(algorithm.ComputeHash(clearTextUnicodeBytes)); - var pass = "ThisIsAHashedPassword"; - var hashed = passwordSecurity.HashNewPassword(passwordConfiguration.HashAlgorithmType, pass, out string salt); - var storedPassword = passwordSecurity.FormatPasswordForStorage(passwordConfiguration.HashAlgorithmType, hashed, salt); - - var result = passwordSecurity.VerifyPassword(passwordConfiguration.HashAlgorithmType, "ThisIsAHashedPassword", storedPassword); + var result = new LegacyPasswordSecurity().VerifyLegacyHashedPassword(clearText, dbPassword); Assert.IsTrue(result); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/UmbracoPasswordHasherTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/UmbracoPasswordHasherTests.cs index aa6bb4156b..db4ec3392f 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/UmbracoPasswordHasherTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/UmbracoPasswordHasherTests.cs @@ -1,5 +1,9 @@ +using System; +using System.Security.Cryptography; +using System.Text; using AutoFixture.NUnit3; using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Models.Membership; @@ -15,9 +19,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security // Technically MD5, HMACSHA384 & HMACSHA512 were also possible but opt in as opposed to historic defaults. [Test] [InlineAutoMoqData("HMACSHA256", "Umbraco9Rocks!", "uB/pLEhhe1W7EtWMv/pSgg==1y8+aso9+h3AKRtJXlVYeg2TZKJUr64hccj82ZZ7Ksk=")] // Actually HMACSHA256 - [InlineAutoMoqData("HMACSHA256", "Umbraco9Rocks!", "t0U8atXTX/efNCtTafukwZeIpr8=")] // v4 site legacy password, with incorrect algorithm specified in database actually HMACSHA1 with password used as key. [InlineAutoMoqData("SHA1", "Umbraco9Rocks!", "6tZGfG9NTxJJYp19Fac9og==zzRggqANxhb+CbD/VabEt8cIde8=")] // When SHA1 is set on machine key. - public void VerifyHashedPassword_WithValidLegacyPasswordHash_ReturnsSuccessRehashNeeded( + public void VerifyHashedPassword_ValidHashWithoutLegacyEncoding_ReturnsSuccessRehashNeeded( string algorithm, string providedPassword, string hashedPassword, @@ -34,10 +37,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security Assert.AreEqual(PasswordVerificationResult.SuccessRehashNeeded, result); } - [Test] - [InlineAutoMoqData("PBKDF2.ASPNETCORE.V3", "Umbraco9Rocks!", "AQAAAAEAACcQAAAAEDCrYcnIhHKr38yuchsDu6AFqqmLNvRooKObV25GC1LC1tLY+gWGU4xNug0lc17PHA==")] - public void VerifyHashedPassword_WithValidModernPasswordHash_ReturnsSuccess( + [InlineAutoMoqData("HMACSHA1", "Umbraco9Rocks!", "t0U8atXTX/efNCtTafukwZeIpr8=")] + [InlineAutoMoqData("HMACSHA256", "Umbraco9Rocks!", "t0U8atXTX/efNCtTafukwZeIpr8=")] + [InlineAutoMoqData("FOOBARBAZQUX", "Umbraco9Rocks!", "t0U8atXTX/efNCtTafukwZeIpr8=")] + [InlineAutoMoqData("", "Umbraco9Rocks!", "t0U8atXTX/efNCtTafukwZeIpr8=")] + [InlineAutoMoqData(null, "Umbraco9Rocks!", "t0U8atXTX/efNCtTafukwZeIpr8=")] + public void VerifyHashedPassword_ValidHashWithLegacyEncoding_ReturnsSuccessRehashNeeded( string algorithm, string providedPassword, string hashedPassword, @@ -51,7 +57,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security var result = sut.VerifyHashedPassword(aUser, hashedPassword, providedPassword); - Assert.AreEqual(PasswordVerificationResult.Success, result); + Assert.AreEqual(PasswordVerificationResult.SuccessRehashNeeded, result); } [Test] @@ -73,6 +79,46 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security Assert.AreEqual(PasswordVerificationResult.Failed, result); } + [Test] + [AutoMoqData] + public void VerifyHashedPassword_WithIdentityV1OrV2StyleHash_ReturnsSuccessRehashNeeded( + TestUserStub aUser, + UmbracoPasswordHasher sut) + { + var options = Options.Create(new PasswordHasherOptions + { + CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2 + }); + + var upstreamHasher = new PasswordHasher(options); + + const string password = "Umbraco9Rocks!"; + var identityV1Or2StyleHash = upstreamHasher.HashPassword(aUser, password); + var result = sut.VerifyHashedPassword(aUser, identityV1Or2StyleHash, password); + + Assert.AreEqual(PasswordVerificationResult.SuccessRehashNeeded, result); + } + + [Test] + [AutoMoqData] + public void VerifyHashedPassword_WithIdentityV3StyleHash_ReturnsSuccess( + TestUserStub aUser, + UmbracoPasswordHasher sut) + { + var options = Options.Create(new PasswordHasherOptions + { + CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV3 + }); + + var upstreamHasher = new PasswordHasher(options); + + const string password = "Umbraco9Rocks!"; + var identityV1Or2StyleHash = upstreamHasher.HashPassword(aUser, password); + var result = sut.VerifyHashedPassword(aUser, identityV1Or2StyleHash, password); + + Assert.AreEqual(PasswordVerificationResult.Success, result); + } + public class TestUserStub : UmbracoIdentityUser { public TestUserStub() => PasswordConfig = "not null or empty"; diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs index c25b2fde1e..fdb29f88e6 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs @@ -323,6 +323,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.PublishedCache.NuCache } [Test] + [Retry(5)] // TODO make this test non-flaky. public async Task EventuallyCollectNulls() { var d = new SnapDictionary();