Merge branch 'netcore/feature/AB5822-smidge-implementation' into netcore/feature/ab5819-initialize-composers
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Configuration.Models
|
||||
/// </summary>
|
||||
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!
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
/// </remarks>
|
||||
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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -23,5 +23,12 @@
|
||||
/// Gets the segment.
|
||||
/// </summary>
|
||||
public string Segment { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the segment for the content item
|
||||
/// </summary>
|
||||
/// <param name="contentId"></param>
|
||||
/// <returns></returns>
|
||||
public virtual string GetSegment(int contentId) => Segment;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ namespace Umbraco.Core.Services
|
||||
IEnumerable<string> GetAllRoles();
|
||||
IEnumerable<string> GetAllRoles(int memberId);
|
||||
IEnumerable<string> GetAllRoles(string username);
|
||||
IEnumerable<int> GetAllRolesIds();
|
||||
IEnumerable<int> GetAllRolesIds(int memberId);
|
||||
IEnumerable<int> GetAllRolesIds(string username);
|
||||
IEnumerable<T> GetMembersInRole(string roleName);
|
||||
IEnumerable<T> FindMembersInRole(string roleName, string usernameToMatch, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith);
|
||||
bool DeleteRole(string roleName, bool throwIfBeingUsed);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the extension of the path or URL
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns>Extension of the file</returns>
|
||||
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 = @"(?<extension>\.[^\.\?]+)(\?.*|$)";
|
||||
var match = Regex.Match(file, pattern);
|
||||
return match.Success
|
||||
? match.Groups["extension"].Value
|
||||
: string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
@@ -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" });
|
||||
}
|
||||
|
||||
@@ -169,6 +169,7 @@ namespace Umbraco.Core.Migrations.Upgrade
|
||||
.As("{0576E786-5C30-4000-B969-302B61E90CA3}");
|
||||
|
||||
To<FixLanguageIsoCodeLength>("{48AD6CCD-C7A4-4305-A8AB-38728AD23FC5}");
|
||||
To<AddPackagesSectionAccess>("{DF470D86-E5CA-42AC-9780-9D28070E25F9}");
|
||||
|
||||
// finish migrating from v7 - recreate all keys and indexes
|
||||
To<CreateKeysAndIndexes>("{3F9764F5-73D0-4D45-8804-1240A66E43A2}");
|
||||
|
||||
@@ -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'");
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -958,6 +958,35 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetAllRolesIds()
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
scope.ReadLock(Constants.Locks.MemberTree);
|
||||
return _memberGroupRepository.GetMany().Select(x => x.Id).Distinct();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<int> 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<int> 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<IMember> GetMembersInRole(string roleName)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
|
||||
@@ -368,7 +368,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
/// <returns></returns>
|
||||
public string GetDefaultMemberType()
|
||||
{
|
||||
return "writer";
|
||||
return Constants.Security.WriterGroupAlias;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -355,6 +355,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
|
||||
private static IEnumerable<IPublishedContent> GetByXPath(XPathNodeIterator iterator)
|
||||
{
|
||||
iterator = iterator.Clone();
|
||||
while (iterator.MoveNext())
|
||||
{
|
||||
var xnav = iterator.Current as NavigableNavigator;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,6 @@ namespace Umbraco.Tests.Common.Builders
|
||||
_parentBuilder = parentBuilder;
|
||||
}
|
||||
|
||||
|
||||
public TParent Done()
|
||||
{
|
||||
return _parentBuilder;
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Umbraco.Tests.Common.Builders
|
||||
|
||||
public override IConfigurationEditor Build()
|
||||
{
|
||||
var defaultConfiguration = _defaultConfiguration ?? new Dictionary<string, object>();
|
||||
var defaultConfiguration = _defaultConfiguration ?? new Dictionary<string, object>();
|
||||
|
||||
return new ConfigurationEditor()
|
||||
{
|
||||
|
||||
@@ -8,10 +8,16 @@ namespace Umbraco.Tests.Common.Builders
|
||||
: BuilderBase<DataType>,
|
||||
IWithIdBuilder,
|
||||
IWithKeyBuilder,
|
||||
IWithCreatorIdBuilder,
|
||||
IWithCreateDateBuilder,
|
||||
IWithUpdateDateBuilder,
|
||||
IWithDeleteDateBuilder,
|
||||
IWithNameBuilder
|
||||
IWithNameBuilder,
|
||||
IWithParentIdBuilder,
|
||||
IWithTrashedBuilder,
|
||||
IWithLevelBuilder,
|
||||
IWithPathBuilder,
|
||||
IWithSortOrderBuilder
|
||||
{
|
||||
private readonly DataEditorBuilder<DataTypeBuilder> _dataEditorBuilder;
|
||||
private int? _id;
|
||||
@@ -34,54 +40,18 @@ namespace Umbraco.Tests.Common.Builders
|
||||
_dataEditorBuilder = new DataEditorBuilder<DataTypeBuilder>(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<DataTypeBuilder> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace Umbraco.Tests.Common.Builders
|
||||
private DateTime? _updateDate;
|
||||
private string _value;
|
||||
|
||||
|
||||
public DictionaryTranslationBuilder(DictionaryItemBuilder parentBuilder) : base(parentBuilder)
|
||||
{
|
||||
_languageBuilder = new LanguageBuilder<DictionaryTranslationBuilder>(this);
|
||||
|
||||
@@ -12,6 +12,13 @@ namespace Umbraco.Tests.Common.Builders.Extensions
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static T WithCreatorId<T>(this T builder, int creatorId)
|
||||
where T : IWithCreatorIdBuilder
|
||||
{
|
||||
builder.CreatorId = creatorId;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static T WithCreateDate<T>(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<T>(this T builder, int parentId)
|
||||
where T : IWithParentIdBuilder
|
||||
{
|
||||
builder.ParentId = parentId;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static T WithTrashed<T>(this T builder, bool trashed)
|
||||
where T : IWithTrashedBuilder
|
||||
{
|
||||
builder.Trashed = trashed;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static T WithLevel<T>(this T builder, int level)
|
||||
where T : IWithLevelBuilder
|
||||
{
|
||||
builder.Level = level;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static T WithPath<T>(this T builder, string path)
|
||||
where T : IWithPathBuilder
|
||||
{
|
||||
builder.Path = path;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static T WithSortOrder<T>(this T builder, int sortOrder)
|
||||
where T : IWithSortOrderBuilder
|
||||
{
|
||||
builder.SortOrder = sortOrder;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static T WithDescription<T>(this T builder, string description)
|
||||
where T : IWithDescriptionBuilder
|
||||
{
|
||||
builder.Description = description;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static T WithIcon<T>(this T builder, string icon)
|
||||
where T : IWithIconBuilder
|
||||
{
|
||||
builder.Icon = icon;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static T WithThumbnail<T>(this T builder, string thumbnail)
|
||||
where T : IWithThumbnailBuilder
|
||||
{
|
||||
builder.Thumbnail = thumbnail;
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Tests.Common.Builders
|
||||
{
|
||||
public class GenericCollectionBuilder<TBuilder, T>
|
||||
: ChildBuilderBase<TBuilder, IEnumerable<T>>
|
||||
{
|
||||
private readonly IList<T> _collection;
|
||||
|
||||
public GenericCollectionBuilder(TBuilder parentBuilder) : base(parentBuilder)
|
||||
{
|
||||
_collection = new List<T>();
|
||||
}
|
||||
|
||||
public override IEnumerable<T> Build()
|
||||
{
|
||||
return _collection;
|
||||
}
|
||||
|
||||
public GenericCollectionBuilder<TBuilder, T> WithValue(T value)
|
||||
{
|
||||
_collection.Add(value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Tests.Common.Builders
|
||||
{
|
||||
public class GenericDictionaryBuilder<TBuilder, TKey, TValue>
|
||||
: ChildBuilderBase<TBuilder, IDictionary<TKey, TValue>>
|
||||
{
|
||||
private readonly IDictionary<TKey, TValue> _dictionary;
|
||||
|
||||
public GenericDictionaryBuilder(TBuilder parentBuilder) : base(parentBuilder)
|
||||
{
|
||||
_dictionary = new Dictionary<TKey, TValue>();
|
||||
}
|
||||
|
||||
public override IDictionary<TKey, TValue> Build()
|
||||
{
|
||||
return _dictionary;
|
||||
}
|
||||
|
||||
public GenericDictionaryBuilder<TBuilder, TKey, TValue> WithKeyValue(TKey key, TValue value)
|
||||
{
|
||||
_dictionary.Add(key, value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Tests.Common.Builders.Interfaces
|
||||
{
|
||||
public interface IWithApprovedBuilder
|
||||
{
|
||||
bool? Approved { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Tests.Common.Builders.Interfaces
|
||||
{
|
||||
public interface IWithCreatorIdBuilder
|
||||
{
|
||||
int? CreatorId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Tests.Common.Builders.Interfaces
|
||||
{
|
||||
public interface IWithDescriptionBuilder
|
||||
{
|
||||
string Description { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Tests.Common.Builders.Interfaces
|
||||
{
|
||||
public interface IWithIconBuilder
|
||||
{
|
||||
string Icon { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Tests.Common.Builders.Interfaces
|
||||
{
|
||||
public interface IWithLevelBuilder
|
||||
{
|
||||
int? Level { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Tests.Common.Builders.Interfaces
|
||||
{
|
||||
public interface IWithParentIdBuilder
|
||||
{
|
||||
int? ParentId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Tests.Common.Builders.Interfaces
|
||||
{
|
||||
public interface IWithPathBuilder
|
||||
{
|
||||
string Path { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Tests.Common.Builders.Interfaces
|
||||
{
|
||||
public interface IWithSortOrderBuilder
|
||||
{
|
||||
int? SortOrder { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Tests.Common.Builders.Interfaces
|
||||
{
|
||||
public interface IWithThumbnailBuilder
|
||||
{
|
||||
string Thumbnail { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Tests.Common.Builders.Interfaces
|
||||
{
|
||||
public interface IWithTrashedBuilder
|
||||
{
|
||||
bool? Trashed { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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<Member>,
|
||||
IWithIdBuilder,
|
||||
IWithKeyBuilder,
|
||||
IWithCreatorIdBuilder,
|
||||
IWithCreateDateBuilder,
|
||||
IWithUpdateDateBuilder,
|
||||
IWithNameBuilder,
|
||||
IWithTrashedBuilder,
|
||||
IWithLevelBuilder,
|
||||
IWithPathBuilder,
|
||||
IWithSortOrderBuilder
|
||||
{
|
||||
private MemberTypeBuilder _memberTypeBuilder;
|
||||
private GenericCollectionBuilder<MemberBuilder, string> _memberGroupsBuilder;
|
||||
private GenericDictionaryBuilder<MemberBuilder, string, object> _additionalDataBuilder;
|
||||
private GenericDictionaryBuilder<MemberBuilder, string, object> _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<MemberBuilder, string> AddMemberGroups()
|
||||
{
|
||||
var builder = new GenericCollectionBuilder<MemberBuilder, string>(this);
|
||||
_memberGroupsBuilder = builder;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public GenericDictionaryBuilder<MemberBuilder, string, object> AddAdditionalData()
|
||||
{
|
||||
var builder = new GenericDictionaryBuilder<MemberBuilder, string, object>(this);
|
||||
_additionalDataBuilder = builder;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public GenericDictionaryBuilder<MemberBuilder, string, object> AddPropertyData()
|
||||
{
|
||||
var builder = new GenericDictionaryBuilder<MemberBuilder, string, object>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<MemberGroup>,
|
||||
IWithIdBuilder,
|
||||
IWithKeyBuilder,
|
||||
IWithCreatorIdBuilder,
|
||||
IWithCreateDateBuilder,
|
||||
IWithUpdateDateBuilder,
|
||||
IWithNameBuilder
|
||||
{
|
||||
private GenericDictionaryBuilder<MemberGroupBuilder, string, object> _additionalDataBuilder;
|
||||
|
||||
private int? _id;
|
||||
private Guid? _key;
|
||||
private DateTime? _createDate;
|
||||
private DateTime? _updateDate;
|
||||
private string _name;
|
||||
private int? _creatorId;
|
||||
|
||||
public GenericDictionaryBuilder<MemberGroupBuilder, string, object> AddAdditionalData()
|
||||
{
|
||||
var builder = new GenericDictionaryBuilder<MemberGroupBuilder, string, object>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<MemberBuilder, MemberType>,
|
||||
IWithIdBuilder,
|
||||
IWithAliasBuilder,
|
||||
IWithNameBuilder,
|
||||
IWithParentIdBuilder,
|
||||
IWithSortOrderBuilder,
|
||||
IWithCreatorIdBuilder,
|
||||
IWithDescriptionBuilder,
|
||||
IWithIconBuilder,
|
||||
IWithThumbnailBuilder,
|
||||
IWithTrashedBuilder
|
||||
{
|
||||
private readonly List<PropertyGroupBuilder> _propertyGroupBuilders = new List<PropertyGroupBuilder>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<MemberTypeBuilder, PropertyGroup>, // TODO: likely want to generalise this, so can use for document and media types too.
|
||||
IWithNameBuilder,
|
||||
IWithSortOrderBuilder
|
||||
{
|
||||
private readonly List<PropertyTypeBuilder> _propertyTypeBuilders = new List<PropertyTypeBuilder>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<PropertyGroupBuilder, PropertyType>,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<User>,
|
||||
IWithIdBuilder
|
||||
|
||||
public class UserBuilder : UserBuilder<object>
|
||||
{
|
||||
public UserBuilder() : base(null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class UserBuilder<TParent>
|
||||
: ChildBuilderBase<TParent, User>,
|
||||
IWithIdBuilder,
|
||||
IWithNameBuilder,
|
||||
IWithApprovedBuilder
|
||||
{
|
||||
private int? _id;
|
||||
private string _language;
|
||||
@@ -20,63 +26,57 @@ namespace Umbraco.Tests.Common.Builders
|
||||
private bool? _isLockedOut;
|
||||
private string _email;
|
||||
private string _username;
|
||||
private string _defaultLang;
|
||||
private string _suffix = string.Empty;
|
||||
private string _defaultLang;
|
||||
|
||||
public UserBuilder WithDefaultUILanguage(string defaultLang)
|
||||
|
||||
public UserBuilder(TParent parentBuilder) : base(parentBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public UserBuilder<TParent> WithDefaultUILanguage(string defaultLang)
|
||||
{
|
||||
_defaultLang = defaultLang;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithLanguage(string language)
|
||||
public UserBuilder<TParent> WithLanguage(string language)
|
||||
{
|
||||
_language = language;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithApproved(bool approved)
|
||||
{
|
||||
_approved = approved;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithRawPassword(string rawPassword)
|
||||
public UserBuilder<TParent> WithRawPassword(string rawPassword)
|
||||
{
|
||||
_rawPassword = rawPassword;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithEmail(string email)
|
||||
public UserBuilder<TParent> WithEmail(string email)
|
||||
{
|
||||
_email = email;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithUsername(string username)
|
||||
public UserBuilder<TParent> WithUsername(string username)
|
||||
{
|
||||
_username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithLockedOut(bool isLockedOut)
|
||||
public UserBuilder<TParent> WithLockedOut(bool isLockedOut)
|
||||
{
|
||||
_isLockedOut = isLockedOut;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithName(string name)
|
||||
{
|
||||
_name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will suffix the name, email and username for testing
|
||||
/// </summary>
|
||||
/// <param name="suffix"></param>
|
||||
/// <returns></returns>
|
||||
public UserBuilder WithSuffix(string suffix)
|
||||
public UserBuilder<TParent> WithSuffix(string suffix)
|
||||
{
|
||||
_suffix = suffix;
|
||||
return this;
|
||||
@@ -84,16 +84,25 @@ namespace Umbraco.Tests.Common.Builders
|
||||
|
||||
public override User Build()
|
||||
{
|
||||
var globalSettings = Mock.Of<IGlobalSettings>(x => x.DefaultUILanguage == (_defaultLang ?? "en-US"));
|
||||
return new User(globalSettings,
|
||||
_name ?? "TestUser" + _suffix,
|
||||
_email ?? "test" + _suffix + "@test.com",
|
||||
_username ?? "TestUser" + _suffix,
|
||||
_rawPassword ?? "abcdefghijklmnopqrstuvwxyz")
|
||||
var globalSettings = new GlobalSettingsBuilder().WithDefaultUiLanguage(_defaultLang).Build();
|
||||
var name = _name ?? "TestUser" + _suffix;
|
||||
var email = _email ?? "test" + _suffix + "@test.com";
|
||||
var username = _username ?? "TestUser" + _suffix;
|
||||
var rawPassword = _rawPassword ?? "abcdefghijklmnopqrstuvwxyz";
|
||||
var language = _language ?? globalSettings.DefaultUILanguage;
|
||||
var isLockedOut = _isLockedOut ?? false;
|
||||
var approved = _approved ?? true;
|
||||
|
||||
return new User(
|
||||
globalSettings,
|
||||
name,
|
||||
email,
|
||||
username,
|
||||
rawPassword)
|
||||
{
|
||||
Language = _language ?? _defaultLang ?? "en-US",
|
||||
IsLockedOut = _isLockedOut ?? false,
|
||||
IsApproved = _approved ?? true
|
||||
Language = language,
|
||||
IsLockedOut = isLockedOut,
|
||||
IsApproved = approved
|
||||
};
|
||||
}
|
||||
|
||||
@@ -102,5 +111,17 @@ namespace Umbraco.Tests.Common.Builders
|
||||
get => _id;
|
||||
set => _id = value;
|
||||
}
|
||||
|
||||
string IWithNameBuilder.Name
|
||||
{
|
||||
get => _name;
|
||||
set => _name = value;
|
||||
}
|
||||
|
||||
bool? IWithApprovedBuilder.Approved
|
||||
{
|
||||
get => _approved;
|
||||
set => _approved = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,20 @@ using Umbraco.Tests.Common.Builders.Interfaces;
|
||||
|
||||
namespace Umbraco.Tests.Common.Builders
|
||||
{
|
||||
public class UserGroupBuilder
|
||||
: BuilderBase<IUserGroup>,
|
||||
IWithIdBuilder
|
||||
|
||||
public class UserGroupBuilder : UserGroupBuilder<object>
|
||||
{
|
||||
public UserGroupBuilder() : base(null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class UserGroupBuilder<TParent>
|
||||
: ChildBuilderBase<TParent, IUserGroup>,
|
||||
IWithIdBuilder,
|
||||
IWithIconBuilder,
|
||||
IWithAliasBuilder,
|
||||
IWithNameBuilder
|
||||
{
|
||||
private int? _startContentId;
|
||||
private int? _startMediaId;
|
||||
@@ -20,12 +31,16 @@ namespace Umbraco.Tests.Common.Builders
|
||||
private string _suffix;
|
||||
private int? _id;
|
||||
|
||||
public UserGroupBuilder(TParent parentBuilder) : base(parentBuilder)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will suffix the name and alias for testing
|
||||
/// </summary>
|
||||
/// <param name="suffix"></param>
|
||||
/// <returns></returns>
|
||||
public UserGroupBuilder WithSuffix(string suffix)
|
||||
public UserGroupBuilder<TParent> WithSuffix(string suffix)
|
||||
{
|
||||
_suffix = suffix;
|
||||
return this;
|
||||
@@ -61,5 +76,24 @@ namespace Umbraco.Tests.Common.Builders
|
||||
get => _id;
|
||||
set => _id = value;
|
||||
}
|
||||
|
||||
|
||||
string IWithIconBuilder.Icon
|
||||
{
|
||||
get => _icon;
|
||||
set => _icon = value;
|
||||
}
|
||||
|
||||
string IWithAliasBuilder.Alias
|
||||
{
|
||||
get => _alias;
|
||||
set => _alias = value;
|
||||
}
|
||||
|
||||
string IWithNameBuilder.Name
|
||||
{
|
||||
get => _name;
|
||||
set => _name = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Umbraco.Tests.Integration.Extensions
|
||||
// dynamically change the config status
|
||||
var umbVersion = app.ApplicationServices.GetRequiredService<IUmbracoVersion>();
|
||||
var config = app.ApplicationServices.GetRequiredService<IConfiguration>();
|
||||
config[GlobalSettings.Prefix + "ConfigurationStatus"] = umbVersion.SemanticVersion.ToString();
|
||||
config[Constants.Configuration.ConfigGlobalPrefix + "ConfigurationStatus"] = umbVersion.SemanticVersion.ToString();
|
||||
|
||||
// re-run the runtime level check
|
||||
var profilingLogger = app.ApplicationServices.GetRequiredService<IProfilingLogger>();
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace Umbraco.Tests.Integration
|
||||
|
||||
// Add it!
|
||||
services.AddUmbracoConfiguration(hostContext.Configuration);
|
||||
services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly);
|
||||
services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, out _);
|
||||
});
|
||||
|
||||
var host = await hostBuilder.StartAsync();
|
||||
@@ -136,7 +136,7 @@ namespace Umbraco.Tests.Integration
|
||||
|
||||
// Add it!
|
||||
services.AddUmbracoConfiguration(hostContext.Configuration);
|
||||
services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly);
|
||||
services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, out _);
|
||||
});
|
||||
|
||||
var host = await hostBuilder.StartAsync();
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
|
||||
// Add it!
|
||||
services.AddUmbracoConfiguration(hostContext.Configuration);
|
||||
services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly);
|
||||
services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, out _);
|
||||
});
|
||||
|
||||
var host = await hostBuilder.StartAsync();
|
||||
|
||||
@@ -4,18 +4,17 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Tests.Common.Builders;
|
||||
using Umbraco.Tests.Common.Builders.Extensions;
|
||||
|
||||
|
||||
namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Models
|
||||
{
|
||||
[TestFixture]
|
||||
public class DataTypeTests
|
||||
{
|
||||
|
||||
private readonly DataTypeBuilder _builder = new DataTypeBuilder();
|
||||
|
||||
[Test]
|
||||
public void Can_Deep_Clone()
|
||||
{
|
||||
var dtd = _builder
|
||||
var dtd = _builder
|
||||
.WithId(3123)
|
||||
.Build();
|
||||
|
||||
|
||||
+22
-26
@@ -3,28 +3,21 @@ using System.Diagnostics;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Serialization;
|
||||
using Umbraco.Tests.Common.Builders;
|
||||
using Umbraco.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Tests.Models
|
||||
namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Models
|
||||
{
|
||||
[TestFixture]
|
||||
public class MemberGroupTests
|
||||
{
|
||||
private readonly MemberGroupBuilder _builder = new MemberGroupBuilder();
|
||||
|
||||
[Test]
|
||||
public void Can_Deep_Clone()
|
||||
{
|
||||
// Arrange
|
||||
var group = new MemberGroup()
|
||||
{
|
||||
CreateDate = DateTime.Now,
|
||||
CreatorId = 4,
|
||||
Id = 6,
|
||||
Key = Guid.NewGuid(),
|
||||
UpdateDate = DateTime.Now,
|
||||
Name = "asdf"
|
||||
};
|
||||
group.AdditionalData.Add("test1", 123);
|
||||
group.AdditionalData.Add("test2", "hello");
|
||||
var group = BuildMemberGroup();
|
||||
|
||||
// Act
|
||||
var clone = (MemberGroup)group.DeepClone();
|
||||
@@ -44,29 +37,32 @@ namespace Umbraco.Tests.Models
|
||||
//This double verifies by reflection
|
||||
var allProps = clone.GetType().GetProperties();
|
||||
foreach (var propertyInfo in allProps)
|
||||
{
|
||||
Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(group, null));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Serialize_Without_Error()
|
||||
{
|
||||
var group = new MemberGroup()
|
||||
{
|
||||
CreateDate = DateTime.Now,
|
||||
CreatorId = 4,
|
||||
Id = 6,
|
||||
Key = Guid.NewGuid(),
|
||||
UpdateDate = DateTime.Now,
|
||||
Name = "asdf"
|
||||
};
|
||||
group.AdditionalData.Add("test1", 123);
|
||||
group.AdditionalData.Add("test2", "hello");
|
||||
var group = BuildMemberGroup();
|
||||
|
||||
var json = JsonConvert.SerializeObject(group);
|
||||
Debug.Print(json);
|
||||
}
|
||||
|
||||
private MemberGroup BuildMemberGroup()
|
||||
{
|
||||
return _builder
|
||||
.WithId(6)
|
||||
.WithKey(Guid.NewGuid())
|
||||
.WithName("Test Group")
|
||||
.WithCreatorId(4)
|
||||
.WithCreateDate(DateTime.Now)
|
||||
.WithUpdateDate(DateTime.Now)
|
||||
.AddAdditionalData()
|
||||
.WithKeyValue("test1", 123)
|
||||
.WithKeyValue("test2", "hello")
|
||||
.Done()
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Tests.Common.Builders;
|
||||
using Umbraco.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Models
|
||||
{
|
||||
[TestFixture]
|
||||
public class MemberTests
|
||||
{
|
||||
private readonly MemberBuilder _builder = new MemberBuilder();
|
||||
|
||||
[Test]
|
||||
public void Can_Deep_Clone()
|
||||
{
|
||||
// Arrange
|
||||
var member = BuildMember();
|
||||
|
||||
// Act
|
||||
var clone = (Member)member.DeepClone();
|
||||
|
||||
// Assert
|
||||
Assert.AreNotSame(clone, member);
|
||||
Assert.AreEqual(clone, member);
|
||||
Assert.AreEqual(clone.Id, member.Id);
|
||||
Assert.AreEqual(clone.VersionId, member.VersionId);
|
||||
Assert.AreEqual(clone.AdditionalData, member.AdditionalData);
|
||||
Assert.AreEqual(clone.ContentType, member.ContentType);
|
||||
Assert.AreEqual(clone.ContentTypeId, member.ContentTypeId);
|
||||
Assert.AreEqual(clone.CreateDate, member.CreateDate);
|
||||
Assert.AreEqual(clone.CreatorId, member.CreatorId);
|
||||
Assert.AreEqual(clone.Comments, member.Comments);
|
||||
Assert.AreEqual(clone.Key, member.Key);
|
||||
Assert.AreEqual(clone.FailedPasswordAttempts, member.FailedPasswordAttempts);
|
||||
Assert.AreEqual(clone.Level, member.Level);
|
||||
Assert.AreEqual(clone.Path, member.Path);
|
||||
Assert.AreEqual(clone.Groups, member.Groups);
|
||||
Assert.AreEqual(clone.Groups.Count(), member.Groups.Count());
|
||||
Assert.AreEqual(clone.IsApproved, member.IsApproved);
|
||||
Assert.AreEqual(clone.IsLockedOut, member.IsLockedOut);
|
||||
Assert.AreEqual(clone.SortOrder, member.SortOrder);
|
||||
Assert.AreEqual(clone.LastLockoutDate, member.LastLockoutDate);
|
||||
Assert.AreNotSame(clone.LastLoginDate, member.LastLoginDate);
|
||||
Assert.AreEqual(clone.LastPasswordChangeDate, member.LastPasswordChangeDate);
|
||||
Assert.AreEqual(clone.Trashed, member.Trashed);
|
||||
Assert.AreEqual(clone.UpdateDate, member.UpdateDate);
|
||||
Assert.AreEqual(clone.VersionId, member.VersionId);
|
||||
Assert.AreEqual(clone.RawPasswordValue, member.RawPasswordValue);
|
||||
Assert.AreNotSame(clone.Properties, member.Properties);
|
||||
Assert.AreEqual(clone.Properties.Count(), member.Properties.Count());
|
||||
for (var index = 0; index < member.Properties.Count; index++)
|
||||
{
|
||||
Assert.AreNotSame(clone.Properties[index], member.Properties[index]);
|
||||
Assert.AreEqual(clone.Properties[index], member.Properties[index]);
|
||||
}
|
||||
|
||||
// this can be the same, it is immutable
|
||||
Assert.AreSame(clone.ContentType, member.ContentType);
|
||||
|
||||
//This double verifies by reflection
|
||||
var allProps = clone.GetType().GetProperties();
|
||||
foreach (var propertyInfo in allProps)
|
||||
Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(member, null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Serialize_Without_Error()
|
||||
{
|
||||
var member = BuildMember();
|
||||
|
||||
var json = JsonConvert.SerializeObject(member);
|
||||
Debug.Print(json);
|
||||
}
|
||||
|
||||
private Member BuildMember()
|
||||
{
|
||||
return _builder
|
||||
.AddMemberType()
|
||||
.WithId(99)
|
||||
.WithAlias("memberType")
|
||||
.WithName("Member Type")
|
||||
.WithMembershipPropertyGroup()
|
||||
.AddPropertyGroup()
|
||||
.WithName("Content")
|
||||
.WithSortOrder(1)
|
||||
.AddPropertyType()
|
||||
.WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox)
|
||||
.WithValueStorageType(ValueStorageType.Nvarchar)
|
||||
.WithAlias("title")
|
||||
.WithName("Title")
|
||||
.WithSortOrder(1)
|
||||
.WithDataTypeId(-88)
|
||||
.Done()
|
||||
.AddPropertyType()
|
||||
.WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox)
|
||||
.WithValueStorageType(ValueStorageType.Ntext)
|
||||
.WithAlias("bodyText")
|
||||
.WithName("Body text")
|
||||
.WithSortOrder(2)
|
||||
.WithDataTypeId(-87)
|
||||
.Done()
|
||||
.AddPropertyType()
|
||||
.WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox)
|
||||
.WithValueStorageType(ValueStorageType.Nvarchar)
|
||||
.WithAlias("author")
|
||||
.WithName("Author")
|
||||
.WithDescription("Name of the author")
|
||||
.WithSortOrder(3)
|
||||
.WithDataTypeId(-88)
|
||||
.Done()
|
||||
.Done()
|
||||
.Done()
|
||||
.WithId(10)
|
||||
.WithKey(Guid.NewGuid())
|
||||
.WithName("Fred")
|
||||
.WithUserName("fred")
|
||||
.WithRawPasswordValue("raw pass")
|
||||
.WithEmail("email@email.com")
|
||||
.WithCreatorId(22)
|
||||
.WithCreateDate(DateTime.Now)
|
||||
.WithUpdateDate(DateTime.Now)
|
||||
.WithFailedPasswordAttempts(22)
|
||||
.WithLevel(3)
|
||||
.WithPath("-1, 4, 10")
|
||||
.WithIsApproved(true)
|
||||
.WithIsLockedOut(true)
|
||||
.WithSortOrder(5)
|
||||
.WithTrashed(false)
|
||||
.AddMemberGroups()
|
||||
.WithValue("Group 1")
|
||||
.WithValue("Group 2")
|
||||
.Done()
|
||||
.AddAdditionalData()
|
||||
.WithKeyValue("test1", 123)
|
||||
.WithKeyValue("test2", "hello")
|
||||
.Done()
|
||||
.WithPropertyIdsIncrementingFrom(200)
|
||||
.AddPropertyData()
|
||||
.WithKeyValue("title", "Name member")
|
||||
.WithKeyValue("bodyText", "This is a subpage")
|
||||
.WithKeyValue("author", "John Doe")
|
||||
.Done()
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Tests.Common.Builders;
|
||||
using Umbraco.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Tests.UnitTests.Umbraco.Tests.Common.Builders
|
||||
{
|
||||
[TestFixture]
|
||||
public class DataTypeBuilderTests
|
||||
{
|
||||
[Test]
|
||||
public void Is_Built_Correctly()
|
||||
{
|
||||
// Arrange
|
||||
const int testId = 3123;
|
||||
|
||||
var builder = new DataTypeBuilder();
|
||||
|
||||
// Act
|
||||
var dtd = builder
|
||||
.WithId(testId)
|
||||
.Build();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(testId, dtd.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Tests.Common.Builders;
|
||||
using Umbraco.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Tests.UnitTests.Umbraco.Tests.Common.Builders
|
||||
{
|
||||
[TestFixture]
|
||||
public class MemberBuilderTests
|
||||
{
|
||||
private class PropertyTypeDetail
|
||||
{
|
||||
public string Alias { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
public int DataTypeId { get; set; }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Is_Built_Correctly()
|
||||
{
|
||||
// Arrange
|
||||
const int testMemberTypeId = 99;
|
||||
const string testMemberTypeAlias = "memberType";
|
||||
const string testMemberTypeName = "Member Type";
|
||||
const string testMemberTypePropertyGroupName = "Content";
|
||||
const int testId = 10;
|
||||
const string testName = "Fred";
|
||||
const string testUsername = "fred";
|
||||
const string testRawPasswordValue = "raw pass";
|
||||
const string testEmail = "email@email.com";
|
||||
const int testCreatorId = 22;
|
||||
const int testLevel = 3;
|
||||
const string testPath = "-1, 4, 10";
|
||||
const bool testIsApproved = true;
|
||||
const bool testIsLockedOut = true;
|
||||
const int testSortOrder = 5;
|
||||
const bool testTrashed = false;
|
||||
var testKey = Guid.NewGuid();
|
||||
var testCreateDate = DateTime.Now.AddHours(-1);
|
||||
var testUpdateDate = DateTime.Now;
|
||||
var testLastLockoutDate = DateTime.Now.AddHours(-2);
|
||||
var testLastLoginDate = DateTime.Now.AddHours(-3);
|
||||
var testLastPasswordChangeDate = DateTime.Now.AddHours(-4);
|
||||
var testPropertyType1 = new PropertyTypeDetail { Alias = "title", Name = "Title", SortOrder = 1, DataTypeId = -88 };
|
||||
var testPropertyType2 = new PropertyTypeDetail { Alias = "bodyText", Name = "Body Text", SortOrder = 2, DataTypeId = -87 };
|
||||
var testPropertyType3 = new PropertyTypeDetail { Alias = "author", Name = "Author", Description = "Writer of the article", SortOrder = 1, DataTypeId = -88 };
|
||||
var testGroups = new string[] { "group1", "group2" };
|
||||
var testPropertyData1 = new KeyValuePair<string, object>("title", "Name member");
|
||||
var testPropertyData2 = new KeyValuePair<string, object>("bodyText", "This is a subpage");
|
||||
var testPropertyData3 = new KeyValuePair<string, object>("author", "John Doe");
|
||||
var testAdditionalData1 = new KeyValuePair<string, object>("test1", 123);
|
||||
var testAdditionalData2 = new KeyValuePair<string, object>("test2", "hello");
|
||||
|
||||
var builder = new MemberBuilder();
|
||||
|
||||
// Act
|
||||
var member = builder
|
||||
.AddMemberType()
|
||||
.WithId(testMemberTypeId)
|
||||
.WithAlias(testMemberTypeAlias)
|
||||
.WithName(testMemberTypeName)
|
||||
.WithMembershipPropertyGroup()
|
||||
.AddPropertyGroup()
|
||||
.WithName(testMemberTypePropertyGroupName)
|
||||
.WithSortOrder(1)
|
||||
.AddPropertyType()
|
||||
.WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox)
|
||||
.WithValueStorageType(ValueStorageType.Nvarchar)
|
||||
.WithAlias(testPropertyType1.Alias)
|
||||
.WithName(testPropertyType1.Name)
|
||||
.WithSortOrder(testPropertyType1.SortOrder)
|
||||
.WithDataTypeId(testPropertyType1.DataTypeId)
|
||||
.Done()
|
||||
.AddPropertyType()
|
||||
.WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox)
|
||||
.WithValueStorageType(ValueStorageType.Ntext)
|
||||
.WithAlias(testPropertyType2.Alias)
|
||||
.WithName(testPropertyType2.Name)
|
||||
.WithSortOrder(testPropertyType2.SortOrder)
|
||||
.WithDataTypeId(testPropertyType2.DataTypeId)
|
||||
.Done()
|
||||
.AddPropertyType()
|
||||
.WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox)
|
||||
.WithValueStorageType(ValueStorageType.Nvarchar)
|
||||
.WithAlias(testPropertyType3.Alias)
|
||||
.WithName(testPropertyType3.Name)
|
||||
.WithDescription(testPropertyType3.Description)
|
||||
.WithSortOrder(testPropertyType3.SortOrder)
|
||||
.WithDataTypeId(testPropertyType3.DataTypeId)
|
||||
.Done()
|
||||
.Done()
|
||||
.Done()
|
||||
.WithId(testId)
|
||||
.WithKey(testKey)
|
||||
.WithName(testName)
|
||||
.WithUserName(testUsername)
|
||||
.WithRawPasswordValue(testRawPasswordValue)
|
||||
.WithEmail(testEmail)
|
||||
.WithCreatorId(testCreatorId)
|
||||
.WithCreateDate(testCreateDate)
|
||||
.WithUpdateDate(testUpdateDate)
|
||||
.WithFailedPasswordAttempts(22)
|
||||
.WithLevel(testLevel)
|
||||
.WithPath(testPath)
|
||||
.WithIsApproved(testIsApproved)
|
||||
.WithIsLockedOut(testIsLockedOut)
|
||||
.WithLastLockoutDate(testLastLockoutDate)
|
||||
.WithLastLoginDate(testLastLoginDate)
|
||||
.WithLastPasswordChangeDate(testLastPasswordChangeDate)
|
||||
.WithSortOrder(testSortOrder)
|
||||
.WithTrashed(testTrashed)
|
||||
.AddMemberGroups()
|
||||
.WithValue(testGroups[0])
|
||||
.WithValue(testGroups[1])
|
||||
.Done()
|
||||
.AddAdditionalData()
|
||||
.WithKeyValue(testAdditionalData1.Key, testAdditionalData1.Value)
|
||||
.WithKeyValue(testAdditionalData2.Key, testAdditionalData2.Value)
|
||||
.Done()
|
||||
.WithPropertyIdsIncrementingFrom(200)
|
||||
.AddPropertyData()
|
||||
.WithKeyValue(testPropertyData1.Key, testPropertyData1.Value)
|
||||
.WithKeyValue(testPropertyData2.Key, testPropertyData2.Value)
|
||||
.WithKeyValue(testPropertyData3.Key, testPropertyData3.Value)
|
||||
.Done()
|
||||
.Build();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(testMemberTypeId, member.ContentTypeId);
|
||||
Assert.AreEqual(testMemberTypeAlias, member.ContentType.Alias);
|
||||
Assert.AreEqual(testMemberTypeName, member.ContentType.Name);
|
||||
Assert.AreEqual(testId, member.Id);
|
||||
Assert.AreEqual(testKey, member.Key);
|
||||
Assert.AreEqual(testName, member.Name);
|
||||
Assert.AreEqual(testCreateDate, member.CreateDate);
|
||||
Assert.AreEqual(testUpdateDate, member.UpdateDate);
|
||||
Assert.AreEqual(testCreatorId, member.CreatorId);
|
||||
Assert.AreEqual(testGroups, member.Groups.ToArray());
|
||||
Assert.AreEqual(10, member.Properties.Count); // 7 from membership properties group, 3 custom
|
||||
Assert.AreEqual(testPropertyData1.Value, member.GetValue<string>(testPropertyData1.Key));
|
||||
Assert.AreEqual(testPropertyData2.Value, member.GetValue<string>(testPropertyData2.Key));
|
||||
Assert.AreEqual(testPropertyData3.Value, member.GetValue<string>(testPropertyData3.Key));
|
||||
Assert.AreEqual(2, member.AdditionalData.Count);
|
||||
Assert.AreEqual(testAdditionalData1.Value, member.AdditionalData[testAdditionalData1.Key]);
|
||||
Assert.AreEqual(testAdditionalData2.Value, member.AdditionalData[testAdditionalData2.Key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Tests.Common.Builders;
|
||||
using Umbraco.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Tests.UnitTests.Umbraco.Tests.Common.Builders
|
||||
{
|
||||
[TestFixture]
|
||||
public class MemberGroupBuilderTests
|
||||
{
|
||||
[Test]
|
||||
public void Is_Built_Correctly()
|
||||
{
|
||||
// Arrange
|
||||
const int testId = 6;
|
||||
const string testName = "Test Group";
|
||||
const int testCreatorId = 4;
|
||||
var testKey = Guid.NewGuid();
|
||||
var testCreateDate = DateTime.Now.AddHours(-1);
|
||||
var testUpdateDate = DateTime.Now;
|
||||
var testAdditionalData1 = new KeyValuePair<string, object>("test1", 123);
|
||||
var testAdditionalData2 = new KeyValuePair<string, object>("test2", "hello");
|
||||
|
||||
var builder = new MemberGroupBuilder();
|
||||
|
||||
// Act
|
||||
var group = builder
|
||||
.WithId(testId)
|
||||
.WithKey(testKey)
|
||||
.WithName(testName)
|
||||
.WithCreatorId(testCreatorId)
|
||||
.WithCreateDate(testCreateDate)
|
||||
.WithUpdateDate(testUpdateDate)
|
||||
.AddAdditionalData()
|
||||
.WithKeyValue(testAdditionalData1.Key, testAdditionalData1.Value)
|
||||
.WithKeyValue(testAdditionalData2.Key, testAdditionalData2.Value)
|
||||
.Done()
|
||||
.Build();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(testId, group.Id);
|
||||
Assert.AreEqual(testKey, group.Key);
|
||||
Assert.AreEqual(testName, group.Name);
|
||||
Assert.AreEqual(testCreateDate, group.CreateDate);
|
||||
Assert.AreEqual(testUpdateDate, group.UpdateDate);
|
||||
Assert.AreEqual(testCreatorId, group.CreatorId);
|
||||
Assert.AreEqual(3, group.AdditionalData.Count); // previousName is added as part of the MemberGroup construction, plus the 2 we've added.
|
||||
Assert.AreEqual(testAdditionalData1.Value, group.AdditionalData[testAdditionalData1.Key]);
|
||||
Assert.AreEqual(testAdditionalData2.Value, group.AdditionalData[testAdditionalData2.Key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ namespace Umbraco.Tests.Cache
|
||||
return "";
|
||||
});
|
||||
|
||||
Assert.AreEqual(counter, 1);
|
||||
Assert.AreEqual(1, counter);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -27,17 +27,17 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
|
||||
[Test]
|
||||
public virtual void Can_Set_Multiple()
|
||||
{
|
||||
Assert.AreEqual(ContentSettings.Error404Collection.Count(), 3);
|
||||
Assert.AreEqual(ContentSettings.Error404Collection.ElementAt(0).Culture, "default");
|
||||
Assert.AreEqual(ContentSettings.Error404Collection.ElementAt(0).ContentId, 1047);
|
||||
Assert.AreEqual(3, ContentSettings.Error404Collection.Count());
|
||||
Assert.AreEqual("default", ContentSettings.Error404Collection.ElementAt(0).Culture);
|
||||
Assert.AreEqual(1047, ContentSettings.Error404Collection.ElementAt(0).ContentId);
|
||||
Assert.IsTrue(ContentSettings.Error404Collection.ElementAt(0).HasContentId);
|
||||
Assert.IsFalse(ContentSettings.Error404Collection.ElementAt(0).HasContentKey);
|
||||
Assert.AreEqual(ContentSettings.Error404Collection.ElementAt(1).Culture, "en-US");
|
||||
Assert.AreEqual(ContentSettings.Error404Collection.ElementAt(1).ContentXPath, "$site/error [@name = 'error']");
|
||||
Assert.AreEqual("en-US", ContentSettings.Error404Collection.ElementAt(1).Culture);
|
||||
Assert.AreEqual("$site/error [@name = 'error']", ContentSettings.Error404Collection.ElementAt(1).ContentXPath);
|
||||
Assert.IsFalse(ContentSettings.Error404Collection.ElementAt(1).HasContentId);
|
||||
Assert.IsFalse(ContentSettings.Error404Collection.ElementAt(1).HasContentKey);
|
||||
Assert.AreEqual(ContentSettings.Error404Collection.ElementAt(2).Culture, "en-UK");
|
||||
Assert.AreEqual(ContentSettings.Error404Collection.ElementAt(2).ContentKey, new Guid("8560867F-B88F-4C74-A9A4-679D8E5B3BFC"));
|
||||
Assert.AreEqual("en-UK", ContentSettings.Error404Collection.ElementAt(2).Culture);
|
||||
Assert.AreEqual(new Guid("8560867F-B88F-4C74-A9A4-679D8E5B3BFC"), ContentSettings.Error404Collection.ElementAt(2).ContentKey);
|
||||
Assert.IsTrue(ContentSettings.Error404Collection.ElementAt(2).HasContentKey);
|
||||
Assert.IsFalse(ContentSettings.Error404Collection.ElementAt(2).HasContentId);
|
||||
}
|
||||
@@ -51,17 +51,17 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
|
||||
[Test]
|
||||
public virtual void ImageAutoFillProperties()
|
||||
{
|
||||
Assert.AreEqual(ContentSettings.ImageAutoFillProperties.Count(), 2);
|
||||
Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(0).Alias, "umbracoFile");
|
||||
Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(0).WidthFieldAlias, "umbracoWidth");
|
||||
Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(0).HeightFieldAlias, "umbracoHeight");
|
||||
Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(0).LengthFieldAlias, "umbracoBytes");
|
||||
Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(0).ExtensionFieldAlias, "umbracoExtension");
|
||||
Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(1).Alias, "umbracoFile2");
|
||||
Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(1).WidthFieldAlias, "umbracoWidth2");
|
||||
Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(1).HeightFieldAlias, "umbracoHeight2");
|
||||
Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(1).LengthFieldAlias, "umbracoBytes2");
|
||||
Assert.AreEqual(ContentSettings.ImageAutoFillProperties.ElementAt(1).ExtensionFieldAlias, "umbracoExtension2");
|
||||
Assert.AreEqual(2, ContentSettings.ImageAutoFillProperties.Count());
|
||||
Assert.AreEqual("umbracoFile", ContentSettings.ImageAutoFillProperties.ElementAt(0).Alias);
|
||||
Assert.AreEqual("umbracoWidth", ContentSettings.ImageAutoFillProperties.ElementAt(0).WidthFieldAlias);
|
||||
Assert.AreEqual("umbracoHeight", ContentSettings.ImageAutoFillProperties.ElementAt(0).HeightFieldAlias);
|
||||
Assert.AreEqual("umbracoBytes", ContentSettings.ImageAutoFillProperties.ElementAt(0).LengthFieldAlias);
|
||||
Assert.AreEqual("umbracoExtension", ContentSettings.ImageAutoFillProperties.ElementAt(0).ExtensionFieldAlias);
|
||||
Assert.AreEqual("umbracoFile2", ContentSettings.ImageAutoFillProperties.ElementAt(1).Alias);
|
||||
Assert.AreEqual("umbracoWidth2", ContentSettings.ImageAutoFillProperties.ElementAt(1).WidthFieldAlias);
|
||||
Assert.AreEqual("umbracoHeight2", ContentSettings.ImageAutoFillProperties.ElementAt(1).HeightFieldAlias);
|
||||
Assert.AreEqual("umbracoBytes2", ContentSettings.ImageAutoFillProperties.ElementAt(1).LengthFieldAlias);
|
||||
Assert.AreEqual("umbracoExtension2", ContentSettings.ImageAutoFillProperties.ElementAt(1).ExtensionFieldAlias);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -116,7 +116,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
|
||||
Debug.WriteLine("AllowedContainsExtension: {0}", allowedContainsExtension);
|
||||
Debug.WriteLine("DisallowedContainsExtension: {0}", disallowedContainsExtension);
|
||||
|
||||
Assert.AreEqual(ContentSettings.IsFileAllowedForUpload(extension), expected);
|
||||
Assert.AreEqual(expected, ContentSettings.IsFileAllowedForUpload(extension));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Umbraco.Tests.CoreThings
|
||||
|
||||
var xmlNode = cdata.GetXmlNode(xdoc);
|
||||
|
||||
Assert.AreEqual(xmlNode.InnerText, "hello world");
|
||||
Assert.AreEqual("hello world", xmlNode.InnerText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -27,7 +27,7 @@ namespace Umbraco.Tests.CoreThings
|
||||
|
||||
var xmlNode = cdata.GetXmlNode(xdoc);
|
||||
|
||||
Assert.AreEqual(xmlNode.InnerText, "hello world");
|
||||
Assert.AreEqual("hello world", xmlNode.InnerText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -41,7 +41,7 @@ namespace Umbraco.Tests.CoreThings
|
||||
|
||||
var xmlNode = cdata.GetXmlNode(xdoc);
|
||||
|
||||
Assert.AreEqual(xmlNode.InnerText, "hello world");
|
||||
Assert.AreEqual("hello world", xmlNode.InnerText);
|
||||
Assert.AreEqual(xml, xdoc.OuterXml);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
|
||||
namespace Umbraco.Tests.Models
|
||||
{
|
||||
[TestFixture]
|
||||
public class MemberTests
|
||||
{
|
||||
[Test]
|
||||
public void Can_Deep_Clone()
|
||||
{
|
||||
// Arrange
|
||||
var memberType = MockedContentTypes.CreateSimpleMemberType("memberType", "Member Type");
|
||||
memberType.Id = 99;
|
||||
var member = MockedMember.CreateSimpleMember(memberType, "Name", "email@email.com", "pass", "user", Guid.NewGuid());
|
||||
var i = 200;
|
||||
foreach (var property in member.Properties)
|
||||
{
|
||||
property.Id = ++i;
|
||||
}
|
||||
member.Id = 10;
|
||||
member.CreateDate = DateTime.Now;
|
||||
member.CreatorId = 22;
|
||||
member.Comments = "comments";
|
||||
member.Key = Guid.NewGuid();
|
||||
member.FailedPasswordAttempts = 22;
|
||||
member.Level = 3;
|
||||
member.Path = "-1,4,10";
|
||||
member.Groups = new[] {"group1", "group2"};
|
||||
member.IsApproved = true;
|
||||
member.IsLockedOut = true;
|
||||
member.LastLockoutDate = DateTime.Now;
|
||||
member.LastLoginDate = DateTime.Now;
|
||||
member.LastPasswordChangeDate = DateTime.Now;
|
||||
member.RawPasswordValue = "raw pass";
|
||||
member.SortOrder = 5;
|
||||
member.Trashed = false;
|
||||
member.UpdateDate = DateTime.Now;
|
||||
member.AdditionalData.Add("test1", 123);
|
||||
member.AdditionalData.Add("test2", "hello");
|
||||
|
||||
// Act
|
||||
var clone = (Member)member.DeepClone();
|
||||
|
||||
// Assert
|
||||
Assert.AreNotSame(clone, member);
|
||||
Assert.AreEqual(clone, member);
|
||||
Assert.AreEqual(clone.Id, member.Id);
|
||||
Assert.AreEqual(clone.VersionId, member.VersionId);
|
||||
Assert.AreEqual(clone.AdditionalData, member.AdditionalData);
|
||||
Assert.AreEqual(clone.ContentType, member.ContentType);
|
||||
Assert.AreEqual(clone.ContentTypeId, member.ContentTypeId);
|
||||
Assert.AreEqual(clone.CreateDate, member.CreateDate);
|
||||
Assert.AreEqual(clone.CreatorId, member.CreatorId);
|
||||
Assert.AreEqual(clone.Comments, member.Comments);
|
||||
Assert.AreEqual(clone.Key, member.Key);
|
||||
Assert.AreEqual(clone.FailedPasswordAttempts, member.FailedPasswordAttempts);
|
||||
Assert.AreEqual(clone.Level, member.Level);
|
||||
Assert.AreEqual(clone.Path, member.Path);
|
||||
Assert.AreEqual(clone.Groups, member.Groups);
|
||||
Assert.AreEqual(clone.Groups.Count(), member.Groups.Count());
|
||||
Assert.AreEqual(clone.IsApproved, member.IsApproved);
|
||||
Assert.AreEqual(clone.IsLockedOut, member.IsLockedOut);
|
||||
Assert.AreEqual(clone.SortOrder, member.SortOrder);
|
||||
Assert.AreEqual(clone.LastLockoutDate, member.LastLockoutDate);
|
||||
Assert.AreNotSame(clone.LastLoginDate, member.LastLoginDate);
|
||||
Assert.AreEqual(clone.LastPasswordChangeDate, member.LastPasswordChangeDate);
|
||||
Assert.AreEqual(clone.Trashed, member.Trashed);
|
||||
Assert.AreEqual(clone.UpdateDate, member.UpdateDate);
|
||||
Assert.AreEqual(clone.VersionId, member.VersionId);
|
||||
Assert.AreEqual(clone.RawPasswordValue, member.RawPasswordValue);
|
||||
Assert.AreNotSame(clone.Properties, member.Properties);
|
||||
Assert.AreEqual(clone.Properties.Count(), member.Properties.Count());
|
||||
for (var index = 0; index < member.Properties.Count; index++)
|
||||
{
|
||||
Assert.AreNotSame(clone.Properties[index], member.Properties[index]);
|
||||
Assert.AreEqual(clone.Properties[index], member.Properties[index]);
|
||||
}
|
||||
|
||||
// this can be the same, it is immutable
|
||||
Assert.AreSame(clone.ContentType, member.ContentType);
|
||||
|
||||
//This double verifies by reflection
|
||||
var allProps = clone.GetType().GetProperties();
|
||||
foreach (var propertyInfo in allProps)
|
||||
{
|
||||
Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(member, null));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Serialize_Without_Error()
|
||||
{
|
||||
var memberType = MockedContentTypes.CreateSimpleMemberType("memberType", "Member Type");
|
||||
memberType.Id = 99;
|
||||
var member = MockedMember.CreateSimpleMember(memberType, "Name", "email@email.com", "pass", "user", Guid.NewGuid());
|
||||
var i = 200;
|
||||
foreach (var property in member.Properties)
|
||||
{
|
||||
property.Id = ++i;
|
||||
}
|
||||
member.Id = 10;
|
||||
member.CreateDate = DateTime.Now;
|
||||
member.CreatorId = 22;
|
||||
member.Comments = "comments";
|
||||
member.Key = Guid.NewGuid();
|
||||
member.FailedPasswordAttempts = 22;
|
||||
member.Level = 3;
|
||||
member.Path = "-1,4,10";
|
||||
member.Groups = new[] { "group1", "group2" };
|
||||
member.IsApproved = true;
|
||||
member.IsLockedOut = true;
|
||||
member.LastLockoutDate = DateTime.Now;
|
||||
member.LastLoginDate = DateTime.Now;
|
||||
member.LastPasswordChangeDate = DateTime.Now;
|
||||
member.RawPasswordValue = "raw pass";
|
||||
member.SortOrder = 5;
|
||||
member.Trashed = false;
|
||||
member.UpdateDate = DateTime.Now;
|
||||
member.AdditionalData.Add("test1", 123);
|
||||
member.AdditionalData.Add("test2", "hello");
|
||||
|
||||
var json = JsonConvert.SerializeObject(member);
|
||||
Debug.Print(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1693,7 +1693,7 @@ namespace Umbraco.Tests.Persistence
|
||||
{
|
||||
Assert.IsTrue(testReader.Read());
|
||||
|
||||
Assert.AreEqual(testReader.Depth, 0);
|
||||
Assert.AreEqual(0, testReader.Depth);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2067,7 +2067,7 @@ namespace Umbraco.Tests.Persistence
|
||||
{
|
||||
Assert.IsTrue(testReader.Read());
|
||||
|
||||
Assert.AreEqual(testReader.RecordsAffected, -1);
|
||||
Assert.AreEqual(-1, testReader.RecordsAffected);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -303,15 +303,13 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
stylesheet = repository.Get("missing.css");
|
||||
Assert.IsNull(stylesheet);
|
||||
|
||||
// fixed in 7.3 - 7.2.8 used to...
|
||||
Assert.Throws<UnauthorizedAccessException>(() =>
|
||||
{
|
||||
stylesheet = repository.Get("\\test-path-4.css"); // outside the filesystem, does not exist
|
||||
});
|
||||
Assert.Throws<UnauthorizedAccessException>(() =>
|
||||
{
|
||||
stylesheet = repository.Get("../packages.config"); // outside the filesystem, exists
|
||||
});
|
||||
// #7713 changes behaviour to return null when outside the filesystem
|
||||
// to accomodate changing the CSS path and not flooding the backoffice with errors
|
||||
stylesheet = repository.Get("\\test-path-4.css"); // outside the filesystem, does not exist
|
||||
Assert.IsNull(stylesheet);
|
||||
|
||||
stylesheet = repository.Get("../packages.config"); // outside the filesystem, exists
|
||||
Assert.IsNull(stylesheet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1325,6 +1325,18 @@ namespace Umbraco.Tests.PublishedContent
|
||||
AssertLinkedNode(child3.contentNode, 1, 3, -1, -1, -1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultipleCacheIteration()
|
||||
{
|
||||
//see https://github.com/umbraco/Umbraco-CMS/issues/7798
|
||||
this.Init(this.GetInvariantKits());
|
||||
var snapshot = this._snapshotService.CreatePublishedSnapshot(previewToken: null);
|
||||
this._snapshotAccessor.PublishedSnapshot = snapshot;
|
||||
|
||||
var items = snapshot.Content.GetByXPath("/root/itype");
|
||||
Assert.AreEqual(items.Count(), items.Count());
|
||||
}
|
||||
|
||||
private void AssertLinkedNode(ContentNode node, int parent, int prevSibling, int nextSibling, int firstChild, int lastChild)
|
||||
{
|
||||
Assert.AreEqual(parent, node.ParentContentId);
|
||||
|
||||
@@ -1811,7 +1811,7 @@ namespace Umbraco.Tests.Services
|
||||
ServiceContext.ContentService.Save(content2, Constants.Security.SuperUserId);
|
||||
Assert.IsTrue(ServiceContext.ContentService.SaveAndPublish(content2, userId: 0).Success);
|
||||
|
||||
var editorGroup = ServiceContext.UserService.GetUserGroupByAlias("editor");
|
||||
var editorGroup = ServiceContext.UserService.GetUserGroupByAlias(Constants.Security.EditorGroupAlias);
|
||||
editorGroup.StartContentId = content1.Id;
|
||||
ServiceContext.UserService.Save(editorGroup);
|
||||
|
||||
|
||||
@@ -151,7 +151,17 @@ namespace Umbraco.Tests.Services
|
||||
|
||||
Assert.AreEqual(3, found.Count());
|
||||
}
|
||||
[Test]
|
||||
public void Can_Get_All_Roles_IDs()
|
||||
{
|
||||
ServiceContext.MemberService.AddRole("MyTestRole1");
|
||||
ServiceContext.MemberService.AddRole("MyTestRole2");
|
||||
ServiceContext.MemberService.AddRole("MyTestRole3");
|
||||
|
||||
var found = ServiceContext.MemberService.GetAllRolesIds();
|
||||
|
||||
Assert.AreEqual(3, found.Count());
|
||||
}
|
||||
[Test]
|
||||
public void Can_Get_All_Roles_By_Member_Id()
|
||||
{
|
||||
@@ -170,7 +180,24 @@ namespace Umbraco.Tests.Services
|
||||
Assert.AreEqual(2, memberRoles.Count());
|
||||
|
||||
}
|
||||
[Test]
|
||||
public void Can_Get_All_Roles_Ids_By_Member_Id()
|
||||
{
|
||||
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
|
||||
ServiceContext.MemberTypeService.Save(memberType);
|
||||
IMember member = MockedMember.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
|
||||
ServiceContext.MemberService.Save(member);
|
||||
|
||||
ServiceContext.MemberService.AddRole("MyTestRole1");
|
||||
ServiceContext.MemberService.AddRole("MyTestRole2");
|
||||
ServiceContext.MemberService.AddRole("MyTestRole3");
|
||||
ServiceContext.MemberService.AssignRoles(new[] { member.Id }, new[] { "MyTestRole1", "MyTestRole2" });
|
||||
|
||||
var memberRoles = ServiceContext.MemberService.GetAllRolesIds(member.Id);
|
||||
|
||||
Assert.AreEqual(2, memberRoles.Count());
|
||||
|
||||
}
|
||||
[Test]
|
||||
public void Can_Get_All_Roles_By_Member_Username()
|
||||
{
|
||||
|
||||
@@ -60,6 +60,24 @@ namespace Umbraco.Tests.Strings
|
||||
Assert.AreEqual(stripped, result);
|
||||
}
|
||||
|
||||
[TestCase("../wtf.js?x=wtf", ".js")]
|
||||
[TestCase(".htaccess", ".htaccess")]
|
||||
[TestCase("path/to/file/image.png", ".png")]
|
||||
[TestCase("c:\\abc\\def\\ghi.jkl", ".jkl")]
|
||||
[TestCase("/root/folder.name/file.ext", ".ext")]
|
||||
[TestCase("http://www.domain.com/folder/name/file.txt", ".txt")]
|
||||
[TestCase("i/don't\\have\\an/extension", "")]
|
||||
[TestCase("https://some.where/path/to/file.ext?query=this&more=that", ".ext")]
|
||||
[TestCase("double_query.string/file.ext?query=abc?something.else", ".ext")]
|
||||
[TestCase("test.tar.gz", ".gz")]
|
||||
[TestCase("wierd.file,but._legal", "._legal")]
|
||||
[TestCase("one_char.x", ".x")]
|
||||
public void Get_File_Extension(string input, string result)
|
||||
{
|
||||
var extension = input.GetFileExtension();
|
||||
Assert.AreEqual(result, extension);
|
||||
}
|
||||
|
||||
[TestCase("'+alert(1234)+'", "+alert1234+")]
|
||||
[TestCase("'+alert(56+78)+'", "+alert56+78+")]
|
||||
[TestCase("{{file}}", "file")]
|
||||
|
||||
@@ -297,8 +297,6 @@
|
||||
<Compile Include="Models\ContentTypeTests.cs" />
|
||||
<Compile Include="Models\DeepCloneHelperTests.cs" />
|
||||
<Compile Include="Models\DictionaryTranslationTests.cs" />
|
||||
<Compile Include="Models\MemberGroupTests.cs" />
|
||||
<Compile Include="Models\MemberTests.cs" />
|
||||
<Compile Include="Models\PropertyGroupTests.cs" />
|
||||
<Compile Include="Models\PropertyTypeTests.cs" />
|
||||
<Compile Include="Models\RelationTests.cs" />
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Umbraco.Tests.Web.AngularIntegration
|
||||
string cookieToken, headerToken;
|
||||
AngularAntiForgeryHelper.GetTokens(out cookieToken, out headerToken);
|
||||
|
||||
Assert.AreEqual(true, AngularAntiForgeryHelper.ValidateTokens(cookieToken, headerToken));
|
||||
Assert.IsTrue(AngularAntiForgeryHelper.ValidateTokens(cookieToken, headerToken));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,7 +45,7 @@ namespace Umbraco.Tests.Web.AngularIntegration
|
||||
string cookieToken, headerToken;
|
||||
AngularAntiForgeryHelper.GetTokens(out cookieToken, out headerToken);
|
||||
|
||||
Assert.AreEqual(true, AngularAntiForgeryHelper.ValidateTokens(cookieToken, headerToken));
|
||||
Assert.IsTrue(AngularAntiForgeryHelper.ValidateTokens(cookieToken, headerToken));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,13 +52,25 @@ namespace Umbraco.Web.Common.Extensions
|
||||
/// <param name="webHostEnvironment"></param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddUmbracoCore(this IServiceCollection services, IWebHostEnvironment webHostEnvironment)
|
||||
{
|
||||
return services.AddUmbracoCore(webHostEnvironment,out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Umbraco Back Core requirements
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="webHostEnvironment"></param>
|
||||
/// <param name="factory"></param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddUmbracoCore(this IServiceCollection services, IWebHostEnvironment webHostEnvironment, out IFactory factory)
|
||||
{
|
||||
if (!UmbracoServiceProviderFactory.IsActive)
|
||||
throw new InvalidOperationException("Ensure to add UseUmbraco() in your Program.cs after ConfigureWebHostDefaults to enable Umbraco's service provider factory");
|
||||
|
||||
var umbContainer = UmbracoServiceProviderFactory.UmbracoContainer;
|
||||
|
||||
services.AddUmbracoCore(webHostEnvironment, umbContainer, Assembly.GetEntryAssembly());
|
||||
services.AddUmbracoCore(webHostEnvironment, umbContainer, Assembly.GetEntryAssembly(), out factory);
|
||||
|
||||
return services;
|
||||
}
|
||||
@@ -70,8 +82,9 @@ namespace Umbraco.Web.Common.Extensions
|
||||
/// <param name="webHostEnvironment"></param>
|
||||
/// <param name="umbContainer"></param>
|
||||
/// <param name="entryAssembly"></param>
|
||||
/// <param name="factory"></param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddUmbracoCore(this IServiceCollection services, IWebHostEnvironment webHostEnvironment, IRegister umbContainer, Assembly entryAssembly)
|
||||
public static IServiceCollection AddUmbracoCore(this IServiceCollection services, IWebHostEnvironment webHostEnvironment, IRegister umbContainer, Assembly entryAssembly, out IFactory factory)
|
||||
{
|
||||
if (services is null) throw new ArgumentNullException(nameof(services));
|
||||
var container = umbContainer;
|
||||
@@ -101,7 +114,7 @@ namespace Umbraco.Web.Common.Extensions
|
||||
backOfficeInfo,
|
||||
typeFinder);
|
||||
|
||||
var factory = coreRuntime.Configure(container);
|
||||
factory = coreRuntime.Configure(container);
|
||||
|
||||
return services;
|
||||
}
|
||||
@@ -159,8 +172,6 @@ namespace Umbraco.Web.Common.Extensions
|
||||
backOfficeInfo = new AspNetCoreBackOfficeInfo(globalSettings);
|
||||
profiler = GetWebProfiler(hostingEnvironment, httpContextAccessor);
|
||||
|
||||
Current.Initialize(logger, configs, ioHelper, hostingEnvironment, backOfficeInfo, profiler);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
"tinyMCE": false,
|
||||
"FileReader": false,
|
||||
"Umbraco": false,
|
||||
"Utilities": false,
|
||||
"window": false,
|
||||
"LazyLoad": false,
|
||||
"ActiveXObject": false,
|
||||
"Bloodhound": false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ module.exports = {
|
||||
installer: { files: "./src/installer/**/*.js", out: "umbraco.installer.js" },
|
||||
filters: { files: "./src/common/filters/**/*.js", out: "umbraco.filters.js" },
|
||||
resources: { files: "./src/common/resources/**/*.js", out: "umbraco.resources.js" },
|
||||
services: { files: "./src/common/services/**/*.js", out: "umbraco.services.js" },
|
||||
services: { files: ["./src/common/services/**/*.js", "./src/utilities.js"], out: "umbraco.services.js" },
|
||||
security: { files: "./src/common/interceptors/**/*.js", out: "umbraco.interceptors.js" },
|
||||
|
||||
|
||||
//the controllers for views
|
||||
controllers: {
|
||||
files: [
|
||||
|
||||
+25
-22
@@ -876,9 +876,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "5.7.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
|
||||
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
|
||||
"version": "5.7.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
|
||||
"integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
@@ -1035,9 +1035,9 @@
|
||||
"integrity": "sha1-avwuQ6e17/3ETYQHQ2EShSVo6A0="
|
||||
},
|
||||
"acorn": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
|
||||
"integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
|
||||
"integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-jsx": {
|
||||
@@ -2103,13 +2103,15 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
@@ -2122,9 +2124,10 @@
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
@@ -2416,7 +2419,7 @@
|
||||
},
|
||||
"callsites": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "http://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
|
||||
"integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
|
||||
"dev": true
|
||||
},
|
||||
@@ -2459,9 +2462,9 @@
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001002",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001002.tgz",
|
||||
"integrity": "sha512-pRuxPE8wdrWmVPKcDmJJiGBxr6lFJq4ivdSeo9FTmGj5Rb8NX3Mby2pARG57MXF15hYAhZ0nHV5XxT2ig4bz3g==",
|
||||
"version": "1.0.30001038",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001038.tgz",
|
||||
"integrity": "sha512-zii9quPo96XfOiRD4TrfYGs+QsGZpb2cGiMAzPjtf/hpFgB6zCPZgJb7I1+EATeMw/o+lG8FyRAnI+CWStHcaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"caseless": {
|
||||
@@ -6547,7 +6550,7 @@
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
|
||||
"integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=",
|
||||
"dev": true
|
||||
},
|
||||
@@ -6712,9 +6715,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "5.7.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
|
||||
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
|
||||
"version": "5.7.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
|
||||
"integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
@@ -6759,7 +6762,7 @@
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -12972,7 +12975,7 @@
|
||||
},
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
|
||||
"dev": true
|
||||
},
|
||||
@@ -14071,7 +14074,7 @@
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -14093,7 +14096,7 @@
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
|
||||
"integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=",
|
||||
"dev": true
|
||||
},
|
||||
@@ -14678,7 +14681,7 @@
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
"@babel/core": "7.6.4",
|
||||
"@babel/preset-env": "7.6.3",
|
||||
"autoprefixer": "9.6.5",
|
||||
"caniuse-lite": "^1.0.30001002",
|
||||
"caniuse-lite": "^1.0.30001037",
|
||||
"cssnano": "4.1.10",
|
||||
"fs": "0.0.2",
|
||||
"gulp": "4.0.2",
|
||||
|
||||
+14
-23
@@ -12,26 +12,25 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se
|
||||
|
||||
var sectionItemsWidth = [];
|
||||
var evts = [];
|
||||
var maxSections = 8;
|
||||
|
||||
//setup scope vars
|
||||
scope.maxSections = maxSections;
|
||||
scope.overflowingSections = 0;
|
||||
scope.sections = [];
|
||||
scope.visibleSections = 0;
|
||||
scope.currentSection = appState.getSectionState("currentSection");
|
||||
scope.showTray = false; //appState.getGlobalState("showTray");
|
||||
scope.showTray = false;
|
||||
scope.stickyNavigation = appState.getGlobalState("stickyNavigation");
|
||||
scope.needTray = false;
|
||||
|
||||
function loadSections() {
|
||||
sectionService.getSectionsForUser()
|
||||
.then(function (result) {
|
||||
scope.sections = result;
|
||||
scope.visibleSections = scope.sections.length;
|
||||
|
||||
// store the width of each section so we can hide/show them based on browser width
|
||||
// we store them because the sections get removed from the dom and then we
|
||||
// can't tell when to show them gain
|
||||
$timeout(function () {
|
||||
$("#applications .sections li").each(function (index) {
|
||||
$("#applications .sections li:not(:last)").each(function (index) {
|
||||
sectionItemsWidth.push($(this).outerWidth());
|
||||
});
|
||||
});
|
||||
@@ -42,25 +41,22 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se
|
||||
function calculateWidth() {
|
||||
$timeout(function () {
|
||||
//total width minus room for avatar, search, and help icon
|
||||
var windowWidth = $(window).width() - 150;
|
||||
var containerWidth = $(".umb-app-header").outerWidth() - $(".umb-app-header__actions").outerWidth();
|
||||
var trayToggleWidth = $("#applications .sections li.expand").outerWidth();
|
||||
var sectionsWidth = 0;
|
||||
scope.totalSections = scope.sections.length;
|
||||
scope.maxSections = maxSections;
|
||||
scope.overflowingSections = scope.maxSections - scope.totalSections;
|
||||
scope.needTray = scope.sections.length > scope.maxSections;
|
||||
|
||||
|
||||
// detect how many sections we can show on the screen
|
||||
for (var i = 0; i < sectionItemsWidth.length; i++) {
|
||||
var sectionItemWidth = sectionItemsWidth[i];
|
||||
sectionsWidth += sectionItemWidth;
|
||||
|
||||
if (sectionsWidth > windowWidth) {
|
||||
scope.needTray = true;
|
||||
scope.maxSections = i - 1;
|
||||
scope.overflowingSections = scope.maxSections - scope.totalSections;
|
||||
break;
|
||||
if (sectionsWidth + trayToggleWidth > containerWidth) {
|
||||
scope.visibleSections = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
scope.visibleSections = scope.sections.length;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -134,14 +130,9 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se
|
||||
};
|
||||
|
||||
scope.currentSectionInOverflow = function () {
|
||||
if (scope.overflowingSections === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var currentSection = scope.sections.filter(s => s.alias === scope.currentSection);
|
||||
|
||||
return (scope.sections.indexOf(currentSection[0]) >= scope.maxSections);
|
||||
|
||||
return currentSection.length > 0 && scope.sections.indexOf(currentSection[0]) > scope.visibleSections - 1;
|
||||
};
|
||||
|
||||
loadSections();
|
||||
|
||||
@@ -178,11 +178,11 @@ function angularHelper($q) {
|
||||
$valid: true,
|
||||
$submitted: false,
|
||||
$pending: undefined,
|
||||
$addControl: angular.noop,
|
||||
$removeControl: angular.noop,
|
||||
$setValidity: angular.noop,
|
||||
$setDirty: angular.noop,
|
||||
$setPristine: angular.noop,
|
||||
$addControl: Utilities.noop,
|
||||
$removeControl: Utilities.noop,
|
||||
$setValidity: Utilities.noop,
|
||||
$setDirty: Utilities.noop,
|
||||
$setPristine: Utilities.noop,
|
||||
$name: formName
|
||||
};
|
||||
}
|
||||
|
||||
@@ -578,30 +578,12 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
|
||||
if (args.action.metaData["actionView"]) {
|
||||
templateUrl = args.action.metaData["actionView"];
|
||||
}
|
||||
else {
|
||||
|
||||
//by convention we will look into the /views/{treetype}/{action}.html
|
||||
// for example: /views/content/create.html
|
||||
|
||||
//we will also check for a 'packageName' for the current tree, if it exists then the convention will be:
|
||||
// for example: /App_Plugins/{mypackage}/backoffice/{treetype}/create.html
|
||||
|
||||
else {
|
||||
var treeAlias = treeService.getTreeAlias(args.node);
|
||||
var packageTreeFolder = treeService.getTreePackageFolder(treeAlias);
|
||||
|
||||
if (!treeAlias) {
|
||||
throw "Could not get tree alias for node " + args.node.id;
|
||||
}
|
||||
|
||||
if (packageTreeFolder) {
|
||||
templateUrl = Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
|
||||
"/" + packageTreeFolder +
|
||||
"/backoffice/" + treeAlias + "/" + args.action.alias + ".html";
|
||||
}
|
||||
else {
|
||||
templateUrl = "views/" + treeAlias + "/" + args.action.alias + ".html";
|
||||
}
|
||||
|
||||
}
|
||||
templateUrl = this.getTreeTemplateUrl(treeAlias, args.action.alias);
|
||||
}
|
||||
|
||||
setMode("dialog");
|
||||
@@ -611,6 +593,31 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
|
||||
}
|
||||
|
||||
},
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.navigationService#getTreeTemplateUrl
|
||||
* @methodOf umbraco.services.navigationService
|
||||
*
|
||||
* @param {string} treeAlias the alias of the tree to look up
|
||||
* @param {string} action the view file name
|
||||
* @description
|
||||
* creates the templateUrl based on treeAlias and action
|
||||
* by convention we will look into the /views/{treetype}/{action}.html
|
||||
* for example: /views/content/create.html
|
||||
* we will also check for a 'packageName' for the current tree, if it exists then the convention will be:
|
||||
* for example: /App_Plugins/{mypackage}/backoffice/{treetype}/create.html
|
||||
*/
|
||||
getTreeTemplateUrl: function(treeAlias, action) {
|
||||
var packageTreeFolder = treeService.getTreePackageFolder(treeAlias);
|
||||
if (packageTreeFolder) {
|
||||
return Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
|
||||
"/" + packageTreeFolder +
|
||||
"/backoffice/" + treeAlias + "/" + action + ".html";
|
||||
}
|
||||
else {
|
||||
return "views/" + treeAlias + "/" + action + ".html";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
|
||||
@@ -365,12 +365,23 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.contentResource#downloadFile
|
||||
* @methodOf umbraco.resources.contentResource
|
||||
*
|
||||
* @description
|
||||
* Downloads a file to the client using AJAX/XHR
|
||||
* Based on an implementation here: web.student.tuwien.ac.at/~e0427417/jsdownload.html
|
||||
* See https://stackoverflow.com/a/24129082/694494
|
||||
*
|
||||
* @param {string} httpPath the path (url) to the resource being downloaded
|
||||
* @returns {Promise} http promise object.
|
||||
*/
|
||||
downloadFile : function (httpPath) {
|
||||
|
||||
/**
|
||||
* Based on an implementation here: web.student.tuwien.ac.at/~e0427417/jsdownload.html
|
||||
* See https://stackoverflow.com/a/24129082/694494
|
||||
*/
|
||||
|
||||
// Use an arraybuffer
|
||||
return $http.get(httpPath, { responseType: 'arraybuffer' })
|
||||
.then(function (response) {
|
||||
|
||||
@@ -2,17 +2,16 @@ angular.module("umbraco.install").factory('installerService', function ($rootSco
|
||||
|
||||
var _status = {
|
||||
index: 0,
|
||||
current: undefined,
|
||||
steps: undefined,
|
||||
current: null,
|
||||
steps: null,
|
||||
loading: true,
|
||||
progress: "100%"
|
||||
};
|
||||
|
||||
var factTimer = undefined;
|
||||
var factTimer;
|
||||
var _installerModel = {
|
||||
installId: undefined,
|
||||
instructions: {
|
||||
}
|
||||
installId: null,
|
||||
instructions: {}
|
||||
};
|
||||
|
||||
//add to umbraco installer facts here
|
||||
@@ -304,7 +303,7 @@ angular.module("umbraco.install").factory('installerService', function ($rootSco
|
||||
},
|
||||
|
||||
switchToFeedback : function(){
|
||||
service.status.current = undefined;
|
||||
service.status.current = null;
|
||||
service.status.loading = true;
|
||||
service.status.configuring = false;
|
||||
|
||||
@@ -320,11 +319,11 @@ angular.module("umbraco.install").factory('installerService', function ($rootSco
|
||||
switchToConfiguration : function(){
|
||||
service.status.loading = false;
|
||||
service.status.configuring = true;
|
||||
service.status.feedback = undefined;
|
||||
service.status.fact = undefined;
|
||||
service.status.feedback = null;
|
||||
service.status.fact = null;
|
||||
|
||||
if(factTimer){
|
||||
clearInterval(factTimer);
|
||||
if (factTimer) {
|
||||
clearInterval(factTimer);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -335,8 +334,8 @@ angular.module("umbraco.install").factory('installerService', function ($rootSco
|
||||
service.status.feedback = "Redirecting you to Umbraco, please wait";
|
||||
service.status.loading = false;
|
||||
|
||||
if(factTimer){
|
||||
clearInterval(factTimer);
|
||||
if (factTimer) {
|
||||
clearInterval(factTimer);
|
||||
}
|
||||
|
||||
$timeout(function(){
|
||||
|
||||
@@ -4,18 +4,19 @@ angular.module("umbraco.install").controller("Umbraco.Installer.DataBaseControll
|
||||
$scope.invalidDbDns = false;
|
||||
|
||||
$scope.dbs = [
|
||||
{ name: 'Microsoft SQL Server Compact (SQL CE)', id: 0},
|
||||
{ name: 'Microsoft SQL Server', id: 1},
|
||||
{ name: 'Microsoft SQL Server Compact (SQL CE)', id: 0 },
|
||||
{ name: 'Microsoft SQL Server', id: 1 },
|
||||
{ name: 'Microsoft SQL Azure', id: 3 },
|
||||
{ name: 'Custom connection string', id: -1}
|
||||
{ name: 'Custom connection string', id: -1 }
|
||||
];
|
||||
|
||||
if ( installerService.status.current.model.dbType === undefined ) {
|
||||
if (angular.isUndefined(installerService.status.current.model.dbType) || installerService.status.current.model.dbType === null) {
|
||||
installerService.status.current.model.dbType = 0;
|
||||
}
|
||||
|
||||
$scope.validateAndForward = function(){
|
||||
if ( !$scope.checking && this.myForm.$valid ) {
|
||||
$scope.validateAndForward = function() {
|
||||
if (!$scope.checking && this.myForm.$valid)
|
||||
{
|
||||
$scope.checking = true;
|
||||
$scope.invalidDbDns = false;
|
||||
|
||||
@@ -23,9 +24,9 @@ angular.module("umbraco.install").controller("Umbraco.Installer.DataBaseControll
|
||||
|
||||
$http.post(
|
||||
Umbraco.Sys.ServerVariables.installApiBaseUrl + "PostValidateDatabaseConnection",
|
||||
model ).then( function( response ) {
|
||||
model).then(function(response) {
|
||||
|
||||
if ( response.data === true ) {
|
||||
if (response.data === true) {
|
||||
installerService.forward();
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
angular.module("umbraco.install").controller("Umbraco.Installer.MachineKeyController", function ($scope, installerService) {
|
||||
|
||||
|
||||
$scope.continue = function () {
|
||||
installerService.status.current.model = true;
|
||||
@@ -11,4 +10,4 @@
|
||||
installerService.forward();
|
||||
};
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<a href ng-click="setPackageAndContinue('00000000-0000-0000-0000-000000000000')"
|
||||
class="btn btn-link btn-link-reverse">
|
||||
<button type="button"
|
||||
class="btn btn-link btn-link-reverse"
|
||||
ng-click="setPackageAndContinue('00000000-0000-0000-0000-000000000000')">
|
||||
No thanks, I do not want to install a starter website
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -13,14 +13,14 @@ angular.module("umbraco.install").controller("Umbraco.Install.UserController", f
|
||||
$scope.passwordPattern = new RegExp(exp);
|
||||
}
|
||||
|
||||
$scope.validateAndInstall = function(){
|
||||
installerService.install();
|
||||
$scope.validateAndInstall = function() {
|
||||
installerService.install();
|
||||
};
|
||||
|
||||
$scope.validateAndForward = function(){
|
||||
if(this.myForm.$valid){
|
||||
installerService.forward();
|
||||
}
|
||||
if (this.myForm.$valid) {
|
||||
installerService.forward();
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,15 +17,17 @@
|
||||
.umb-button-group {
|
||||
|
||||
.umb-button__button {
|
||||
border-radius: @baseBorderRadius;
|
||||
border-radius: @baseBorderRadius 0 0 @baseBorderRadius;
|
||||
|
||||
&:hover {
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-button-group__toggle {
|
||||
border-radius: 0 @baseBorderRadius @baseBorderRadius 0;
|
||||
border-left: 1px solid rgba(0,0,0,0.09);
|
||||
margin-left: -2px;
|
||||
margin-left: -1px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
|
||||
/**
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name Umbraco.MainController
|
||||
* @name Umbraco.MainController
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* @description
|
||||
* The main application controller
|
||||
*
|
||||
*/
|
||||
function MainController($scope, $location, appState, treeService, notificationsService,
|
||||
userService, historyService, updateChecker, navigationService, eventsService,
|
||||
tmhDynamicLocale, localStorageService, editorService, overlayService, assetsService, tinyMceAssets) {
|
||||
|
||||
|
||||
//the null is important because we do an explicit bool check on this in the view
|
||||
$scope.authenticated = null;
|
||||
$scope.authenticated = null;
|
||||
$scope.touchDevice = appState.getGlobalState("touchDevice");
|
||||
$scope.infiniteMode = false;
|
||||
$scope.overlay = {};
|
||||
|
||||
@@ -154,7 +154,7 @@ app.config(function ($routeProvider) {
|
||||
//This allows us to dynamically change the template for this route since you cannot inject services into the templateUrl method.
|
||||
template: "<div ng-include='templateUrl'></div>",
|
||||
//This controller will execute for this route, then we replace the template dynamically based on the current tree.
|
||||
controller: function ($scope, $routeParams, treeService) {
|
||||
controller: function ($scope, $routeParams, navigationService) {
|
||||
|
||||
if (!$routeParams.method) {
|
||||
$scope.templateUrl = "views/common/dashboard.html";
|
||||
@@ -176,24 +176,7 @@ app.config(function ($routeProvider) {
|
||||
$scope.templateUrl = "views/users/overview.html";
|
||||
return;
|
||||
}
|
||||
|
||||
// Here we need to figure out if this route is for a user's package tree and if so then we need
|
||||
// to change it's convention view path to:
|
||||
// /App_Plugins/{mypackage}/backoffice/{treetype}/{method}.html
|
||||
|
||||
// otherwise if it is a core tree we use the core paths:
|
||||
// views/{treetype}/{method}.html
|
||||
|
||||
var packageTreeFolder = treeService.getTreePackageFolder($routeParams.tree);
|
||||
|
||||
if (packageTreeFolder) {
|
||||
$scope.templateUrl = (Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
|
||||
"/" + packageTreeFolder +
|
||||
"/backoffice/" + $routeParams.tree + "/" + $routeParams.method + ".html");
|
||||
}
|
||||
else {
|
||||
$scope.templateUrl = ('views/' + $routeParams.tree + '/' + $routeParams.method + '.html');
|
||||
}
|
||||
$scope.templateUrl = navigationService.getTreeTemplateUrl($routeParams.tree, $routeParams.method);
|
||||
},
|
||||
reloadOnSearch: false,
|
||||
resolve: canRoute(true)
|
||||
@@ -202,30 +185,13 @@ app.config(function ($routeProvider) {
|
||||
//This allows us to dynamically change the template for this route since you cannot inject services into the templateUrl method.
|
||||
template: "<div ng-include='templateUrl'></div>",
|
||||
//This controller will execute for this route, then we replace the template dynamically based on the current tree.
|
||||
controller: function ($scope, $route, $routeParams, treeService) {
|
||||
controller: function ($scope, $routeParams, navigationService) {
|
||||
|
||||
if (!$routeParams.tree || !$routeParams.method) {
|
||||
$scope.templateUrl = "views/common/dashboard.html";
|
||||
return;
|
||||
}
|
||||
|
||||
// Here we need to figure out if this route is for a package tree and if so then we need
|
||||
// to change it's convention view path to:
|
||||
// /App_Plugins/{mypackage}/backoffice/{treetype}/{method}.html
|
||||
|
||||
// otherwise if it is a core tree we use the core paths:
|
||||
// views/{treetype}/{method}.html
|
||||
|
||||
var packageTreeFolder = treeService.getTreePackageFolder($routeParams.tree);
|
||||
|
||||
if (packageTreeFolder) {
|
||||
$scope.templateUrl = (Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
|
||||
"/" + packageTreeFolder +
|
||||
"/backoffice/" + $routeParams.tree + "/" + $routeParams.method + ".html");
|
||||
}
|
||||
else {
|
||||
$scope.templateUrl = ('views/' + $routeParams.tree + '/' + $routeParams.method + '.html');
|
||||
}
|
||||
|
||||
$scope.templateUrl = navigationService.getTreeTemplateUrl($routeParams.tree, $routeParams.method);
|
||||
},
|
||||
reloadOnSearch: false,
|
||||
reloadOnUrl: false,
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* A friendly utility collection to replace AngularJs' ng-functions
|
||||
* If it doesn't exist here, it's probably available as vanilla JS
|
||||
*
|
||||
* Still carries a dependency on underscore, but if usages of underscore from
|
||||
* elsewhere in the codebase can instead use these methods, the underscore
|
||||
* dependency will be nicely abstracted and can be removed/swapped later
|
||||
*
|
||||
* This collection is open to extension...
|
||||
*/
|
||||
(function (window) {
|
||||
|
||||
/**
|
||||
* Equivalent to angular.noop
|
||||
*/
|
||||
const noop = () => { };
|
||||
|
||||
/**
|
||||
* Facade to angular.copy
|
||||
*/
|
||||
const copy = val => angular.copy(val);
|
||||
|
||||
/**
|
||||
* Equivalent to angular.isArray
|
||||
*/
|
||||
const isArray = val => Array.isArray(val) || val instanceof Array;
|
||||
|
||||
/**
|
||||
* Facade to angular.equals
|
||||
*/
|
||||
const equals = (a, b) => angular.equals(a, b);
|
||||
|
||||
/**
|
||||
* Facade to angular.extend
|
||||
* Use this with Angular objects, for vanilla JS objects, use Object.assign()
|
||||
*/
|
||||
const extend = (dst, src) => angular.extend(dst, src);
|
||||
|
||||
/**
|
||||
* Equivalent to angular.isFunction
|
||||
*/
|
||||
const isFunction = val => typeof val === 'function';
|
||||
|
||||
/**
|
||||
* Equivalent to angular.isUndefined
|
||||
*/
|
||||
const isUndefined = val => typeof val === 'undefined';
|
||||
|
||||
/**
|
||||
* Equivalent to angular.isDefined. Inverts result of const isUndefined
|
||||
*/
|
||||
const isDefined = val => !isUndefined(val);
|
||||
|
||||
/**
|
||||
* Equivalent to angular.isString
|
||||
*/
|
||||
const isString = val => typeof val === 'string';
|
||||
|
||||
/**
|
||||
* Equivalent to angular.isNumber
|
||||
*/
|
||||
const isNumber = val => typeof val === 'number';
|
||||
|
||||
/**
|
||||
* Equivalent to angular.isObject
|
||||
*/
|
||||
const isObject = val => val !== null && typeof val === 'object';
|
||||
|
||||
let _utilities = {
|
||||
noop: noop,
|
||||
copy: copy,
|
||||
isArray: isArray,
|
||||
equals: equals,
|
||||
extend: extend,
|
||||
isFunction: isFunction,
|
||||
isUndefined: isUndefined,
|
||||
isDefined: isDefined,
|
||||
isString: isString,
|
||||
isNumber: isNumber,
|
||||
isObject: isObject
|
||||
};
|
||||
|
||||
if (typeof (window.Utilities) === 'undefined') {
|
||||
window.Utilities = _utilities;
|
||||
}
|
||||
})(window);
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="applications" ng-class="{faded:stickyNavigation}">
|
||||
<ul class="sections" data-element="sections">
|
||||
|
||||
<li data-element="section-{{section.alias}}" ng-repeat="section in sections | limitTo: maxSections" ng-class="{current: section.alias == currentSection}">
|
||||
<li data-element="section-{{section.alias}}" ng-repeat="section in sections | limitTo: visibleSections" ng-class="{current: section.alias == currentSection}">
|
||||
<a href="#/{{section.alias}}"
|
||||
ng-dblclick="sectionDblClick(section)"
|
||||
ng-click="sectionClick($event, section)"
|
||||
@@ -11,13 +11,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li data-element="section-expand" class="expand" ng-class="{ 'open': showTray === true, current: currentSectionInOverflow() }" ng-show="needTray">
|
||||
<li data-element="section-expand" class="expand" ng-class="{ 'open': showTray === true, current: currentSectionInOverflow() }" ng-show="visibleSections < sections.length">
|
||||
<a href="#" ng-click="trayClick()" prevent-default>
|
||||
<span class="section__name"><i></i><i></i><i></i></span>
|
||||
<span class="section__name">•••</span>
|
||||
</a>
|
||||
|
||||
<ul id="applications-tray" class="sections-tray shadow-depth-2" ng-if="showTray" on-outside-click="trayClick()">
|
||||
<li ng-repeat="section in sections | limitTo: overflowingSections" ng-class="{current: section.alias == currentSection}">
|
||||
<li ng-repeat="section in sections | limitTo: sections.length | limitTo: -(sections.length - visibleSections)" ng-class="{current: section.alias == currentSection}">
|
||||
<a href="#/{{section.alias}}"
|
||||
ng-dblclick="sectionDblClick(section)"
|
||||
ng-click="sectionClick($event, section)"
|
||||
|
||||
@@ -325,7 +325,7 @@
|
||||
/* ---------- SAVE ---------- */
|
||||
|
||||
function save() {
|
||||
saveInternal().then(angular.noop, angular.noop);
|
||||
saveInternal().then(Utilities.noop, Utilities.noop);
|
||||
}
|
||||
|
||||
/** This internal save method performs the actual saving and returns a promise, not to be bound to any buttons but used by other bound methods */
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
packageResource.getAllCreated().then(createdPackages => {
|
||||
vm.createdPackages = createdPackages;
|
||||
}, angular.noop);
|
||||
}, Utilities.noop);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
activate: false
|
||||
});
|
||||
completeSave(saved);
|
||||
}, angular.noop);
|
||||
}, Utilities.noop);
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
@@ -9,6 +9,7 @@ angular.module("umbraco")
|
||||
$element,
|
||||
eventsService,
|
||||
editorService,
|
||||
overlayService,
|
||||
$interpolate
|
||||
) {
|
||||
|
||||
@@ -320,21 +321,22 @@ angular.module("umbraco")
|
||||
var title = "";
|
||||
localizationService.localize("grid_insertControl").then(function (value) {
|
||||
title = value;
|
||||
$scope.editorOverlay = {
|
||||
view: "itempicker",
|
||||
overlayService.open({
|
||||
view: "itempicker",
|
||||
filter: area.$allowedEditors.length > 15,
|
||||
title: title,
|
||||
availableItems: area.$allowedEditors,
|
||||
event: event,
|
||||
show: true,
|
||||
submit: function (model) {
|
||||
submit: function(model) {
|
||||
if (model.selectedItem) {
|
||||
$scope.addControl(model.selectedItem, area, index);
|
||||
$scope.editorOverlay.show = false;
|
||||
$scope.editorOverlay = null;
|
||||
overlayService.close();
|
||||
}
|
||||
},
|
||||
close: function() {
|
||||
overlayService.close();
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -299,11 +299,4 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-overlay
|
||||
ng-if="editorOverlay.show"
|
||||
model="editorOverlay"
|
||||
view="editorOverlay.view"
|
||||
position="target">
|
||||
</umb-overlay>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -39,8 +39,14 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController",
|
||||
|
||||
stylesheetResource.getAll().then(function(stylesheets){
|
||||
$scope.stylesheets = stylesheets;
|
||||
|
||||
_.each($scope.stylesheets, function (stylesheet) {
|
||||
|
||||
// if the CSS directory changes, previously assigned stylesheets are retained, but will not be visible
|
||||
// and will throw a 404 when loading the RTE. Remove them here. Still needs to be saved...
|
||||
let cssPath = Umbraco.Sys.ServerVariables.umbracoSettings.cssPath;
|
||||
$scope.model.value.stylesheets = $scope.model.value.stylesheets
|
||||
.filter(sheet => sheet.startsWith(cssPath));
|
||||
|
||||
$scope.stylesheets.forEach(stylesheet => {
|
||||
// support both current format (full stylesheet path) and legacy format (stylesheet name only)
|
||||
stylesheet.selected = $scope.model.value.stylesheets.indexOf(stylesheet.path) >= 0 ||$scope.model.value.stylesheets.indexOf(stylesheet.name) >= 0;
|
||||
});
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
extendedSave(saved).then(function(result) {
|
||||
//if all is good, then reset the form
|
||||
formHelper.resetForm({ scope: $scope });
|
||||
}, angular.noop);
|
||||
}, Utilities.noop);
|
||||
|
||||
vm.user = _.omit(saved, "navigation");
|
||||
//restore
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
userGroupsResource.deleteUserGroups(_.pluck(vm.selection, "id")).then(function (data) {
|
||||
clearSelection();
|
||||
onInit();
|
||||
}, angular.noop);
|
||||
}, Utilities.noop);
|
||||
overlayService.close();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -386,7 +386,7 @@
|
||||
vm.selectedBulkUserGroups = [];
|
||||
editorService.close();
|
||||
clearSelection();
|
||||
}, angular.noop);
|
||||
}, Utilities.noop);
|
||||
},
|
||||
close: function () {
|
||||
vm.selectedBulkUserGroups = [];
|
||||
|
||||
@@ -6,10 +6,15 @@ using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Umbraco.Composing;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Web.BackOffice.AspNetCore;
|
||||
using Umbraco.Web.Common.AspNetCore;
|
||||
using Umbraco.Web.Common.Extensions;
|
||||
using Umbraco.Web.Website.AspNetCore;
|
||||
using IHostingEnvironment = Umbraco.Core.Hosting.IHostingEnvironment;
|
||||
|
||||
|
||||
namespace Umbraco.Web.UI.BackOffice
|
||||
@@ -39,7 +44,7 @@ namespace Umbraco.Web.UI.BackOffice
|
||||
{
|
||||
services.AddUmbracoConfiguration(_config);
|
||||
services.AddUmbracoRuntimeMinifier(_config);
|
||||
services.AddUmbracoCore(_webHostEnvironment);
|
||||
services.AddUmbracoCore(_webHostEnvironment, out var factory);
|
||||
services.AddUmbracoWebsite();
|
||||
|
||||
services.AddMvc();
|
||||
@@ -47,6 +52,17 @@ namespace Umbraco.Web.UI.BackOffice
|
||||
{
|
||||
options.ShouldProfile = request => false; // WebProfiler determine and start profiling. We should not use the MiniProfilerMiddleware to also profile
|
||||
});
|
||||
|
||||
//Finally initialize Current
|
||||
Current.Initialize(
|
||||
factory.GetInstance<ILogger> (),
|
||||
factory.GetInstance<Configs>(),
|
||||
factory.GetInstance<IIOHelper>(),
|
||||
factory.GetInstance<IHostingEnvironment>(),
|
||||
factory.GetInstance<IBackOfficeInfo>(),
|
||||
factory.GetInstance<IProfiler>()
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
@using Umbraco.Web
|
||||
@using Umbraco.Core
|
||||
@using Umbraco.Core.Media
|
||||
@using Umbraco.Core.Models
|
||||
@using Umbraco.Web.Composing
|
||||
@inherits Umbraco.Web.Macros.PartialViewMacroPage
|
||||
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name Umbraco.MainController
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The main application controller
|
||||
*
|
||||
*/
|
||||
function MainController($scope, $location, appState, treeService, notificationsService,
|
||||
userService, historyService, updateChecker, navigationService, eventsService,
|
||||
tmhDynamicLocale, localStorageService, editorService, overlayService, assetsService, tinyMceAssets) {
|
||||
|
||||
//the null is important because we do an explicit bool check on this in the view
|
||||
$scope.authenticated = null;
|
||||
$scope.touchDevice = appState.getGlobalState("touchDevice");
|
||||
$scope.infiniteMode = false;
|
||||
$scope.overlay = {};
|
||||
$scope.drawer = {};
|
||||
$scope.search = {};
|
||||
$scope.login = {};
|
||||
$scope.tabbingActive = false;
|
||||
|
||||
// Load TinyMCE assets ahead of time in the background for the user
|
||||
// To help with first load of the RTE
|
||||
tinyMceAssets.forEach(function (tinyJsAsset) {
|
||||
assetsService.loadJs(tinyJsAsset, $scope);
|
||||
});
|
||||
|
||||
// There are a number of ways to detect when a focus state should be shown when using the tab key and this seems to be the simplest solution.
|
||||
// For more information about this approach, see https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2
|
||||
function handleFirstTab(evt) {
|
||||
if (evt.keyCode === 9) {
|
||||
$scope.tabbingActive = true;
|
||||
$scope.$digest();
|
||||
window.removeEventListener('keydown', handleFirstTab);
|
||||
window.addEventListener('mousedown', disableTabbingActive);
|
||||
}
|
||||
}
|
||||
|
||||
function disableTabbingActive(evt) {
|
||||
$scope.tabbingActive = false;
|
||||
$scope.$digest();
|
||||
window.removeEventListener('mousedown', disableTabbingActive);
|
||||
window.addEventListener("keydown", handleFirstTab);
|
||||
}
|
||||
|
||||
window.addEventListener("keydown", handleFirstTab);
|
||||
|
||||
|
||||
$scope.removeNotification = function (index) {
|
||||
notificationsService.remove(index);
|
||||
};
|
||||
|
||||
$scope.closeSearch = function() {
|
||||
appState.setSearchState("show", false);
|
||||
};
|
||||
|
||||
$scope.showLoginScreen = function(isTimedOut) {
|
||||
$scope.login.isTimedOut = isTimedOut;
|
||||
$scope.login.show = true;
|
||||
};
|
||||
|
||||
$scope.hideLoginScreen = function() {
|
||||
$scope.login.show = false;
|
||||
};
|
||||
|
||||
var evts = [];
|
||||
|
||||
//when a user logs out or timesout
|
||||
evts.push(eventsService.on("app.notAuthenticated", function (evt, data) {
|
||||
$scope.authenticated = null;
|
||||
$scope.user = null;
|
||||
const isTimedOut = data && data.isTimedOut ? true : false;
|
||||
$scope.showLoginScreen(isTimedOut);
|
||||
|
||||
// Remove the localstorage items for tours shown
|
||||
// Means that when next logged in they can be re-shown if not already dismissed etc
|
||||
localStorageService.remove("emailMarketingTourShown");
|
||||
localStorageService.remove("introTourShown");
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("app.userRefresh", function(evt) {
|
||||
userService.refreshCurrentUser().then(function(data) {
|
||||
$scope.user = data;
|
||||
|
||||
//Load locale file
|
||||
if ($scope.user.locale) {
|
||||
tmhDynamicLocale.set($scope.user.locale);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
//when the app is ready/user is logged in, setup the data
|
||||
evts.push(eventsService.on("app.ready", function (evt, data) {
|
||||
|
||||
$scope.authenticated = data.authenticated;
|
||||
$scope.user = data.user;
|
||||
|
||||
updateChecker.check().then(function (update) {
|
||||
if (update && update !== "null") {
|
||||
if (update.type !== "None") {
|
||||
var notification = {
|
||||
headline: "Update available",
|
||||
message: "Click to download",
|
||||
sticky: true,
|
||||
type: "info",
|
||||
url: update.url
|
||||
};
|
||||
notificationsService.add(notification);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//if the user has changed we need to redirect to the root so they don't try to continue editing the
|
||||
//last item in the URL (NOTE: the user id can equal zero, so we cannot just do !data.lastUserId since that will resolve to true)
|
||||
if (data.lastUserId !== undefined && data.lastUserId !== null && data.lastUserId !== data.user.id) {
|
||||
|
||||
var section = appState.getSectionState("currentSection");
|
||||
if (section) {
|
||||
//if there's a section already assigned, reload it so the tree is cleared
|
||||
navigationService.reloadSection(section);
|
||||
}
|
||||
|
||||
$location.path("/").search("");
|
||||
historyService.removeAll();
|
||||
treeService.clearCache();
|
||||
editorService.closeAll();
|
||||
overlayService.close();
|
||||
|
||||
//if the user changed, clearout local storage too - could contain sensitive data
|
||||
localStorageService.clearAll();
|
||||
}
|
||||
|
||||
//if this is a new login (i.e. the user entered credentials), then clear out local storage - could contain sensitive data
|
||||
if (data.loginType === "credentials") {
|
||||
localStorageService.clearAll();
|
||||
}
|
||||
|
||||
//Load locale file
|
||||
if ($scope.user.locale) {
|
||||
tmhDynamicLocale.set($scope.user.locale);
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
// events for search
|
||||
evts.push(eventsService.on("appState.searchState.changed", function (e, args) {
|
||||
if (args.key === "show") {
|
||||
$scope.search.show = args.value;
|
||||
}
|
||||
}));
|
||||
|
||||
// events for drawer
|
||||
// manage the help dialog by subscribing to the showHelp appState
|
||||
evts.push(eventsService.on("appState.drawerState.changed", function (e, args) {
|
||||
// set view
|
||||
if (args.key === "view") {
|
||||
$scope.drawer.view = args.value;
|
||||
}
|
||||
// set custom model
|
||||
if (args.key === "model") {
|
||||
$scope.drawer.model = args.value;
|
||||
}
|
||||
// show / hide drawer
|
||||
if (args.key === "showDrawer") {
|
||||
$scope.drawer.show = args.value;
|
||||
}
|
||||
}));
|
||||
|
||||
// events for overlays
|
||||
evts.push(eventsService.on("appState.overlay", function (name, args) {
|
||||
$scope.overlay = args;
|
||||
}));
|
||||
|
||||
// events for tours
|
||||
evts.push(eventsService.on("appState.tour.start", function (name, args) {
|
||||
$scope.tour = args;
|
||||
$scope.tour.show = true;
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("appState.tour.end", function () {
|
||||
$scope.tour = null;
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("appState.tour.complete", function () {
|
||||
$scope.tour = null;
|
||||
}));
|
||||
|
||||
// events for backdrop
|
||||
evts.push(eventsService.on("appState.backdrop", function (name, args) {
|
||||
$scope.backdrop = args;
|
||||
}));
|
||||
|
||||
// event for infinite editors
|
||||
evts.push(eventsService.on("appState.editors.open", function (name, args) {
|
||||
$scope.infiniteMode = args && args.editors.length > 0 ? true : false;
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("appState.editors.close", function (name, args) {
|
||||
$scope.infiniteMode = args && args.editors.length > 0 ? true : false;
|
||||
}));
|
||||
|
||||
//ensure to unregister from all events!
|
||||
$scope.$on('$destroy', function () {
|
||||
for (var e in evts) {
|
||||
eventsService.unsubscribe(evts[e]);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
//register it
|
||||
angular.module('umbraco').controller("Umbraco.MainController", MainController).
|
||||
config(function (tmhDynamicLocaleProvider) {
|
||||
//Set url for locale files
|
||||
tmhDynamicLocaleProvider.localeLocationPattern('lib/angular-i18n/angular-locale_{{locale}}.js');
|
||||
});
|
||||
@@ -1,569 +0,0 @@
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name Umbraco.NavigationController
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Handles the section area of the app
|
||||
*
|
||||
* @param {navigationService} navigationService A reference to the navigationService
|
||||
*/
|
||||
function NavigationController($scope, $rootScope, $location, $log, $q, $routeParams, $timeout, $cookies, treeService, appState, navigationService, keyboardService, historyService, eventsService, angularHelper, languageResource, contentResource, editorState) {
|
||||
|
||||
//this is used to trigger the tree to start loading once everything is ready
|
||||
var treeInitPromise = $q.defer();
|
||||
|
||||
$scope.treeApi = {};
|
||||
|
||||
//Bind to the main tree events
|
||||
$scope.onTreeInit = function () {
|
||||
|
||||
$scope.treeApi.callbacks.treeNodeExpanded(nodeExpandedHandler);
|
||||
|
||||
//when a tree is loaded into a section, we need to put it into appState
|
||||
$scope.treeApi.callbacks.treeLoaded(function (args) {
|
||||
appState.setTreeState("currentRootNode", args.tree);
|
||||
});
|
||||
|
||||
//when a tree node is synced this event will fire, this allows us to set the currentNode
|
||||
$scope.treeApi.callbacks.treeSynced(function (args) {
|
||||
|
||||
if (args.activate === undefined || args.activate === true) {
|
||||
//set the current selected node
|
||||
appState.setTreeState("selectedNode", args.node);
|
||||
//when a node is activated, this is the same as clicking it and we need to set the
|
||||
//current menu item to be this node as well.
|
||||
//appState.setMenuState("currentNode", args.node);// Niels: No, we are setting it from the dialog.
|
||||
}
|
||||
});
|
||||
|
||||
//this reacts to the options item in the tree
|
||||
$scope.treeApi.callbacks.treeOptionsClick(function (args) {
|
||||
args.event.stopPropagation();
|
||||
args.event.preventDefault();
|
||||
|
||||
//Set the current action node (this is not the same as the current selected node!)
|
||||
//appState.setMenuState("currentNode", args.node);// Niels: No, we are setting it from the dialog.
|
||||
|
||||
if (args.event && args.event.altKey) {
|
||||
args.skipDefault = true;
|
||||
}
|
||||
|
||||
navigationService.showMenu(args);
|
||||
});
|
||||
|
||||
$scope.treeApi.callbacks.treeNodeAltSelect(function (args) {
|
||||
args.event.stopPropagation();
|
||||
args.event.preventDefault();
|
||||
|
||||
args.skipDefault = true;
|
||||
navigationService.showMenu(args);
|
||||
});
|
||||
|
||||
//this reacts to tree items themselves being clicked
|
||||
//the tree directive should not contain any handling, simply just bubble events
|
||||
$scope.treeApi.callbacks.treeNodeSelect(function (args) {
|
||||
var n = args.node;
|
||||
args.event.stopPropagation();
|
||||
args.event.preventDefault();
|
||||
|
||||
if (n.metaData && n.metaData["jsClickCallback"] && angular.isString(n.metaData["jsClickCallback"]) && n.metaData["jsClickCallback"] !== "") {
|
||||
//this is a legacy tree node!
|
||||
var jsPrefix = "javascript:";
|
||||
var js;
|
||||
if (n.metaData["jsClickCallback"].startsWith(jsPrefix)) {
|
||||
js = n.metaData["jsClickCallback"].substr(jsPrefix.length);
|
||||
}
|
||||
else {
|
||||
js = n.metaData["jsClickCallback"];
|
||||
}
|
||||
try {
|
||||
var func = eval(js);
|
||||
//this is normally not necessary since the eval above should execute the method and will return nothing.
|
||||
if (func != null && (typeof func === "function")) {
|
||||
func.call();
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
$log.error("Error evaluating js callback from legacy tree node: " + ex);
|
||||
}
|
||||
}
|
||||
else if (n.routePath) {
|
||||
//add action to the history service
|
||||
historyService.add({ name: n.name, link: n.routePath, icon: n.icon });
|
||||
|
||||
//put this node into the tree state
|
||||
appState.setTreeState("selectedNode", args.node);
|
||||
//when a node is clicked we also need to set the active menu node to this node
|
||||
//appState.setMenuState("currentNode", args.node);
|
||||
|
||||
//not legacy, lets just set the route value and clear the query string if there is one.
|
||||
$location.path(n.routePath);
|
||||
navigationService.clearSearch();
|
||||
}
|
||||
else if (n.section) {
|
||||
$location.path(n.section);
|
||||
navigationService.clearSearch();
|
||||
}
|
||||
|
||||
navigationService.hideNavigation();
|
||||
});
|
||||
|
||||
return treeInitPromise.promise;
|
||||
}
|
||||
|
||||
//set up our scope vars
|
||||
$scope.showContextMenuDialog = false;
|
||||
$scope.showContextMenu = false;
|
||||
$scope.showSearchResults = false;
|
||||
$scope.menuDialogTitle = null;
|
||||
$scope.menuActions = [];
|
||||
$scope.menuNode = null;
|
||||
$scope.languages = [];
|
||||
$scope.selectedLanguage = {};
|
||||
$scope.page = {};
|
||||
$scope.page.languageSelectorIsOpen = false;
|
||||
|
||||
$scope.currentSection = null;
|
||||
$scope.customTreeParams = null;
|
||||
$scope.treeCacheKey = "_";
|
||||
$scope.showNavigation = appState.getGlobalState("showNavigation");
|
||||
// tracks all expanded paths so when the language is switched we can resync it with the already loaded paths
|
||||
var expandedPaths = [];
|
||||
|
||||
//trigger search with a hotkey:
|
||||
keyboardService.bind("ctrl+shift+s", function () {
|
||||
navigationService.showSearch();
|
||||
});
|
||||
|
||||
//// TODO: remove this it's not a thing
|
||||
//$scope.selectedId = navigationService.currentId;
|
||||
|
||||
var isInit = false;
|
||||
var evts = [];
|
||||
|
||||
//Listen for global state changes
|
||||
evts.push(eventsService.on("appState.globalState.changed", function (e, args) {
|
||||
if (args.key === "showNavigation") {
|
||||
$scope.showNavigation = args.value;
|
||||
}
|
||||
}));
|
||||
|
||||
//Listen for menu state changes
|
||||
evts.push(eventsService.on("appState.menuState.changed", function (e, args) {
|
||||
if (args.key === "showMenuDialog") {
|
||||
$scope.showContextMenuDialog = args.value;
|
||||
}
|
||||
if (args.key === "dialogTemplateUrl") {
|
||||
$scope.dialogTemplateUrl = args.value;
|
||||
}
|
||||
if (args.key === "showMenu") {
|
||||
$scope.showContextMenu = args.value;
|
||||
}
|
||||
if (args.key === "dialogTitle") {
|
||||
$scope.menuDialogTitle = args.value;
|
||||
}
|
||||
if (args.key === "menuActions") {
|
||||
$scope.menuActions = args.value;
|
||||
}
|
||||
if (args.key === "currentNode") {
|
||||
$scope.menuNode = args.value;
|
||||
}
|
||||
}));
|
||||
|
||||
//Listen for tree state changes
|
||||
evts.push(eventsService.on("appState.treeState.changed", function (e, args) {
|
||||
if (args.key === "currentRootNode") {
|
||||
|
||||
//if the changed state is the currentRootNode, determine if this is a full screen app
|
||||
if (args.value.root && args.value.root.containsTrees === false) {
|
||||
$rootScope.emptySection = true;
|
||||
}
|
||||
else {
|
||||
$rootScope.emptySection = false;
|
||||
}
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
//Listen for section state changes
|
||||
evts.push(eventsService.on("appState.sectionState.changed", function (e, args) {
|
||||
|
||||
//section changed
|
||||
if (args.key === "currentSection" && $scope.currentSection != args.value) {
|
||||
//before loading the main tree we need to ensure that the nav is ready
|
||||
navigationService.waitForNavReady().then(() => {
|
||||
$scope.currentSection = args.value;
|
||||
//load the tree
|
||||
configureTreeAndLanguages();
|
||||
$scope.treeApi.load({ section: $scope.currentSection, customTreeParams: $scope.customTreeParams, cacheKey: $scope.treeCacheKey });
|
||||
});
|
||||
}
|
||||
|
||||
//show/hide search results
|
||||
if (args.key === "showSearchResults") {
|
||||
$scope.showSearchResults = args.value;
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
// Listen for language updates
|
||||
evts.push(eventsService.on("editors.languages.languageDeleted", function (e, args) {
|
||||
loadLanguages().then(function (languages) {
|
||||
$scope.languages = languages;
|
||||
const defaultCulture = $scope.languages[0].culture;
|
||||
|
||||
if (args.language.culture === $scope.selectedLanguage.culture) {
|
||||
$scope.selectedLanguage = defaultCulture;
|
||||
|
||||
if ($scope.languages.length > 1) {
|
||||
$location.search("mculture", defaultCulture);
|
||||
} else {
|
||||
$location.search("mculture", null);
|
||||
}
|
||||
|
||||
var currentEditorState = editorState.getCurrent();
|
||||
if (currentEditorState && currentEditorState.path) {
|
||||
$scope.treeApi.syncTree({ path: currentEditorState.path, activate: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
//Emitted when a language is created or an existing one saved/edited
|
||||
evts.push(eventsService.on("editors.languages.languageSaved", function (e, args) {
|
||||
if(args.isNew){
|
||||
//A new language has been created - reload languages for tree
|
||||
loadLanguages().then(function (languages) {
|
||||
$scope.languages = languages;
|
||||
});
|
||||
}
|
||||
else if(args.language.isDefault){
|
||||
//A language was saved and was set to be the new default (refresh the tree, so its at the top)
|
||||
loadLanguages().then(function (languages) {
|
||||
$scope.languages = languages;
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
//when a user logs out or timesout
|
||||
evts.push(eventsService.on("app.notAuthenticated", function () {
|
||||
$scope.authenticated = false;
|
||||
}));
|
||||
|
||||
//when the application is ready and the user is authorized, setup the data
|
||||
//this will occur anytime a new user logs in!
|
||||
evts.push(eventsService.on("app.ready", function (evt, data) {
|
||||
$scope.authenticated = true;
|
||||
ensureInit();
|
||||
ensureMainCulture();
|
||||
}));
|
||||
|
||||
// event for infinite editors
|
||||
evts.push(eventsService.on("appState.editors.open", function (name, args) {
|
||||
$scope.infiniteMode = args && args.editors.length > 0 ? true : false;
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("appState.editors.close", function (name, args) {
|
||||
$scope.infiniteMode = args && args.editors.length > 0 ? true : false;
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("treeService.removeNode", function (e, args) {
|
||||
//check to see if the current page has been removed
|
||||
|
||||
var currentEditorState = editorState.getCurrent();
|
||||
if (currentEditorState && currentEditorState.id.toString() === args.node.id.toString()) {
|
||||
//current page is loaded, so navigate to root
|
||||
var section = appState.getSectionState("currentSection");
|
||||
$location.path("/" + section);
|
||||
}
|
||||
}));
|
||||
|
||||
/**
|
||||
* For multi language sites, this ensures that mculture is set to either the last selected language or the default one
|
||||
*/
|
||||
function ensureMainCulture() {
|
||||
if ($location.search().mculture) {
|
||||
return;
|
||||
}
|
||||
var language = lastLanguageOrDefault();
|
||||
if (!language) {
|
||||
return;
|
||||
}
|
||||
// trigger a language selection in the next digest cycle
|
||||
$timeout(function () {
|
||||
$scope.selectLanguage(language);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the current state of the application, this configures the scope variables that control the main tree and language drop down
|
||||
*/
|
||||
function configureTreeAndLanguages() {
|
||||
|
||||
//create the custom query string param for this tree, this is currently only relevant for content
|
||||
if ($scope.currentSection === "content") {
|
||||
|
||||
//must use $location here because $routeParams isn't available until after the route change
|
||||
var mainCulture = $location.search().mculture;
|
||||
//select the current language if set in the query string
|
||||
if (mainCulture && $scope.languages && $scope.languages.length > 1) {
|
||||
var found = _.find($scope.languages, function (l) {
|
||||
if (mainCulture === true) {
|
||||
return false;
|
||||
}
|
||||
return l.culture.toLowerCase() === mainCulture.toLowerCase();
|
||||
});
|
||||
if (found) {
|
||||
//set the route param
|
||||
found.active = true;
|
||||
$scope.selectedLanguage = found;
|
||||
}
|
||||
}
|
||||
|
||||
var queryParams = {};
|
||||
if ($scope.selectedLanguage && $scope.selectedLanguage.culture) {
|
||||
queryParams["culture"] = $scope.selectedLanguage.culture;
|
||||
}
|
||||
var queryString = $.param(queryParams); //create the query string from the params object
|
||||
}
|
||||
|
||||
if (queryString) {
|
||||
$scope.customTreeParams = queryString;
|
||||
$scope.treeCacheKey = queryString; // this tree uses caching but we need to change it's cache key per lang
|
||||
}
|
||||
else {
|
||||
$scope.treeCacheKey = "_"; // this tree uses caching, there's no lang selected so use the default
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the app is ready and sets up the navigation (should only be called once)
|
||||
*/
|
||||
function ensureInit() {
|
||||
|
||||
//only run once ever!
|
||||
if (isInit) {
|
||||
return;
|
||||
}
|
||||
|
||||
isInit = true;
|
||||
|
||||
var navInit = false;
|
||||
|
||||
//$routeParams will be populated after $routeChangeSuccess since this controller is used outside ng-view,
|
||||
//* we listen for the first route change with a section to setup the navigation.
|
||||
//* we listen for all route changes to track the current section.
|
||||
$rootScope.$on('$routeChangeSuccess', function () {
|
||||
|
||||
//only continue if there's a section available
|
||||
if ($routeParams.section) {
|
||||
|
||||
if (!navInit) {
|
||||
navInit = true;
|
||||
initNav();
|
||||
}
|
||||
|
||||
//keep track of the current section when it changes
|
||||
if ($scope.currentSection != $routeParams.section) {
|
||||
appState.setSectionState("currentSection", $routeParams.section);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This loads the language data, if the are no variant content types configured this will return no languages
|
||||
*/
|
||||
function loadLanguages() {
|
||||
|
||||
return contentResource.allowsCultureVariation().then(function (b) {
|
||||
if (b === true) {
|
||||
return languageResource.getAll();
|
||||
} else {
|
||||
return $q.when([]); //resolve an empty collection
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once during init to initialize the navigation/tree/languages
|
||||
*/
|
||||
function initNav() {
|
||||
// load languages
|
||||
loadLanguages().then(function (languages) {
|
||||
|
||||
$scope.languages = languages;
|
||||
|
||||
if ($scope.languages.length > 1) {
|
||||
//if there's already one set, check if it exists
|
||||
var language = null;
|
||||
var mainCulture = $location.search().mculture;
|
||||
if (mainCulture) {
|
||||
language = _.find($scope.languages, function (l) {
|
||||
return l.culture.toLowerCase() === mainCulture.toLowerCase();
|
||||
});
|
||||
}
|
||||
if (!language) {
|
||||
language = lastLanguageOrDefault();
|
||||
|
||||
if (language) {
|
||||
$location.search("mculture", language.culture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$scope.currentSection = $routeParams.section;
|
||||
|
||||
configureTreeAndLanguages();
|
||||
|
||||
//resolve the tree promise, set it's property values for loading the tree which will make the tree load
|
||||
treeInitPromise.resolve({
|
||||
section: $scope.currentSection,
|
||||
customTreeParams: $scope.customTreeParams,
|
||||
cacheKey: $scope.treeCacheKey,
|
||||
|
||||
//because angular doesn't return a promise for the resolve method, we need to resort to some hackery, else
|
||||
//like normal JS promises we could do resolve(...).then()
|
||||
onLoaded: function () {
|
||||
|
||||
//the nav is ready, let the app know
|
||||
eventsService.emit("app.navigationReady", { treeApi: $scope.treeApi });
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function lastLanguageOrDefault() {
|
||||
if (!$scope.languages || $scope.languages.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
// see if we can find a culture in the cookie set when changing language
|
||||
var lastCulture = $cookies.get("UMB_MCULTURE");
|
||||
var language = lastCulture ? _.find($scope.languages, function (l) {
|
||||
return l.culture.toLowerCase() === lastCulture.toLowerCase();
|
||||
}) : null;
|
||||
if (!language) {
|
||||
// no luck, look for the default language
|
||||
language = _.find($scope.languages, function (l) {
|
||||
return l.isDefault;
|
||||
});
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
||||
function nodeExpandedHandler(args) {
|
||||
//store the reference to the expanded node path
|
||||
if (args.node) {
|
||||
treeService._trackExpandedPaths(args.node, expandedPaths);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.selectLanguage = function (language) {
|
||||
|
||||
$location.search("mculture", language.culture);
|
||||
// add the selected culture to a cookie so the user will log back into the same culture later on (cookie lifetime = one year)
|
||||
var expireDate = new Date();
|
||||
expireDate.setDate(expireDate.getDate() + 365);
|
||||
$cookies.put("UMB_MCULTURE", language.culture, {path: "/", expires: expireDate});
|
||||
|
||||
// close the language selector
|
||||
$scope.page.languageSelectorIsOpen = false;
|
||||
|
||||
configureTreeAndLanguages(); //re-bind language to the query string and update the tree params
|
||||
|
||||
//reload the tree with it's updated querystring args
|
||||
$scope.treeApi.load({ section: $scope.currentSection, customTreeParams: $scope.customTreeParams, cacheKey: $scope.treeCacheKey }).then(function () {
|
||||
|
||||
//re-sync to currently edited node
|
||||
var currNode = appState.getTreeState("selectedNode");
|
||||
//create the list of promises
|
||||
var promises = [];
|
||||
//starting with syncing to the currently selected node if there is one
|
||||
if (currNode) {
|
||||
var path = treeService.getPath(currNode);
|
||||
promises.push($scope.treeApi.syncTree({ path: path, activate: true }));
|
||||
}
|
||||
// TODO: If we want to keep all paths expanded ... but we need more testing since we need to deal with unexpanding
|
||||
//for (var i = 0; i < expandedPaths.length; i++) {
|
||||
// promises.push($scope.treeApi.syncTree({ path: expandedPaths[i], activate: false, forceReload: true }));
|
||||
//}
|
||||
//execute them sequentially
|
||||
|
||||
// set selected language to active
|
||||
angular.forEach($scope.languages, function(language){
|
||||
language.active = false;
|
||||
});
|
||||
language.active = true;
|
||||
|
||||
angularHelper.executeSequentialPromises(promises);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
//this reacts to the options item in the tree
|
||||
// TODO: migrate to nav service
|
||||
// TODO: is this used?
|
||||
$scope.searchShowMenu = function (ev, args) {
|
||||
//always skip default
|
||||
args.skipDefault = true;
|
||||
navigationService.showMenu(args);
|
||||
};
|
||||
|
||||
// TODO: migrate to nav service
|
||||
// TODO: is this used?
|
||||
$scope.searchHide = function () {
|
||||
navigationService.hideSearch();
|
||||
};
|
||||
|
||||
//the below assists with hiding/showing the tree
|
||||
var treeActive = false;
|
||||
|
||||
//Sets a service variable as soon as the user hovers the navigation with the mouse
|
||||
//used by the leaveTree method to delay hiding
|
||||
$scope.enterTree = function (event) {
|
||||
treeActive = true;
|
||||
};
|
||||
|
||||
// Hides navigation tree, with a short delay, is cancelled if the user moves the mouse over the tree again
|
||||
$scope.leaveTree = function (event) {
|
||||
//this is a hack to handle IE touch events which freaks out due to no mouse events so the tree instantly shuts down
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
closeTree();
|
||||
};
|
||||
|
||||
$scope.onOutsideClick = function() {
|
||||
closeTree();
|
||||
};
|
||||
|
||||
function closeTree() {
|
||||
if (!appState.getGlobalState("touchDevice")) {
|
||||
treeActive = false;
|
||||
$timeout(function () {
|
||||
if (!treeActive) {
|
||||
navigationService.hideTree();
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.toggleLanguageSelector = function () {
|
||||
$scope.page.languageSelectorIsOpen = !$scope.page.languageSelectorIsOpen;
|
||||
};
|
||||
|
||||
//ensure to unregister from all events!
|
||||
$scope.$on('$destroy', function () {
|
||||
for (var e in evts) {
|
||||
eventsService.unsubscribe(evts[e]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//register it
|
||||
angular.module('umbraco').controller("Umbraco.NavigationController", NavigationController);
|
||||
@@ -21,7 +21,7 @@
|
||||
var altText = Model.value.altText ?? Model.value.caption ?? string.Empty;
|
||||
|
||||
<img src="@url" alt="@altText">
|
||||
|
||||
|
||||
if (Model.value.caption != null)
|
||||
{
|
||||
<p class="caption">@Model.value.caption</p>
|
||||
|
||||
@@ -5,7 +5,7 @@ using Umbraco.Net;
|
||||
|
||||
namespace Umbraco.Web.AspNet
|
||||
{
|
||||
public class AspNetUmbracoApplicationLifetime : IUmbracoApplicationLifetimeManager
|
||||
public class AspNetUmbracoApplicationLifetime : IUmbracoApplicationLifetime, IUmbracoApplicationLifetimeManager
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
|
||||
@@ -230,21 +230,27 @@ namespace Umbraco.Web
|
||||
}
|
||||
}
|
||||
|
||||
if (!lengthReached && currentTextLength >= length)
|
||||
if (!lengthReached)
|
||||
{
|
||||
// if the last character added was the first of a two character unicode pair, add the second character
|
||||
if (Char.IsHighSurrogate((char)ic))
|
||||
if (currentTextLength == length)
|
||||
{
|
||||
var lowSurrogate = tr.Read();
|
||||
outputtw.Write((char)lowSurrogate);
|
||||
}
|
||||
// if the last character added was the first of a two character unicode pair, add the second character
|
||||
if (char.IsHighSurrogate((char)ic))
|
||||
{
|
||||
var lowSurrogate = tr.Read();
|
||||
outputtw.Write((char)lowSurrogate);
|
||||
}
|
||||
|
||||
// Reached truncate limit.
|
||||
if (addElipsis)
|
||||
{
|
||||
outputtw.Write(hellip);
|
||||
}
|
||||
lengthReached = true;
|
||||
// only add elipsis if current length greater than original length
|
||||
if (currentTextLength > length)
|
||||
{
|
||||
if (addElipsis)
|
||||
{
|
||||
outputtw.Write(hellip);
|
||||
}
|
||||
lengthReached = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user