diff --git a/.gitignore b/.gitignore
index 5390da67dd..36834898be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -100,6 +100,9 @@ src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.dev.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/main.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/canvasdesigner.*.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/navigation.controller.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/main.controller.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/utilities.js
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
diff --git a/src/Umbraco.Configuration/Models/GlobalSettings.cs b/src/Umbraco.Configuration/Models/GlobalSettings.cs
index ed1b4e58a2..68d293d104 100644
--- a/src/Umbraco.Configuration/Models/GlobalSettings.cs
+++ b/src/Umbraco.Configuration/Models/GlobalSettings.cs
@@ -12,7 +12,7 @@ namespace Umbraco.Configuration.Models
///
internal class GlobalSettings : IGlobalSettings
{
- public const string Prefix = Constants.Configuration.ConfigPrefix + "Global:";
+ private const string Prefix = Constants.Configuration.ConfigGlobalPrefix;
internal const string
StaticReservedPaths = "~/app_plugins/,~/install/,~/mini-profiler-resources/,"; //must end with a comma!
diff --git a/src/Umbraco.Core/Constants-Configuration.cs b/src/Umbraco.Core/Constants-Configuration.cs
index 5124145a56..86a02affb6 100644
--- a/src/Umbraco.Core/Constants-Configuration.cs
+++ b/src/Umbraco.Core/Constants-Configuration.cs
@@ -12,6 +12,7 @@
///
public const string ConfigPrefix = "Umbraco:CMS:";
public const string ConfigSecurityPrefix = ConfigPrefix+"Security:";
+ public const string ConfigGlobalPrefix = ConfigPrefix + "Global:";
public const string ConfigModelsBuilderPrefix = ConfigPrefix+"ModelsBuilder:";
public const string ConfigRuntimeMinification = ConfigPrefix+"RuntimeMinification";
public const string ConfigRuntimeMinificationVersion = ConfigRuntimeMinification+":Version";
diff --git a/src/Umbraco.Core/Constants-Security.cs b/src/Umbraco.Core/Constants-Security.cs
index ae5741f0ef..b90a741f91 100644
--- a/src/Umbraco.Core/Constants-Security.cs
+++ b/src/Umbraco.Core/Constants-Security.cs
@@ -25,8 +25,10 @@
public const string UnknownUserName = "SYTEM";
public const string AdminGroupAlias = "admin";
+ public const string EditorGroupAlias = "editor";
public const string SensitiveDataGroupAlias = "sensitiveData";
public const string TranslatorGroupAlias = "translator";
+ public const string WriterGroupAlias = "writer";
public const string BackOfficeAuthenticationType = "UmbracoBackOffice";
public const string BackOfficeExternalAuthenticationType = "UmbracoExternalCookie";
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs
index 7c207c23c0..79c7f824fc 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs
@@ -111,7 +111,7 @@ namespace Umbraco.Web.Models.PublishedContent
var propertyType = content.ContentType.GetPropertyType(alias);
if (propertyType != null)
{
- _variationContextAccessor.ContextualizeVariation(propertyType.Variations, ref culture, ref segment);
+ _variationContextAccessor.ContextualizeVariation(propertyType.Variations, content.Id, ref culture, ref segment);
noValueProperty = content.GetProperty(alias);
}
@@ -168,7 +168,7 @@ namespace Umbraco.Web.Models.PublishedContent
{
culture = null;
segment = null;
- _variationContextAccessor.ContextualizeVariation(propertyType.Variations, ref culture, ref segment);
+ _variationContextAccessor.ContextualizeVariation(propertyType.Variations, content.Id, ref culture, ref segment);
}
property = content?.GetProperty(alias);
diff --git a/src/Umbraco.Core/Models/PublishedContent/VariationContext.cs b/src/Umbraco.Core/Models/PublishedContent/VariationContext.cs
index b83002fdce..424fc7c4a8 100644
--- a/src/Umbraco.Core/Models/PublishedContent/VariationContext.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/VariationContext.cs
@@ -23,5 +23,12 @@
/// Gets the segment.
///
public string Segment { get; }
+
+ ///
+ /// Gets the segment for the content item
+ ///
+ ///
+ ///
+ public virtual string GetSegment(int contentId) => Segment;
}
}
diff --git a/src/Umbraco.Core/Models/PublishedContent/VariationContextAccessorExtensions.cs b/src/Umbraco.Core/Models/PublishedContent/VariationContextAccessorExtensions.cs
index 6710d79cc6..a387ea87b8 100644
--- a/src/Umbraco.Core/Models/PublishedContent/VariationContextAccessorExtensions.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/VariationContextAccessorExtensions.cs
@@ -3,13 +3,32 @@
public static class VariationContextAccessorExtensions
{
public static void ContextualizeVariation(this IVariationContextAccessor variationContextAccessor, ContentVariation variations, ref string culture, ref string segment)
+ => variationContextAccessor.ContextualizeVariation(variations, null, ref culture, ref segment);
+
+ public static void ContextualizeVariation(this IVariationContextAccessor variationContextAccessor, ContentVariation variations, int contentId, ref string culture, ref string segment)
+ => variationContextAccessor.ContextualizeVariation(variations, (int?)contentId, ref culture, ref segment);
+
+ private static void ContextualizeVariation(this IVariationContextAccessor variationContextAccessor, ContentVariation variations, int? contentId, ref string culture, ref string segment)
{
if (culture != null && segment != null) return;
// use context values
var publishedVariationContext = variationContextAccessor?.VariationContext;
if (culture == null) culture = variations.VariesByCulture() ? publishedVariationContext?.Culture : "";
- if (segment == null) segment = variations.VariesBySegment() ? publishedVariationContext?.Segment : "";
+
+ if (segment == null)
+ {
+ if (variations.VariesBySegment())
+ {
+ segment = contentId == null
+ ? publishedVariationContext?.Segment
+ : publishedVariationContext?.GetSegment(contentId.Value);
+ }
+ else
+ {
+ segment = "";
+ }
+ }
}
}
}
diff --git a/src/Umbraco.Core/Services/IMembershipRoleService.cs b/src/Umbraco.Core/Services/IMembershipRoleService.cs
index 30531b5031..7389bb9799 100644
--- a/src/Umbraco.Core/Services/IMembershipRoleService.cs
+++ b/src/Umbraco.Core/Services/IMembershipRoleService.cs
@@ -11,6 +11,9 @@ namespace Umbraco.Core.Services
IEnumerable GetAllRoles();
IEnumerable GetAllRoles(int memberId);
IEnumerable GetAllRoles(string username);
+ IEnumerable GetAllRolesIds();
+ IEnumerable GetAllRolesIds(int memberId);
+ IEnumerable GetAllRolesIds(string username);
IEnumerable GetMembersInRole(string roleName);
IEnumerable FindMembersInRole(string roleName, string usernameToMatch, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith);
bool DeleteRole(string roleName, bool throwIfBeingUsed);
diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs
index cf63950bab..67b0b49a45 100644
--- a/src/Umbraco.Core/StringExtensions.cs
+++ b/src/Umbraco.Core/StringExtensions.cs
@@ -8,7 +8,6 @@ using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
-using Umbraco.Composing;
using Umbraco.Core.IO;
using Umbraco.Core.Strings;
@@ -72,6 +71,23 @@ namespace Umbraco.Core
}
return fileName;
+
+
+ }
+
+ ///
+ /// Determines the extension of the path or URL
+ ///
+ ///
+ /// Extension of the file
+ public static string GetFileExtension(this string file)
+ {
+ //Find any characters between the last . and the start of a query string or the end of the string
+ const string pattern = @"(?\.[^\.\?]+)(\?.*|$)";
+ var match = Regex.Match(file, pattern);
+ return match.Success
+ ? match.Groups["extension"].Value
+ : string.Empty;
}
///
diff --git a/src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/EmailNotificationMethod.cs b/src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/EmailNotificationMethod.cs
index 9d36b60d83..dcd539c4a2 100644
--- a/src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/EmailNotificationMethod.cs
+++ b/src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/EmailNotificationMethod.cs
@@ -22,7 +22,7 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods
public EmailNotificationMethod(ILocalizedTextService textService, IRuntimeState runtimeState, ILogger logger, IGlobalSettings globalSettings, IHealthChecksSettings healthChecksSettings, IContentSettings contentSettings) : base(healthChecksSettings)
{
- var recipientEmail = Settings["recipientEmail"]?.Value;
+ var recipientEmail = Settings?["recipientEmail"]?.Value;
if (string.IsNullOrWhiteSpace(recipientEmail))
{
Enabled = false;
diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs
index b4328c973d..36f1a30b20 100644
--- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs
+++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs
@@ -175,8 +175,8 @@ namespace Umbraco.Core.Migrations.Install
private void CreateUserGroupData()
{
_database.Insert(Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 1, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.AdminGroupAlias, Name = "Administrators", DefaultPermissions = "CADMOSKTPIURZ:5F7ï", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-medal" });
- _database.Insert(Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 2, StartMediaId = -1, StartContentId = -1, Alias = "writer", Name = "Writers", DefaultPermissions = "CAH:F", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-edit" });
- _database.Insert(Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 3, StartMediaId = -1, StartContentId = -1, Alias = "editor", Name = "Editors", DefaultPermissions = "CADMOSKTPUZ:5Fï", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-tools" });
+ _database.Insert(Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 2, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.WriterGroupAlias, Name = "Writers", DefaultPermissions = "CAH:F", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-edit" });
+ _database.Insert(Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 3, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.EditorGroupAlias, Name = "Editors", DefaultPermissions = "CADMOSKTPUZ:5Fï", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-tools" });
_database.Insert(Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 4, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.TranslatorGroupAlias, Name = "Translators", DefaultPermissions = "AF", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-globe" });
_database.Insert(Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 5, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.SensitiveDataGroupAlias, Name = "Sensitive data", DefaultPermissions = "", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-lock" });
}
diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
index b8a5755212..cfd67a5a02 100644
--- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
+++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
@@ -169,6 +169,7 @@ namespace Umbraco.Core.Migrations.Upgrade
.As("{0576E786-5C30-4000-B969-302B61E90CA3}");
To("{48AD6CCD-C7A4-4305-A8AB-38728AD23FC5}");
+ To("{DF470D86-E5CA-42AC-9780-9D28070E25F9}");
// finish migrating from v7 - recreate all keys and indexes
To("{3F9764F5-73D0-4D45-8804-1240A66E43A2}");
diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddPackagesSectionAccess.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddPackagesSectionAccess.cs
new file mode 100644
index 0000000000..d44e637a2c
--- /dev/null
+++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddPackagesSectionAccess.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
+{
+ public class AddPackagesSectionAccess : MigrationBase
+ {
+ public AddPackagesSectionAccess(IMigrationContext context)
+ : base(context)
+ { }
+
+ public override void Migrate()
+ {
+ // Any user group which had access to the Developer section should have access to Packages
+ Database.Execute($@"
+ insert into {Constants.DatabaseSchema.Tables.UserGroup2App}
+ select userGroupId, '{Constants.Applications.Packages}'
+ from {Constants.DatabaseSchema.Tables.UserGroup2App}
+ where app='developer'");
+ }
+ }
+}
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs
index c1fb5c3159..f432d6959e 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs
@@ -32,8 +32,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
path = path.EnsureEndsWith(".css");
- if (FileSystem.FileExists(path) == false)
+ // if the css directory is changed, references to the old path can still exist (ie in RTE config)
+ // these old references will throw an error, which breaks the RTE
+ // try-catch here makes the request fail silently, and allows RTE to load correctly
+ try
+ {
+ if (FileSystem.FileExists(path) == false)
+ return null;
+ } catch
+ {
return null;
+ }
// content will be lazy-loaded when required
var created = FileSystem.GetCreated(path).UtcDateTime;
diff --git a/src/Umbraco.Infrastructure/PropertyEditors/UploadFileTypeValidator.cs b/src/Umbraco.Infrastructure/PropertyEditors/UploadFileTypeValidator.cs
index b833a60c2d..b64c52648a 100644
--- a/src/Umbraco.Infrastructure/PropertyEditors/UploadFileTypeValidator.cs
+++ b/src/Umbraco.Infrastructure/PropertyEditors/UploadFileTypeValidator.cs
@@ -58,7 +58,7 @@ namespace Umbraco.Web.PropertyEditors
internal static bool IsValidFileExtension(string fileName, IContentSettings contentSettings)
{
if (fileName.IndexOf('.') <= 0) return false;
- var extension = new FileInfo(fileName).Extension.TrimStart(".");
+ var extension = fileName.GetFileExtension().TrimStart(".");
return contentSettings.IsFileAllowedForUpload(extension);
}
}
diff --git a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs
index fc0dd8a554..7ee6065210 100644
--- a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs
+++ b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs
@@ -958,6 +958,35 @@ namespace Umbraco.Core.Services.Implement
}
}
+ public IEnumerable GetAllRolesIds()
+ {
+ using (var scope = ScopeProvider.CreateScope(autoComplete: true))
+ {
+ scope.ReadLock(Constants.Locks.MemberTree);
+ return _memberGroupRepository.GetMany().Select(x => x.Id).Distinct();
+ }
+ }
+
+ public IEnumerable GetAllRolesIds(int memberId)
+ {
+ using (var scope = ScopeProvider.CreateScope(autoComplete: true))
+ {
+ scope.ReadLock(Constants.Locks.MemberTree);
+ var result = _memberGroupRepository.GetMemberGroupsForMember(memberId);
+ return result.Select(x => x.Id).Distinct();
+ }
+ }
+
+ public IEnumerable GetAllRolesIds(string username)
+ {
+ using (var scope = ScopeProvider.CreateScope(autoComplete: true))
+ {
+ scope.ReadLock(Constants.Locks.MemberTree);
+ var result = _memberGroupRepository.GetMemberGroupsForMember(username);
+ return result.Select(x => x.Id).Distinct();
+ }
+ }
+
public IEnumerable GetMembersInRole(string roleName)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
diff --git a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs
index e23f7539c2..24f1af5843 100644
--- a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs
+++ b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs
@@ -368,7 +368,7 @@ namespace Umbraco.Core.Services.Implement
///
public string GetDefaultMemberType()
{
- return "writer";
+ return Constants.Security.WriterGroupAlias;
}
///
diff --git a/src/Umbraco.PublishedCache.NuCache/ContentCache.cs b/src/Umbraco.PublishedCache.NuCache/ContentCache.cs
index dd5a76837e..b4388b7b0b 100644
--- a/src/Umbraco.PublishedCache.NuCache/ContentCache.cs
+++ b/src/Umbraco.PublishedCache.NuCache/ContentCache.cs
@@ -355,6 +355,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
private static IEnumerable GetByXPath(XPathNodeIterator iterator)
{
+ iterator = iterator.Clone();
while (iterator.MoveNext())
{
var xnav = iterator.Current as NavigableNavigator;
diff --git a/src/Umbraco.PublishedCache.NuCache/Property.cs b/src/Umbraco.PublishedCache.NuCache/Property.cs
index 0254b815c1..86023bb302 100644
--- a/src/Umbraco.PublishedCache.NuCache/Property.cs
+++ b/src/Umbraco.PublishedCache.NuCache/Property.cs
@@ -90,7 +90,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
// determines whether a property has value
public override bool HasValue(string culture = null, string segment = null)
{
- _content.VariationContextAccessor.ContextualizeVariation(_variations, ref culture, ref segment);
+ _content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment);
var value = GetSourceValue(culture, segment);
var hasValue = PropertyType.IsValue(value, PropertyValueLevel.Source);
@@ -194,7 +194,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
public override object GetSourceValue(string culture = null, string segment = null)
{
- _content.VariationContextAccessor.ContextualizeVariation(_variations, ref culture, ref segment);
+ _content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment);
if (culture == "" && segment == "")
return _sourceValue;
@@ -208,7 +208,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
public override object GetValue(string culture = null, string segment = null)
{
- _content.VariationContextAccessor.ContextualizeVariation(_variations, ref culture, ref segment);
+ _content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment);
object value;
lock (_locko)
@@ -229,7 +229,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
public override object GetXPathValue(string culture = null, string segment = null)
{
- _content.VariationContextAccessor.ContextualizeVariation(_variations, ref culture, ref segment);
+ _content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment);
lock (_locko)
{
diff --git a/src/Umbraco.Tests.Common/Builders/ChildBuilderBase.cs b/src/Umbraco.Tests.Common/Builders/ChildBuilderBase.cs
index e1436ac1fe..fcd9691e1f 100644
--- a/src/Umbraco.Tests.Common/Builders/ChildBuilderBase.cs
+++ b/src/Umbraco.Tests.Common/Builders/ChildBuilderBase.cs
@@ -9,7 +9,6 @@ namespace Umbraco.Tests.Common.Builders
_parentBuilder = parentBuilder;
}
-
public TParent Done()
{
return _parentBuilder;
diff --git a/src/Umbraco.Tests.Common/Builders/ConfigurationEditorBuilder.cs b/src/Umbraco.Tests.Common/Builders/ConfigurationEditorBuilder.cs
index 7789090d16..3e0b9b96a9 100644
--- a/src/Umbraco.Tests.Common/Builders/ConfigurationEditorBuilder.cs
+++ b/src/Umbraco.Tests.Common/Builders/ConfigurationEditorBuilder.cs
@@ -21,7 +21,7 @@ namespace Umbraco.Tests.Common.Builders
public override IConfigurationEditor Build()
{
- var defaultConfiguration = _defaultConfiguration ?? new Dictionary();
+ var defaultConfiguration = _defaultConfiguration ?? new Dictionary();
return new ConfigurationEditor()
{
diff --git a/src/Umbraco.Tests.Common/Builders/DataTypeBuilder.cs b/src/Umbraco.Tests.Common/Builders/DataTypeBuilder.cs
index 20dc1bab81..de7b8d9a97 100644
--- a/src/Umbraco.Tests.Common/Builders/DataTypeBuilder.cs
+++ b/src/Umbraco.Tests.Common/Builders/DataTypeBuilder.cs
@@ -8,10 +8,16 @@ namespace Umbraco.Tests.Common.Builders
: BuilderBase,
IWithIdBuilder,
IWithKeyBuilder,
+ IWithCreatorIdBuilder,
IWithCreateDateBuilder,
IWithUpdateDateBuilder,
IWithDeleteDateBuilder,
- IWithNameBuilder
+ IWithNameBuilder,
+ IWithParentIdBuilder,
+ IWithTrashedBuilder,
+ IWithLevelBuilder,
+ IWithPathBuilder,
+ IWithSortOrderBuilder
{
private readonly DataEditorBuilder _dataEditorBuilder;
private int? _id;
@@ -34,54 +40,18 @@ namespace Umbraco.Tests.Common.Builders
_dataEditorBuilder = new DataEditorBuilder(this);
}
- public DataTypeBuilder WithParentId(int parentId)
- {
- _parentId = parentId;
- return this;
- }
-
- public DataTypeBuilder WithTrashed(bool trashed)
- {
- _trashed = trashed;
- return this;
- }
-
// public DataTypeBuilder WithConfiguration(object configuration)
// {
// _configuration = configuration;
// return this;
// }
- public DataTypeBuilder WithLevel(int level)
- {
- _level = level;
- return this;
- }
-
- public DataTypeBuilder WithPath(string path)
- {
- _path = path;
- return this;
- }
-
- public DataTypeBuilder WithCreatorId(int creatorId)
- {
- _creatorId = creatorId;
- return this;
- }
-
public DataTypeBuilder WithDatabaseType(ValueStorageType databaseType)
{
_databaseType = databaseType;
return this;
}
- public DataTypeBuilder WithSortOrder(int sortOrder)
- {
- _sortOrder = sortOrder;
- return this;
- }
-
public DataEditorBuilder AddEditor()
{
return _dataEditorBuilder;
@@ -133,6 +103,12 @@ namespace Umbraco.Tests.Common.Builders
set => _key = value;
}
+ int? IWithCreatorIdBuilder.CreatorId
+ {
+ get => _creatorId;
+ set => _creatorId = value;
+ }
+
DateTime? IWithCreateDateBuilder.CreateDate
{
get => _createDate;
@@ -156,5 +132,35 @@ namespace Umbraco.Tests.Common.Builders
get => _name;
set => _name = value;
}
+
+ int? IWithParentIdBuilder.ParentId
+ {
+ get => _parentId;
+ set => _parentId = value;
+ }
+
+ bool? IWithTrashedBuilder.Trashed
+ {
+ get => _trashed;
+ set => _trashed = value;
+ }
+
+ int? IWithLevelBuilder.Level
+ {
+ get => _level;
+ set => _level = value;
+ }
+
+ string IWithPathBuilder.Path
+ {
+ get => _path;
+ set => _path = value;
+ }
+
+ int? IWithSortOrderBuilder.SortOrder
+ {
+ get => _sortOrder;
+ set => _sortOrder = value;
+ }
}
}
diff --git a/src/Umbraco.Tests.Common/Builders/DictionaryTranslationBuilder.cs b/src/Umbraco.Tests.Common/Builders/DictionaryTranslationBuilder.cs
index 37fb7c5b07..d2f9d4bf02 100644
--- a/src/Umbraco.Tests.Common/Builders/DictionaryTranslationBuilder.cs
+++ b/src/Umbraco.Tests.Common/Builders/DictionaryTranslationBuilder.cs
@@ -21,7 +21,6 @@ namespace Umbraco.Tests.Common.Builders
private DateTime? _updateDate;
private string _value;
-
public DictionaryTranslationBuilder(DictionaryItemBuilder parentBuilder) : base(parentBuilder)
{
_languageBuilder = new LanguageBuilder(this);
diff --git a/src/Umbraco.Tests.Common/Builders/Extensions/BuilderExtensions.cs b/src/Umbraco.Tests.Common/Builders/Extensions/BuilderExtensions.cs
index c8f3f80bf1..cce3b88470 100644
--- a/src/Umbraco.Tests.Common/Builders/Extensions/BuilderExtensions.cs
+++ b/src/Umbraco.Tests.Common/Builders/Extensions/BuilderExtensions.cs
@@ -12,6 +12,13 @@ namespace Umbraco.Tests.Common.Builders.Extensions
return builder;
}
+ public static T WithCreatorId(this T builder, int creatorId)
+ where T : IWithCreatorIdBuilder
+ {
+ builder.CreatorId = creatorId;
+ return builder;
+ }
+
public static T WithCreateDate(this T builder, DateTime createDate)
where T : IWithCreateDateBuilder
{
@@ -46,5 +53,61 @@ namespace Umbraco.Tests.Common.Builders.Extensions
builder.Key = key;
return builder;
}
+
+ public static T WithParentId(this T builder, int parentId)
+ where T : IWithParentIdBuilder
+ {
+ builder.ParentId = parentId;
+ return builder;
+ }
+
+ public static T WithTrashed(this T builder, bool trashed)
+ where T : IWithTrashedBuilder
+ {
+ builder.Trashed = trashed;
+ return builder;
+ }
+
+ public static T WithLevel(this T builder, int level)
+ where T : IWithLevelBuilder
+ {
+ builder.Level = level;
+ return builder;
+ }
+
+ public static T WithPath(this T builder, string path)
+ where T : IWithPathBuilder
+ {
+ builder.Path = path;
+ return builder;
+ }
+
+ public static T WithSortOrder(this T builder, int sortOrder)
+ where T : IWithSortOrderBuilder
+ {
+ builder.SortOrder = sortOrder;
+ return builder;
+ }
+
+ public static T WithDescription(this T builder, string description)
+ where T : IWithDescriptionBuilder
+ {
+ builder.Description = description;
+ return builder;
+ }
+
+ public static T WithIcon(this T builder, string icon)
+ where T : IWithIconBuilder
+ {
+ builder.Icon = icon;
+ return builder;
+ }
+
+ public static T WithThumbnail(this T builder, string thumbnail)
+ where T : IWithThumbnailBuilder
+ {
+ builder.Thumbnail = thumbnail;
+ return builder;
+ }
}
}
diff --git a/src/Umbraco.Tests.Common/Builders/Extensions/StringExtensions.cs b/src/Umbraco.Tests.Common/Builders/Extensions/StringExtensions.cs
new file mode 100644
index 0000000000..b426cabaa6
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/Extensions/StringExtensions.cs
@@ -0,0 +1,20 @@
+namespace Umbraco.Tests.Common.Builders.Extensions
+{
+ public static class StringExtensions
+ {
+ public static string ToCamelCase(this string s)
+ {
+ if (string.IsNullOrWhiteSpace(s))
+ {
+ return string.Empty;
+ }
+
+ if (s.Length == 1)
+ {
+ return s.ToLowerInvariant();
+ }
+
+ return char.ToLowerInvariant(s[0]) + s.Substring(1);
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/GenericCollectionBuilder.cs b/src/Umbraco.Tests.Common/Builders/GenericCollectionBuilder.cs
new file mode 100644
index 0000000000..c7e176e9b0
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/GenericCollectionBuilder.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+
+namespace Umbraco.Tests.Common.Builders
+{
+ public class GenericCollectionBuilder
+ : ChildBuilderBase>
+ {
+ private readonly IList _collection;
+
+ public GenericCollectionBuilder(TBuilder parentBuilder) : base(parentBuilder)
+ {
+ _collection = new List();
+ }
+
+ public override IEnumerable Build()
+ {
+ return _collection;
+ }
+
+ public GenericCollectionBuilder WithValue(T value)
+ {
+ _collection.Add(value);
+ return this;
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/GenericDictionaryBuilder.cs b/src/Umbraco.Tests.Common/Builders/GenericDictionaryBuilder.cs
new file mode 100644
index 0000000000..8f6aedcf43
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/GenericDictionaryBuilder.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+
+namespace Umbraco.Tests.Common.Builders
+{
+ public class GenericDictionaryBuilder
+ : ChildBuilderBase>
+ {
+ private readonly IDictionary _dictionary;
+
+ public GenericDictionaryBuilder(TBuilder parentBuilder) : base(parentBuilder)
+ {
+ _dictionary = new Dictionary();
+ }
+
+ public override IDictionary Build()
+ {
+ return _dictionary;
+ }
+
+ public GenericDictionaryBuilder WithKeyValue(TKey key, TValue value)
+ {
+ _dictionary.Add(key, value);
+ return this;
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/Interfaces/IWithApprovedBuilder.cs b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithApprovedBuilder.cs
new file mode 100644
index 0000000000..48d41e7a84
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithApprovedBuilder.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Tests.Common.Builders.Interfaces
+{
+ public interface IWithApprovedBuilder
+ {
+ bool? Approved { get; set; }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/Interfaces/IWithCreatorIdBuilder.cs b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithCreatorIdBuilder.cs
new file mode 100644
index 0000000000..ae7712cf9e
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithCreatorIdBuilder.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Umbraco.Tests.Common.Builders.Interfaces
+{
+ public interface IWithCreatorIdBuilder
+ {
+ int? CreatorId { get; set; }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/Interfaces/IWithDescriptionBuilder.cs b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithDescriptionBuilder.cs
new file mode 100644
index 0000000000..1a155073b3
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithDescriptionBuilder.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Tests.Common.Builders.Interfaces
+{
+ public interface IWithDescriptionBuilder
+ {
+ string Description { get; set; }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/Interfaces/IWithIconBuilder.cs b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithIconBuilder.cs
new file mode 100644
index 0000000000..5de5224e18
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithIconBuilder.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Tests.Common.Builders.Interfaces
+{
+ public interface IWithIconBuilder
+ {
+ string Icon { get; set; }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/Interfaces/IWithLevelBuilder.cs b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithLevelBuilder.cs
new file mode 100644
index 0000000000..dc6ee239ab
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithLevelBuilder.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Tests.Common.Builders.Interfaces
+{
+ public interface IWithLevelBuilder
+ {
+ int? Level { get; set; }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/Interfaces/IWithParentIdBuilder.cs b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithParentIdBuilder.cs
new file mode 100644
index 0000000000..33d13b7ef1
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithParentIdBuilder.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Tests.Common.Builders.Interfaces
+{
+ public interface IWithParentIdBuilder
+ {
+ int? ParentId { get; set; }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/Interfaces/IWithPathBuilder.cs b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithPathBuilder.cs
new file mode 100644
index 0000000000..ed632c4e7d
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithPathBuilder.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Tests.Common.Builders.Interfaces
+{
+ public interface IWithPathBuilder
+ {
+ string Path { get; set; }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/Interfaces/IWithSortOrderBuilder.cs b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithSortOrderBuilder.cs
new file mode 100644
index 0000000000..3202c243fb
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithSortOrderBuilder.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Tests.Common.Builders.Interfaces
+{
+ public interface IWithSortOrderBuilder
+ {
+ int? SortOrder { get; set; }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/Interfaces/IWithThumbnailBuilder.cs b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithThumbnailBuilder.cs
new file mode 100644
index 0000000000..ce5b10e274
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithThumbnailBuilder.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Tests.Common.Builders.Interfaces
+{
+ public interface IWithThumbnailBuilder
+ {
+ string Thumbnail { get; set; }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/Interfaces/IWithTrashedBuilder.cs b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithTrashedBuilder.cs
new file mode 100644
index 0000000000..119e6a6e52
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/Interfaces/IWithTrashedBuilder.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Tests.Common.Builders.Interfaces
+{
+ public interface IWithTrashedBuilder
+ {
+ bool? Trashed { get; set; }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/MemberBuilder.cs b/src/Umbraco.Tests.Common/Builders/MemberBuilder.cs
new file mode 100644
index 0000000000..cef4a35524
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/MemberBuilder.cs
@@ -0,0 +1,280 @@
+using System;
+using Umbraco.Core.Models;
+using Umbraco.Tests.Common.Builders.Interfaces;
+
+namespace Umbraco.Tests.Common.Builders
+{
+ public class MemberBuilder
+ : BuilderBase,
+ IWithIdBuilder,
+ IWithKeyBuilder,
+ IWithCreatorIdBuilder,
+ IWithCreateDateBuilder,
+ IWithUpdateDateBuilder,
+ IWithNameBuilder,
+ IWithTrashedBuilder,
+ IWithLevelBuilder,
+ IWithPathBuilder,
+ IWithSortOrderBuilder
+ {
+ private MemberTypeBuilder _memberTypeBuilder;
+ private GenericCollectionBuilder _memberGroupsBuilder;
+ private GenericDictionaryBuilder _additionalDataBuilder;
+ private GenericDictionaryBuilder _propertyDataBuilder;
+
+ private int? _id;
+ private Guid? _key;
+ private DateTime? _createDate;
+ private DateTime? _updateDate;
+ private string _name;
+ private int? _creatorId;
+ private string _username;
+ private string _rawPasswordValue;
+ private string _email;
+ private int? _failedPasswordAttempts;
+ private int? _level;
+ private string _path;
+ private bool? _isApproved;
+ private bool? _isLockedOut;
+ private DateTime? _lastLockoutDate;
+ private DateTime? _lastLoginDate;
+ private DateTime? _lastPasswordChangeDate;
+ private int? _sortOrder;
+ private bool? _trashed;
+ private int? _propertyIdsIncrementingFrom;
+
+ public MemberBuilder WithUserName(string username)
+ {
+ _username = username;
+ return this;
+ }
+
+ public MemberBuilder WithEmail(string email)
+ {
+ _email = email;
+ return this;
+ }
+
+ public MemberBuilder WithRawPasswordValue(string rawPasswordValue)
+ {
+ _rawPasswordValue = rawPasswordValue;
+ return this;
+ }
+
+ public MemberBuilder WithFailedPasswordAttempts(int failedPasswordAttempts)
+ {
+ _failedPasswordAttempts = failedPasswordAttempts;
+ return this;
+ }
+
+ public MemberBuilder WithIsApproved(bool isApproved)
+ {
+ _isApproved = isApproved;
+ return this;
+ }
+
+ public MemberBuilder WithIsLockedOut(bool isLockedOut)
+ {
+ _isLockedOut = isLockedOut;
+ return this;
+ }
+
+ public MemberBuilder WithLastLockoutDate(DateTime lastLockoutDate)
+ {
+ _lastLockoutDate = lastLockoutDate;
+ return this;
+ }
+
+ public MemberBuilder WithLastLoginDate(DateTime lastLoginDate)
+ {
+ _lastLoginDate = lastLoginDate;
+ return this;
+ }
+
+ public MemberBuilder WithLastPasswordChangeDate(DateTime lastPasswordChangeDate)
+ {
+ _lastPasswordChangeDate = lastPasswordChangeDate;
+ return this;
+ }
+
+ public MemberBuilder WithPropertyIdsIncrementingFrom(int propertyIdsIncrementingFrom)
+ {
+ _propertyIdsIncrementingFrom = propertyIdsIncrementingFrom;
+ return this;
+ }
+
+ public MemberTypeBuilder AddMemberType()
+ {
+ var builder = new MemberTypeBuilder(this);
+ _memberTypeBuilder = builder;
+ return builder;
+ }
+
+ public GenericCollectionBuilder AddMemberGroups()
+ {
+ var builder = new GenericCollectionBuilder(this);
+ _memberGroupsBuilder = builder;
+ return builder;
+ }
+
+ public GenericDictionaryBuilder AddAdditionalData()
+ {
+ var builder = new GenericDictionaryBuilder(this);
+ _additionalDataBuilder = builder;
+ return builder;
+ }
+
+ public GenericDictionaryBuilder AddPropertyData()
+ {
+ var builder = new GenericDictionaryBuilder(this);
+ _propertyDataBuilder = builder;
+ return builder;
+ }
+
+ public override Member Build()
+ {
+ var id = _id ?? 1;
+ var key = _key ?? Guid.NewGuid();
+ var createDate = _createDate ?? DateTime.Now;
+ var updateDate = _updateDate ?? DateTime.Now;
+ var name = _name ?? Guid.NewGuid().ToString();
+ var creatorId = _creatorId ?? 1;
+ var username = _username ?? string.Empty;
+ var email = _email ?? string.Empty;
+ var rawPasswordValue = _rawPasswordValue ?? string.Empty;
+ var failedPasswordAttempts = _failedPasswordAttempts ?? 0;
+ var level = _level ?? 1;
+ var path = _path ?? "-1";
+ var isApproved = _isApproved ?? false;
+ var isLockedOut = _isLockedOut ?? false;
+ var lastLockoutDate = _lastLockoutDate ?? DateTime.Now;
+ var lastLoginDate = _lastLoginDate ?? DateTime.Now;
+ var lastPasswordChangeDate = _lastPasswordChangeDate ?? DateTime.Now;
+ var sortOrder = _sortOrder ?? 0;
+ var trashed = _trashed ?? false;
+
+ if (_memberTypeBuilder == null)
+ {
+ throw new InvalidOperationException("A member cannot be constructed without providing a member type (use AddMemberType).");
+ }
+
+ var memberType = _memberTypeBuilder.Build();
+
+ var member = new Member(name, email, username, rawPasswordValue, memberType)
+ {
+ Id = id,
+ Key = key,
+ CreateDate = createDate,
+ UpdateDate = updateDate,
+ CreatorId = creatorId,
+ Level = level,
+ Path = path,
+ SortOrder = sortOrder,
+ Trashed = trashed,
+ };
+
+ if (_propertyIdsIncrementingFrom.HasValue)
+ {
+ var i = _propertyIdsIncrementingFrom.Value;
+ foreach (var property in member.Properties)
+ {
+ property.Id = ++i;
+ }
+ }
+
+ member.FailedPasswordAttempts = failedPasswordAttempts;
+ member.IsApproved = isApproved;
+ member.IsLockedOut = isLockedOut;
+ member.LastLockoutDate = lastLockoutDate;
+ member.LastLoginDate = lastLoginDate;
+ member.LastPasswordChangeDate = lastPasswordChangeDate;
+
+ if (_memberGroupsBuilder != null)
+ {
+ member.Groups = _memberGroupsBuilder.Build();
+ }
+
+ if (_additionalDataBuilder != null)
+ {
+ var additionalData = _additionalDataBuilder.Build();
+ foreach (var kvp in additionalData)
+ {
+ member.AdditionalData.Add(kvp.Key, kvp.Value);
+ }
+ }
+
+ if (_propertyDataBuilder != null)
+ {
+ var propertyData = _propertyDataBuilder.Build();
+ foreach (var kvp in propertyData)
+ {
+ member.SetValue(kvp.Key, kvp.Value);
+ }
+
+ member.ResetDirtyProperties(false);
+ }
+
+ return member;
+ }
+
+ int? IWithIdBuilder.Id
+ {
+ get => _id;
+ set => _id = value;
+ }
+
+ Guid? IWithKeyBuilder.Key
+ {
+ get => _key;
+ set => _key = value;
+ }
+
+ int? IWithCreatorIdBuilder.CreatorId
+ {
+ get => _creatorId;
+ set => _creatorId = value;
+ }
+
+ DateTime? IWithCreateDateBuilder.CreateDate
+ {
+ get => _createDate;
+ set => _createDate = value;
+ }
+
+ DateTime? IWithUpdateDateBuilder.UpdateDate
+ {
+ get => _updateDate;
+ set => _updateDate = value;
+ }
+
+ string IWithNameBuilder.Name
+ {
+ get => _name;
+ set => _name = value;
+ }
+
+ bool? IWithTrashedBuilder.Trashed
+ {
+ get => _trashed;
+ set => _trashed = value;
+ }
+
+ int? IWithLevelBuilder.Level
+ {
+ get => _level;
+ set => _level = value;
+ }
+
+ string IWithPathBuilder.Path
+ {
+ get => _path;
+ set => _path = value;
+ }
+
+ int? IWithSortOrderBuilder.SortOrder
+ {
+ get => _sortOrder;
+ set => _sortOrder = value;
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/MemberGroupBuilder.cs b/src/Umbraco.Tests.Common/Builders/MemberGroupBuilder.cs
new file mode 100644
index 0000000000..bfd7f30a14
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/MemberGroupBuilder.cs
@@ -0,0 +1,99 @@
+using System;
+using Umbraco.Core.Models;
+using Umbraco.Tests.Common.Builders.Interfaces;
+
+namespace Umbraco.Tests.Common.Builders
+{
+ public class MemberGroupBuilder
+ : BuilderBase,
+ IWithIdBuilder,
+ IWithKeyBuilder,
+ IWithCreatorIdBuilder,
+ IWithCreateDateBuilder,
+ IWithUpdateDateBuilder,
+ IWithNameBuilder
+ {
+ private GenericDictionaryBuilder _additionalDataBuilder;
+
+ private int? _id;
+ private Guid? _key;
+ private DateTime? _createDate;
+ private DateTime? _updateDate;
+ private string _name;
+ private int? _creatorId;
+
+ public GenericDictionaryBuilder AddAdditionalData()
+ {
+ var builder = new GenericDictionaryBuilder(this);
+ _additionalDataBuilder = builder;
+ return builder;
+ }
+
+ public override MemberGroup Build()
+ {
+ var id = _id ?? 1;
+ var key = _key ?? Guid.NewGuid();
+ var createDate = _createDate ?? DateTime.Now;
+ var updateDate = _updateDate ?? DateTime.Now;
+ var name = _name ?? Guid.NewGuid().ToString();
+ var creatorId = _creatorId ?? 1;
+
+ var memberGroup = new MemberGroup
+ {
+ Id = id,
+ Key = key,
+ CreateDate = createDate,
+ UpdateDate = updateDate,
+ Name = name,
+ CreatorId = creatorId,
+ };
+
+ if (_additionalDataBuilder != null)
+ {
+ var additionalData = _additionalDataBuilder.Build();
+ foreach (var kvp in additionalData)
+ {
+ memberGroup.AdditionalData.Add(kvp.Key, kvp.Value);
+ }
+ }
+
+ return memberGroup;
+ }
+
+ int? IWithIdBuilder.Id
+ {
+ get => _id;
+ set => _id = value;
+ }
+
+ Guid? IWithKeyBuilder.Key
+ {
+ get => _key;
+ set => _key = value;
+ }
+
+ int? IWithCreatorIdBuilder.CreatorId
+ {
+ get => _creatorId;
+ set => _creatorId = value;
+ }
+
+ DateTime? IWithCreateDateBuilder.CreateDate
+ {
+ get => _createDate;
+ set => _createDate = value;
+ }
+
+ DateTime? IWithUpdateDateBuilder.UpdateDate
+ {
+ get => _updateDate;
+ set => _updateDate = value;
+ }
+
+ string IWithNameBuilder.Name
+ {
+ get => _name;
+ set => _name = value;
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/MemberTypeBuilder.cs b/src/Umbraco.Tests.Common/Builders/MemberTypeBuilder.cs
new file mode 100644
index 0000000000..b01b8a1680
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/MemberTypeBuilder.cs
@@ -0,0 +1,198 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Core.Strings;
+using Umbraco.Tests.Common.Builders.Extensions;
+using Umbraco.Tests.Common.Builders.Interfaces;
+
+namespace Umbraco.Tests.Common.Builders
+{
+ public class MemberTypeBuilder
+ : ChildBuilderBase,
+ IWithIdBuilder,
+ IWithAliasBuilder,
+ IWithNameBuilder,
+ IWithParentIdBuilder,
+ IWithSortOrderBuilder,
+ IWithCreatorIdBuilder,
+ IWithDescriptionBuilder,
+ IWithIconBuilder,
+ IWithThumbnailBuilder,
+ IWithTrashedBuilder
+ {
+ private readonly List _propertyGroupBuilders = new List();
+
+ private int? _id;
+ private string _alias;
+ private string _name;
+ private int? _parentId;
+ private int? _sortOrder;
+ private int? _creatorId;
+ private string _description;
+ private string _icon;
+ private string _thumbnail;
+ private bool? _trashed;
+
+ public MemberTypeBuilder(MemberBuilder parentBuilder) : base(parentBuilder)
+ {
+ }
+
+ public MemberTypeBuilder WithMembershipPropertyGroup()
+ {
+ var builder = new PropertyGroupBuilder(this)
+ .WithName(Constants.Conventions.Member.StandardPropertiesGroupName)
+ .WithSortOrder(1)
+ .AddPropertyType()
+ .WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextArea)
+ .WithValueStorageType(ValueStorageType.Ntext)
+ .WithAlias(Constants.Conventions.Member.Comments)
+ .WithName(Constants.Conventions.Member.CommentsLabel)
+ .Done()
+ .AddPropertyType()
+ .WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.Boolean)
+ .WithValueStorageType(ValueStorageType.Integer)
+ .WithAlias(Constants.Conventions.Member.IsApproved)
+ .WithName(Constants.Conventions.Member.IsApprovedLabel)
+ .Done()
+ .AddPropertyType()
+ .WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.Boolean)
+ .WithValueStorageType(ValueStorageType.Integer)
+ .WithAlias(Constants.Conventions.Member.IsLockedOut)
+ .WithName(Constants.Conventions.Member.IsLockedOutLabel)
+ .Done()
+ .AddPropertyType()
+ .WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.Label)
+ .WithValueStorageType(ValueStorageType.Date)
+ .WithAlias(Constants.Conventions.Member.LastLoginDate)
+ .WithName(Constants.Conventions.Member.LastLoginDateLabel)
+ .Done()
+ .AddPropertyType()
+ .WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.Label)
+ .WithValueStorageType(ValueStorageType.Date)
+ .WithAlias(Constants.Conventions.Member.LastPasswordChangeDate)
+ .WithName(Constants.Conventions.Member.LastPasswordChangeDateLabel)
+ .Done()
+ .AddPropertyType()
+ .WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.Label)
+ .WithValueStorageType(ValueStorageType.Date)
+ .WithAlias(Constants.Conventions.Member.LastLockoutDate)
+ .WithName(Constants.Conventions.Member.LastLockoutDateLabel)
+ .Done()
+ .AddPropertyType()
+ .WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.Label)
+ .WithValueStorageType(ValueStorageType.Integer)
+ .WithAlias(Constants.Conventions.Member.FailedPasswordAttempts)
+ .WithName(Constants.Conventions.Member.FailedPasswordAttemptsLabel)
+ .Done();
+ _propertyGroupBuilders.Add(builder);
+ return this;
+ }
+
+ public PropertyGroupBuilder AddPropertyGroup()
+ {
+ var builder = new PropertyGroupBuilder(this);
+ _propertyGroupBuilders.Add(builder);
+ return builder;
+ }
+
+ public override MemberType Build()
+ {
+ var id = _id ?? 1;
+ var name = _name ?? Guid.NewGuid().ToString();
+ var alias = _alias ?? name.ToCamelCase();
+ var parentId = _parentId ?? -1;
+ var sortOrder = _sortOrder ?? 0;
+ var description = _description ?? string.Empty;
+ var icon = _icon ?? string.Empty;
+ var thumbnail = _thumbnail ?? string.Empty;
+ var creatorId = _creatorId ?? 0;
+ var trashed = _trashed ?? false;
+
+ var shortStringHelper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig());
+
+ var memberType = new MemberType(shortStringHelper, parentId)
+ {
+ Id = id,
+ Alias = alias,
+ Name = name,
+ SortOrder = sortOrder,
+ Description = description,
+ Icon = icon,
+ Thumbnail = thumbnail,
+ CreatorId = creatorId,
+ Trashed = trashed,
+ };
+
+ foreach (var propertyGroup in _propertyGroupBuilders.Select(x => x.Build()))
+ {
+ memberType.PropertyGroups.Add(propertyGroup);
+ }
+
+ memberType.ResetDirtyProperties(false);
+
+ return memberType;
+ }
+
+ int? IWithIdBuilder.Id
+ {
+ get => _id;
+ set => _id = value;
+ }
+
+ string IWithAliasBuilder.Alias
+ {
+ get => _alias;
+ set => _alias = value;
+ }
+
+ string IWithNameBuilder.Name
+ {
+ get => _name;
+ set => _name = value;
+ }
+
+ int? IWithParentIdBuilder.ParentId
+ {
+ get => _parentId;
+ set => _parentId = value;
+ }
+
+ int? IWithSortOrderBuilder.SortOrder
+ {
+ get => _sortOrder;
+ set => _sortOrder = value;
+ }
+
+ int? IWithCreatorIdBuilder.CreatorId
+ {
+ get => _creatorId;
+ set => _creatorId = value;
+ }
+
+ string IWithDescriptionBuilder.Description
+ {
+ get => _description;
+ set => _description = value;
+ }
+
+ string IWithIconBuilder.Icon
+ {
+ get => _icon;
+ set => _icon = value;
+ }
+
+ string IWithThumbnailBuilder.Thumbnail
+ {
+ get => _thumbnail;
+ set => _thumbnail = value;
+ }
+
+ bool? IWithTrashedBuilder.Trashed
+ {
+ get => _trashed;
+ set => _trashed = value;
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/PropertyGroupBuilder.cs b/src/Umbraco.Tests.Common/Builders/PropertyGroupBuilder.cs
new file mode 100644
index 0000000000..5f6fe12dff
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/PropertyGroupBuilder.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Umbraco.Core.Models;
+using Umbraco.Tests.Common.Builders.Interfaces;
+
+namespace Umbraco.Tests.Common.Builders
+{
+ public class PropertyGroupBuilder
+ : ChildBuilderBase, // TODO: likely want to generalise this, so can use for document and media types too.
+ IWithNameBuilder,
+ IWithSortOrderBuilder
+ {
+ private readonly List _propertyTypeBuilders = new List();
+
+ private string _name;
+ private int? _sortOrder;
+
+ public PropertyGroupBuilder(MemberTypeBuilder parentBuilder) : base(parentBuilder)
+ {
+ }
+
+ public PropertyTypeBuilder AddPropertyType()
+ {
+ var builder = new PropertyTypeBuilder(this);
+ _propertyTypeBuilders.Add(builder);
+ return builder;
+ }
+
+ public override PropertyGroup Build()
+ {
+ var name = _name ?? Guid.NewGuid().ToString();
+ var sortOrder = _sortOrder ?? 0;
+
+ var properties = new PropertyTypeCollection(false);
+ foreach (var propertyType in _propertyTypeBuilders.Select(x => x.Build()))
+ {
+ properties.Add(propertyType);
+ }
+
+ return new PropertyGroup(properties)
+ {
+ Name = name,
+ SortOrder = sortOrder,
+ };
+ }
+
+ string IWithNameBuilder.Name
+ {
+ get => _name;
+ set => _name = value;
+ }
+
+ int? IWithSortOrderBuilder.SortOrder
+ {
+ get => _sortOrder;
+ set => _sortOrder = value;
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/PropertyTypeBuilder.cs b/src/Umbraco.Tests.Common/Builders/PropertyTypeBuilder.cs
new file mode 100644
index 0000000000..955e2fca4c
--- /dev/null
+++ b/src/Umbraco.Tests.Common/Builders/PropertyTypeBuilder.cs
@@ -0,0 +1,93 @@
+using System;
+using Moq;
+using Umbraco.Core.Models;
+using Umbraco.Core.Strings;
+using Umbraco.Tests.Common.Builders.Extensions;
+using Umbraco.Tests.Common.Builders.Interfaces;
+
+namespace Umbraco.Tests.Common.Builders
+{
+ public class PropertyTypeBuilder
+ : ChildBuilderBase,
+ IWithAliasBuilder,
+ IWithNameBuilder,
+ IWithSortOrderBuilder,
+ IWithDescriptionBuilder
+ {
+ private string _propertyEditorAlias;
+ private ValueStorageType? _valueStorageType;
+ private string _alias;
+ private string _name;
+ private int? _sortOrder;
+ private string _description;
+ private int? _dataTypeId;
+
+ public PropertyTypeBuilder(PropertyGroupBuilder parentBuilder) : base(parentBuilder)
+ {
+ }
+
+ public PropertyTypeBuilder WithPropertyEditorAlias(string propertyEditorAlias)
+ {
+ _propertyEditorAlias = propertyEditorAlias;
+ return this;
+ }
+
+ public PropertyTypeBuilder WithValueStorageType(ValueStorageType valueStorageType)
+ {
+ _valueStorageType = valueStorageType;
+ return this;
+ }
+
+ public PropertyTypeBuilder WithDataTypeId(int dataTypeId)
+ {
+ _dataTypeId = dataTypeId;
+ return this;
+ }
+
+ public override PropertyType Build()
+ {
+ var propertyEditorAlias = _propertyEditorAlias ?? Guid.NewGuid().ToString().ToCamelCase();
+ var valueStorageType = _valueStorageType ?? ValueStorageType.Ntext;
+ var name = _name ?? Guid.NewGuid().ToString();
+ var alias = _alias ?? name.ToCamelCase();
+ var sortOrder = _sortOrder ?? 0;
+ var dataTypeId = _dataTypeId ?? 0;
+ var description = _description ?? string.Empty;
+
+ var shortStringHelper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig());
+
+ return new PropertyType(shortStringHelper, propertyEditorAlias, valueStorageType)
+ {
+ Alias = alias,
+ Name = name,
+ SortOrder = sortOrder,
+ DataTypeId = dataTypeId,
+ Description = description,
+ };
+ }
+
+ string IWithAliasBuilder.Alias
+ {
+ get => _alias;
+ set => _alias = value;
+ }
+
+ string IWithNameBuilder.Name
+ {
+ get => _name;
+ set => _name = value;
+ }
+
+ int? IWithSortOrderBuilder.SortOrder
+ {
+ get => _sortOrder;
+ set => _sortOrder = value;
+ }
+
+ string IWithDescriptionBuilder.Description
+ {
+ get => _description;
+ set => _description = value;
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.Common/Builders/UserBuilder.cs b/src/Umbraco.Tests.Common/Builders/UserBuilder.cs
index 07ab8ef3f7..cb24e0e8dc 100644
--- a/src/Umbraco.Tests.Common/Builders/UserBuilder.cs
+++ b/src/Umbraco.Tests.Common/Builders/UserBuilder.cs
@@ -1,16 +1,22 @@
-using System;
-using System.Linq;
-using Moq;
-using Umbraco.Core;
-using Umbraco.Core.Configuration;
+using Umbraco.Configuration.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Tests.Common.Builders.Interfaces;
namespace Umbraco.Tests.Common.Builders
{
- public class UserBuilder
- : BuilderBase,
- IWithIdBuilder
+
+ public class UserBuilder : UserBuilder