Merge branch temp8 into temp8-di
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
|
||||
not want this to happen as the alpha of the next major is, really, the next major already.
|
||||
-->
|
||||
<dependency id="AutoMapper" version="[7.0.0,7.999999)" />
|
||||
<dependency id="AutoMapper" version="[7.0.1,7.999999)" />
|
||||
<dependency id="ClientDependency" version="[1.9.6,1.999999)" />
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0,1.999999)" />
|
||||
<dependency id="CSharpTest.Net.Collections" version="[14.906.1403.1082,14.999999)" />
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Umbraco.Core.Composing.CompositionRoots
|
||||
{
|
||||
public void Compose(IServiceRegistry container)
|
||||
{
|
||||
container.Register<IdentityProfile>();
|
||||
container.Register<IdentityMapperProfile>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
internal class ContentElement : UmbracoConfigurationElement, IContentSection
|
||||
{
|
||||
private const string DefaultPreviewBadge = @"<a id=""umbracoPreviewBadge"" style=""position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{0}/assets/img/preview-mode-badge.png') no-repeat;"" href=""{0}/endPreview.aspx?redir={1}""><span style=""display:none;"">In Preview Mode - click to end</span></a>";
|
||||
private const string DefaultPreviewBadge = @"<a id=""umbracoPreviewBadge"" style=""z-index:99999; position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{0}/assets/img/preview-mode-badge.png') no-repeat;"" href=""#"" OnClick=""javascript:window.top.location.href = '{0}/endPreview.aspx?redir={1}'""><span style=""display:none;"">In Preview Mode - click to end</span></a>";
|
||||
|
||||
[ConfigurationProperty("imaging")]
|
||||
internal ContentImagingElement Imaging => (ContentImagingElement) this["imaging"];
|
||||
|
||||
@@ -61,8 +61,28 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
get
|
||||
{
|
||||
var groups = ContentTypeComposition.SelectMany(x => x.CompositionPropertyGroups).Union(PropertyGroups);
|
||||
return groups;
|
||||
// we need to "acquire" composition groups and properties here, ie get our own clones,
|
||||
// so that we can change their variation according to this content type variations.
|
||||
//
|
||||
// it would be nice to cache the resulting enumerable, but alas we cannot, otherwise
|
||||
// any change to compositions are ignored and that breaks many things - and tracking
|
||||
// changes to refresh the cache would be expensive.
|
||||
|
||||
void AcquireProperty(PropertyType propertyType)
|
||||
{
|
||||
propertyType.Variations = propertyType.Variations & Variations;
|
||||
propertyType.ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
return ContentTypeComposition.SelectMany(x => x.CompositionPropertyGroups)
|
||||
.Select(group =>
|
||||
{
|
||||
group = (PropertyGroup) group.DeepClone();
|
||||
foreach (var property in group.PropertyTypes)
|
||||
AcquireProperty(property);
|
||||
return group;
|
||||
})
|
||||
.Union(PropertyGroups);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,8 +94,23 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
get
|
||||
{
|
||||
var propertyTypes = ContentTypeComposition.SelectMany(x => x.CompositionPropertyTypes).Union(PropertyTypes);
|
||||
return propertyTypes;
|
||||
// we need to "acquire" composition properties here, ie get our own clones,
|
||||
// so that we can change their variation according to this content type variations.
|
||||
//
|
||||
// see note in CompositionPropertyGroups for comments on caching the resulting enumerable
|
||||
|
||||
PropertyType AcquireProperty(PropertyType propertyType)
|
||||
{
|
||||
propertyType = (PropertyType) propertyType.DeepClone();
|
||||
propertyType.Variations = propertyType.Variations & Variations;
|
||||
propertyType.ResetDirtyProperties(false);
|
||||
return propertyType;
|
||||
}
|
||||
|
||||
return ContentTypeComposition
|
||||
.SelectMany(x => x.CompositionPropertyTypes)
|
||||
.Select(AcquireProperty)
|
||||
.Union(PropertyTypes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Umbraco.Core.Models
|
||||
/// be available or not, and published or not, individually. Varying by segment
|
||||
/// is a property-level thing.</para>
|
||||
/// </remarks>
|
||||
[Flags]
|
||||
public enum ContentVariation : byte
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Umbraco.Core.Models.Identity
|
||||
private string[] _allowedSections;
|
||||
private int[] _startMediaIds;
|
||||
private int[] _startContentIds;
|
||||
private DateTime? _lastPasswordChangeDateUtc;
|
||||
|
||||
/// <summary>
|
||||
/// Used to construct a new instance without an identity
|
||||
@@ -136,6 +137,15 @@ namespace Umbraco.Core.Models.Identity
|
||||
set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _userName, Ps.Value.UserNameSelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LastPasswordChangeDateUtc so we can track changes to it
|
||||
/// </summary>
|
||||
public override DateTime? LastPasswordChangeDateUtc
|
||||
{
|
||||
get { return _lastPasswordChangeDateUtc; }
|
||||
set { _beingDirty.SetPropertyValueAndDetectChanges(value, ref _lastPasswordChangeDateUtc, Ps.Value.LastPasswordChangeDateUtcSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override LastLoginDateUtc so we can track changes to it
|
||||
/// </summary>
|
||||
@@ -419,6 +429,7 @@ namespace Umbraco.Core.Models.Identity
|
||||
public readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, string>(x => x.Email);
|
||||
public readonly PropertyInfo UserNameSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, string>(x => x.UserName);
|
||||
public readonly PropertyInfo LastLoginDateUtcSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, DateTime?>(x => x.LastLoginDateUtc);
|
||||
public readonly PropertyInfo LastPasswordChangeDateUtcSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, DateTime?>(x => x.LastPasswordChangeDateUtc);
|
||||
public readonly PropertyInfo EmailConfirmedSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, bool>(x => x.EmailConfirmed);
|
||||
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, string>(x => x.Name);
|
||||
public readonly PropertyInfo AccessFailedCountSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, int>(x => x.AccessFailedCount);
|
||||
@@ -439,5 +450,6 @@ namespace Umbraco.Core.Models.Identity
|
||||
groups => groups.GetHashCode());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -8,9 +8,9 @@ using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Models.Identity
|
||||
{
|
||||
public class IdentityProfile : Profile
|
||||
public class IdentityMapperProfile : Profile
|
||||
{
|
||||
public IdentityProfile(ILocalizedTextService textService, IEntityService entityService, IGlobalSettings globalSettings)
|
||||
public IdentityMapperProfile(ILocalizedTextService textService, IEntityService entityService, IGlobalSettings globalSettings)
|
||||
{
|
||||
CreateMap<IUser, BackOfficeIdentityUser>()
|
||||
.BeforeMap((src, dest) =>
|
||||
@@ -19,6 +19,7 @@ namespace Umbraco.Core.Models.Identity
|
||||
})
|
||||
.ConstructUsing(src => new BackOfficeIdentityUser(src.Id, src.Groups))
|
||||
.ForMember(dest => dest.LastLoginDateUtc, opt => opt.MapFrom(src => src.LastLoginDate.ToUniversalTime()))
|
||||
.ForMember(user => user.LastPasswordChangeDateUtc, expression => expression.MapFrom(user => user.LastPasswordChangeDate.ToUniversalTime()))
|
||||
.ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email))
|
||||
.ForMember(dest => dest.EmailConfirmed, opt => opt.MapFrom(src => src.EmailConfirmedDate.HasValue))
|
||||
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
|
||||
@@ -18,10 +18,8 @@ namespace Umbraco.Core.Models.Identity
|
||||
where TRole : IdentityUserRole<string>
|
||||
where TClaim : IdentityUserClaim<TKey>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
///
|
||||
/// Initializes a new instance of the <see cref="IdentityUser{TKey, TLogin, TRole, TClaim}"/> class.
|
||||
/// </summary>
|
||||
public IdentityUser()
|
||||
{
|
||||
@@ -75,6 +73,11 @@ namespace Umbraco.Core.Models.Identity
|
||||
/// </summary>
|
||||
public virtual DateTime? LockoutEndDateUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// DateTime in UTC when the password was last changed.
|
||||
/// </summary>
|
||||
public virtual DateTime? LastPasswordChangeDateUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is lockout enabled for this user
|
||||
/// </summary>
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
|
||||
/// <summary>
|
||||
/// Gets the published content type containing the property type.
|
||||
/// </summary>
|
||||
/// </summary>
|
||||
public PublishedContentType ContentType { get; internal set; } // internally set by PublishedContentType constructor
|
||||
|
||||
/// <summary>
|
||||
@@ -193,6 +193,20 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
_modelClrType = _converter == null ? typeof (object) : _converter.GetPropertyValueType(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a source value is an actual value, or not a value.
|
||||
/// </summary>
|
||||
/// <remarks>Used by property.HasValue and, for instance, in fallback scenarios.</remarks>
|
||||
public bool IsValue(object value)
|
||||
{
|
||||
// if we have a converter, use the converter,
|
||||
// otherwise use the old magic null & string comparisons
|
||||
|
||||
return _converter == null
|
||||
? value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false)
|
||||
: _converter.IsValue(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property cache level.
|
||||
/// </summary>
|
||||
|
||||
@@ -272,7 +272,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
if (orderBySystemField == false || orderBy.InvariantEquals(dbfield) == false)
|
||||
{
|
||||
// get alias, if aliased
|
||||
var matches = VersionableRepositoryBaseAliasRegex.For(SqlContext.SqlSyntax).Matches(sql.SQL);
|
||||
var matches = SqlContext.SqlSyntax.AliasRegex.Matches(sql.SQL);
|
||||
var match = matches.Cast<Match>().FirstOrDefault(m => m.Groups[1].Value.InvariantEquals(dbfield));
|
||||
if (match != null) dbfield = match.Groups[2].Value;
|
||||
|
||||
@@ -304,7 +304,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
// note: ContentTypeAlias is not properly managed because it's not part of the query to begin with!
|
||||
|
||||
// get alias, if aliased
|
||||
var matches = VersionableRepositoryBaseAliasRegex.For(SqlContext.SqlSyntax).Matches(sql.SQL);
|
||||
var matches = SqlContext.SqlSyntax.AliasRegex.Matches(sql.SQL);
|
||||
var match = matches.Cast<Match>().FirstOrDefault(m => m.Groups[1].Value.InvariantEquals(dbfield));
|
||||
if (match != null) dbfield = match.Groups[2].Value;
|
||||
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
internal static class VersionableRepositoryBaseAliasRegex
|
||||
{
|
||||
private static readonly Dictionary<Type, Regex> Regexes = new Dictionary<Type, Regex>();
|
||||
|
||||
public static Regex For(ISqlSyntaxProvider sqlSyntax)
|
||||
{
|
||||
var type = sqlSyntax.GetType();
|
||||
Regex aliasRegex;
|
||||
if (Regexes.TryGetValue(type, out aliasRegex))
|
||||
return aliasRegex;
|
||||
|
||||
var col = Regex.Escape(sqlSyntax.GetQuotedColumnName("column")).Replace("column", @"\w+");
|
||||
var fld = Regex.Escape(sqlSyntax.GetQuotedTableName("table") + ".").Replace("table", @"\w+") + col;
|
||||
aliasRegex = new Regex("(" + fld + @")\s+AS\s+(" + col + ")", RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase);
|
||||
Regexes[type] = aliasRegex;
|
||||
return aliasRegex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using NPoco;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
@@ -19,17 +20,6 @@ namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
string GetStringColumnWildcardComparison(string column, int paramIndex, TextColumnType columnType);
|
||||
string GetConcat(params string[] args);
|
||||
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType);
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType);
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType);
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType);
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType);
|
||||
|
||||
string GetQuotedTableName(string tableName);
|
||||
string GetQuotedColumnName(string columnName);
|
||||
string GetQuotedName(string name);
|
||||
@@ -70,6 +60,14 @@ namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
string FormatColumnRename(string tableName, string oldName, string newName);
|
||||
string FormatTableRename(string oldName, string newName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a regex matching aliased fields.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Matches "(table.column) AS (alias)" where table, column and alias are properly escaped.</para>
|
||||
/// </remarks>
|
||||
Regex AliasRegex { get; }
|
||||
|
||||
Sql<ISqlContext> SelectTop(Sql<ISqlContext> sql, int top);
|
||||
|
||||
bool SupportsClustered();
|
||||
|
||||
@@ -70,66 +70,6 @@ namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
public override string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType)
|
||||
{
|
||||
switch (columnType)
|
||||
{
|
||||
case TextColumnType.NVarchar:
|
||||
return base.GetStringColumnStartsWithComparison(column, value, columnType);
|
||||
case TextColumnType.NText:
|
||||
//MSSQL doesn't allow for upper methods with NText columns
|
||||
return $"{column} LIKE '{value}%'";
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(columnType));
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
public override string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType)
|
||||
{
|
||||
switch (columnType)
|
||||
{
|
||||
case TextColumnType.NVarchar:
|
||||
return base.GetStringColumnEndsWithComparison(column, value, columnType);
|
||||
case TextColumnType.NText:
|
||||
//MSSQL doesn't allow for upper methods with NText columns
|
||||
return $"{column} LIKE '%{value}'";
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(columnType));
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
public override string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType)
|
||||
{
|
||||
switch (columnType)
|
||||
{
|
||||
case TextColumnType.NVarchar:
|
||||
return base.GetStringColumnContainsComparison(column, value, columnType);
|
||||
case TextColumnType.NText:
|
||||
//MSSQL doesn't allow for upper methods with NText columns
|
||||
return $"{column} LIKE '%{value}%'";
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(columnType));
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
public override string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType)
|
||||
{
|
||||
switch (columnType)
|
||||
{
|
||||
case TextColumnType.NVarchar:
|
||||
return base.GetStringColumnContainsComparison(column, value, columnType);
|
||||
case TextColumnType.NText:
|
||||
//MSSQL doesn't allow for upper methods with NText columns
|
||||
return $"{column} LIKE '{value}'";
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(columnType));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This uses a the DbTypeMap created and custom mapping to resolve the SqlDbType
|
||||
/// </summary>
|
||||
|
||||
@@ -49,21 +49,6 @@ namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
return indexType;
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
public override string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType)
|
||||
{
|
||||
switch (columnType)
|
||||
{
|
||||
case TextColumnType.NVarchar:
|
||||
return base.GetStringColumnEqualComparison(column, value, columnType);
|
||||
case TextColumnType.NText:
|
||||
//MSSQL doesn't allow for = comparison with NText columns but allows this syntax
|
||||
return string.Format("{0} LIKE '{1}'", column, value);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("columnType");
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetConcat(params string[] args)
|
||||
{
|
||||
return "(" + string.Join("+", args) + ")";
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using NPoco;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
@@ -40,8 +41,17 @@ namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
DecimalColumnDefinition = string.Format(DecimalColumnDefinitionFormat, DefaultDecimalPrecision, DefaultDecimalScale);
|
||||
|
||||
InitColumnTypeMap();
|
||||
|
||||
// ReSharper disable VirtualMemberCallInConstructor
|
||||
// ok to call virtual GetQuotedXxxName here - they don't depend on any state
|
||||
var col = Regex.Escape(GetQuotedColumnName("column")).Replace("column", @"\w+");
|
||||
var fld = Regex.Escape(GetQuotedTableName("table") + ".").Replace("table", @"\w+") + col;
|
||||
// ReSharper restore VirtualMemberCallInConstructor
|
||||
AliasRegex = new Regex("(" + fld + @")\s+AS\s+(" + col + ")", RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
public Regex AliasRegex { get; }
|
||||
|
||||
public string GetWildcardPlaceholder()
|
||||
{
|
||||
return "%";
|
||||
@@ -137,41 +147,6 @@ namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
return $"upper({column}) LIKE upper(@{paramIndex})";
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
public virtual string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType)
|
||||
{
|
||||
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
|
||||
return $"upper({column}) = '{value.ToUpper()}'";
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
public virtual string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType)
|
||||
{
|
||||
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
|
||||
return $"upper({column}) LIKE '{value.ToUpper()}%'";
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
public virtual string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType)
|
||||
{
|
||||
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
|
||||
return $"upper({column}) LIKE '%{value.ToUpper()}'";
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
public virtual string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType)
|
||||
{
|
||||
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
|
||||
return $"upper({column}) LIKE '%{value.ToUpper()}%'";
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload with the parameter index instead")]
|
||||
public virtual string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType)
|
||||
{
|
||||
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
|
||||
return $"upper({column}) LIKE '{value.ToUpper()}'";
|
||||
}
|
||||
|
||||
public virtual string GetConcat(params string[] args)
|
||||
{
|
||||
return "concat(" + string.Join(",", args) + ")";
|
||||
|
||||
@@ -17,6 +17,11 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// <returns>A value indicating whether the converter supports a property type.</returns>
|
||||
bool IsConverter(PublishedPropertyType propertyType);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a source value is an actual value, or not a value.
|
||||
/// </summary>
|
||||
bool IsValue(object value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of values returned by the converter.
|
||||
/// </summary>
|
||||
|
||||
@@ -9,33 +9,24 @@ namespace Umbraco.Core.PropertyEditors
|
||||
public abstract class PropertyValueConverterBase : IPropertyValueConverter
|
||||
{
|
||||
public virtual bool IsConverter(PublishedPropertyType propertyType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
=> false;
|
||||
|
||||
public bool IsValue(object value)
|
||||
=> value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
|
||||
|
||||
public virtual Type GetPropertyValueType(PublishedPropertyType propertyType)
|
||||
{
|
||||
return typeof (object);
|
||||
}
|
||||
=> typeof (object);
|
||||
|
||||
public virtual PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType)
|
||||
{
|
||||
return PropertyCacheLevel.Snapshot;
|
||||
}
|
||||
=> PropertyCacheLevel.Snapshot;
|
||||
|
||||
public virtual object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
=> source;
|
||||
|
||||
public virtual object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
|
||||
{
|
||||
return inter;
|
||||
}
|
||||
=> inter;
|
||||
|
||||
public virtual object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
|
||||
{
|
||||
return inter?.ToString() ?? string.Empty;
|
||||
}
|
||||
=> inter?.ToString() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,6 +434,7 @@ namespace Umbraco.Core.Security
|
||||
/// </remarks>
|
||||
protected override async Task<IdentityResult> UpdatePassword(IUserPasswordStore<T, int> passwordStore, T user, string newPassword)
|
||||
{
|
||||
user.LastPasswordChangeDateUtc = DateTime.UtcNow;
|
||||
var userAwarePasswordHasher = PasswordHasher as IUserAwarePasswordHasher<BackOfficeIdentityUser, int>;
|
||||
if (userAwarePasswordHasher == null)
|
||||
return await base.UpdatePassword(passwordStore, user, newPassword);
|
||||
@@ -484,15 +485,22 @@ namespace Umbraco.Core.Security
|
||||
|
||||
#endregion
|
||||
|
||||
public override Task<IdentityResult> SetLockoutEndDateAsync(int userId, DateTimeOffset lockoutEnd)
|
||||
public override async Task<IdentityResult> SetLockoutEndDateAsync(int userId, DateTimeOffset lockoutEnd)
|
||||
{
|
||||
var result = base.SetLockoutEndDateAsync(userId, lockoutEnd);
|
||||
var result = await base.SetLockoutEndDateAsync(userId, lockoutEnd);
|
||||
|
||||
// The way we unlock is by setting the lockoutEnd date to the current datetime
|
||||
if (result.Result.Succeeded && lockoutEnd >= DateTimeOffset.UtcNow)
|
||||
if (result.Succeeded && lockoutEnd >= DateTimeOffset.UtcNow)
|
||||
{
|
||||
RaiseAccountLockedEvent(userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseAccountUnlockedEvent(userId);
|
||||
//Resets the login attempt fails back to 0 when unlock is clicked
|
||||
await ResetAccessFailedCountAsync(userId);
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -517,13 +525,36 @@ namespace Umbraco.Core.Security
|
||||
|
||||
|
||||
|
||||
|
||||
public override Task<IdentityResult> AccessFailedAsync(int userId)
|
||||
/// <summary>
|
||||
/// Overides the microsoft ASP.NET user managment method
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns>
|
||||
/// returns a Async Task<IdentityResult>
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Doesnt set fail attempts back to 0
|
||||
/// </remarks>
|
||||
public override async Task<IdentityResult> AccessFailedAsync(int userId)
|
||||
{
|
||||
var result = base.AccessFailedAsync(userId);
|
||||
var lockoutStore = (IUserLockoutStore<BackOfficeIdentityUser, int>)Store;
|
||||
var user = await FindByIdAsync(userId);
|
||||
if (user == null)
|
||||
throw new InvalidOperationException("No user found by user id " + userId);
|
||||
|
||||
var count = await lockoutStore.IncrementAccessFailedCountAsync(user);
|
||||
|
||||
if (count >= MaxFailedAccessAttemptsBeforeLockout)
|
||||
{
|
||||
await lockoutStore.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(DefaultAccountLockoutTimeSpan));
|
||||
//NOTE: in normal aspnet identity this would do set the number of failed attempts back to 0
|
||||
//here we are persisting the value for the back office
|
||||
}
|
||||
|
||||
var result = await UpdateAsync(user);
|
||||
|
||||
//Slightly confusing: this will return a Success if we successfully update the AccessFailed count
|
||||
if (result.Result.Succeeded)
|
||||
if (result.Succeeded)
|
||||
RaiseLoginFailedEvent(userId);
|
||||
|
||||
return result;
|
||||
|
||||
@@ -637,6 +637,13 @@ namespace Umbraco.Core.Security
|
||||
anythingChanged = true;
|
||||
user.LastLoginDate = identityUser.LastLoginDateUtc.Value.ToLocalTime();
|
||||
}
|
||||
if (identityUser.IsPropertyDirty("LastPasswordChangeDateUtc")
|
||||
|| (user.LastPasswordChangeDate != default(DateTime) && identityUser.LastPasswordChangeDateUtc.HasValue == false)
|
||||
|| identityUser.LastPasswordChangeDateUtc.HasValue && user.LastPasswordChangeDate.ToUniversalTime() != identityUser.LastPasswordChangeDateUtc.Value)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.LastPasswordChangeDate = identityUser.LastPasswordChangeDateUtc.Value.ToLocalTime();
|
||||
}
|
||||
if (identityUser.IsPropertyDirty("EmailConfirmed")
|
||||
|| (user.EmailConfirmedDate.HasValue && user.EmailConfirmedDate.Value != default(DateTime) && identityUser.EmailConfirmed == false)
|
||||
|| ((user.EmailConfirmedDate.HasValue == false || user.EmailConfirmedDate.Value == default(DateTime)) && identityUser.EmailConfirmed))
|
||||
|
||||
@@ -1841,7 +1841,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IContent>(copy, TreeChangeTypes.RefreshBranch).ToEventArgs());
|
||||
foreach (var x in copies)
|
||||
scope.Events.Dispatch(Copied, this, new CopyEventArgs<IContent>(x.Item1, x.Item2, false, x.Item2.ParentId, relateToOriginal));
|
||||
Audit(AuditType.Copy, "Copy Content performed by user", content.WriterId, content.Id);
|
||||
Audit(AuditType.Copy, "Copy Content performed by user", userId, content.Id);
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
@@ -332,16 +332,23 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
//NOTE: This isn't pretty but we need to maintain backwards compatibility so we cannot change
|
||||
//fixme: This isn't pretty because we we're required to maintain backwards compatibility so we could not change
|
||||
// the event args here. The other option is to create a different event with different event
|
||||
// args specifically for this method... which also isn't pretty. So for now, we'll use this
|
||||
// dictionary approach to store 'additional data' in.
|
||||
// args specifically for this method... which also isn't pretty. So fix this in v8!
|
||||
var additionalData = new Dictionary<string, object>
|
||||
{
|
||||
{ "CreateTemplateForContentType", true },
|
||||
{ "ContentTypeAlias", contentTypeAlias },
|
||||
};
|
||||
|
||||
// check that the template hasn't been created on disk before creating the content type
|
||||
// if it exists, set the new template content to the existing file content
|
||||
string content = GetViewContent(contentTypeAlias);
|
||||
if (content.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
template.Content = content;
|
||||
}
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<ITemplate>(template, true, evtMsgs, additionalData);
|
||||
@@ -368,6 +375,15 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
Content = content
|
||||
};
|
||||
|
||||
// check that the template hasn't been created on disk before creating the content type
|
||||
// if it exists, set the new template content to the existing file content
|
||||
string existingContent = GetViewContent(template.Alias);
|
||||
if (existingContent.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
template.Content = content;
|
||||
}
|
||||
|
||||
if (masterTemplate != null)
|
||||
{
|
||||
template.SetMasterTemplate(masterTemplate);
|
||||
@@ -659,10 +675,26 @@ namespace Umbraco.Core.Services.Implement
|
||||
return _templateRepository.GetFileSize(filepath);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetViewContent(string fileName)
|
||||
{
|
||||
if (fileName.IsNullOrWhiteSpace())
|
||||
throw new ArgumentNullException(nameof(fileName));
|
||||
|
||||
#endregion
|
||||
if (!fileName.EndsWith(".cshtml"))
|
||||
fileName = string.Concat(fileName, ".cshtml");
|
||||
|
||||
#region Partial Views
|
||||
var fs = _templateRepository.GetFileContentStream(fileName);
|
||||
if (fs == null) return string.Empty;
|
||||
using (var view = new StreamReader(fs))
|
||||
{
|
||||
return view.ReadToEnd().Trim();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Partial Views
|
||||
|
||||
public IEnumerable<string> GetPartialViewSnippetNames(params string[] filterNames)
|
||||
{
|
||||
|
||||
@@ -357,6 +357,7 @@
|
||||
<Compile Include="Models\IAuditEntry.cs" />
|
||||
<Compile Include="Models\IAuditItem.cs" />
|
||||
<Compile Include="Models\IConsent.cs" />
|
||||
<Compile Include="Models\Identity\IdentityMapperProfile.cs" />
|
||||
<Compile Include="Models\Membership\MemberExportModel.cs" />
|
||||
<Compile Include="Models\Membership\MemberExportProperty.cs" />
|
||||
<Compile Include="Models\PathValidationExtensions.cs" />
|
||||
@@ -665,7 +666,6 @@
|
||||
<Compile Include="Models\IDataType.cs" />
|
||||
<Compile Include="Models\IDeepCloneable.cs" />
|
||||
<Compile Include="Models\Identity\BackOfficeIdentityUser.cs" />
|
||||
<Compile Include="Models\Identity\IdentityProfile.cs" />
|
||||
<Compile Include="Models\Identity\IdentityUser.cs" />
|
||||
<Compile Include="Models\Identity\IdentityUserClaim.cs" />
|
||||
<Compile Include="Models\Identity\IdentityUserLogin.cs" />
|
||||
@@ -1208,7 +1208,6 @@
|
||||
<Compile Include="Persistence\Repositories\Implement\UserGroupRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\Implement\UserRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\Implement\ContentRepositoryBase.cs" />
|
||||
<Compile Include="Persistence\Repositories\Implement\VersionableRepositoryBaseAliasRegex.cs" />
|
||||
<Compile Include="Persistence\SqlContext.cs" />
|
||||
<Compile Include="Persistence\SqlSyntax\ColumnInfo.cs" />
|
||||
<Compile Include="Persistence\SqlSyntax\DbTypes.cs" />
|
||||
|
||||
@@ -16,28 +16,28 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
|
||||
[Test]
|
||||
public void EmailAddress()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.NotificationEmailAddress == "robot@umbraco.dk");
|
||||
Assert.AreEqual(SettingsSection.Content.NotificationEmailAddress, "robot@umbraco.dk");
|
||||
}
|
||||
[Test]
|
||||
public virtual void DisableHtmlEmail()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.DisableHtmlEmail == true);
|
||||
Assert.IsTrue(SettingsSection.Content.DisableHtmlEmail);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void Can_Set_Multiple()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.Error404Collection.Count() == 3);
|
||||
Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).Culture == "default");
|
||||
Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).ContentId == 1047);
|
||||
Assert.AreEqual(SettingsSection.Content.Error404Collection.Count(), 3);
|
||||
Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(0).Culture, "default");
|
||||
Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(0).ContentId, 1047);
|
||||
Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).HasContentId);
|
||||
Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(0).HasContentKey);
|
||||
Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(1).Culture == "en-US");
|
||||
Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(1).ContentXPath == "$site/error [@name = 'error']");
|
||||
Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(1).Culture, "en-US");
|
||||
Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(1).ContentXPath, "$site/error [@name = 'error']");
|
||||
Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(1).HasContentId);
|
||||
Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(1).HasContentKey);
|
||||
Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(2).Culture == "en-UK");
|
||||
Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(2).ContentKey == new Guid("8560867F-B88F-4C74-A9A4-679D8E5B3BFC"));
|
||||
Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(2).Culture, "en-UK");
|
||||
Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(2).ContentKey, new Guid("8560867F-B88F-4C74-A9A4-679D8E5B3BFC"));
|
||||
Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(2).HasContentKey);
|
||||
Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(2).HasContentId);
|
||||
}
|
||||
@@ -45,7 +45,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
|
||||
[Test]
|
||||
public void ScriptFolderPath()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.ScriptFolderPath == "/scripts");
|
||||
Assert.AreEqual(SettingsSection.Content.ScriptFolderPath, "/scripts");
|
||||
}
|
||||
[Test]
|
||||
public void ScriptFileTypes()
|
||||
@@ -55,7 +55,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
|
||||
[Test]
|
||||
public void DisableScriptEditor()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.ScriptEditorDisable == false);
|
||||
Assert.AreEqual(SettingsSection.Content.ScriptEditorDisable, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -71,80 +71,79 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
|
||||
[Test]
|
||||
public virtual void ImageAutoFillProperties()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.Count() == 2);
|
||||
Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).Alias == "umbracoFile");
|
||||
Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).WidthFieldAlias == "umbracoWidth");
|
||||
Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).HeightFieldAlias == "umbracoHeight");
|
||||
Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).LengthFieldAlias == "umbracoBytes");
|
||||
Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).ExtensionFieldAlias == "umbracoExtension");
|
||||
Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).Alias == "umbracoFile2");
|
||||
Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).WidthFieldAlias == "umbracoWidth2");
|
||||
Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).HeightFieldAlias == "umbracoHeight2");
|
||||
Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).LengthFieldAlias == "umbracoBytes2");
|
||||
Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).ExtensionFieldAlias == "umbracoExtension2");
|
||||
Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.Count(), 2);
|
||||
Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).Alias, "umbracoFile");
|
||||
Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).WidthFieldAlias, "umbracoWidth");
|
||||
Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).HeightFieldAlias, "umbracoHeight");
|
||||
Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).LengthFieldAlias, "umbracoBytes");
|
||||
Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).ExtensionFieldAlias, "umbracoExtension");
|
||||
Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).Alias, "umbracoFile2");
|
||||
Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).WidthFieldAlias, "umbracoWidth2");
|
||||
Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).HeightFieldAlias, "umbracoHeight2");
|
||||
Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).LengthFieldAlias, "umbracoBytes2");
|
||||
Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).ExtensionFieldAlias, "umbracoExtension2");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UploadAllowDirectories()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.UploadAllowDirectories == true);
|
||||
Assert.IsTrue(SettingsSection.Content.UploadAllowDirectories);
|
||||
}
|
||||
[Test]
|
||||
public void DefaultDocumentTypeProperty()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.DefaultDocumentTypeProperty == "Textstring");
|
||||
Assert.AreEqual(SettingsSection.Content.DefaultDocumentTypeProperty, "Textstring");
|
||||
}
|
||||
[Test]
|
||||
public void GlobalPreviewStorageEnabled()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.GlobalPreviewStorageEnabled == false);
|
||||
Assert.IsFalse(SettingsSection.Content.GlobalPreviewStorageEnabled);
|
||||
}
|
||||
[Test]
|
||||
public void CloneXmlContent()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.CloneXmlContent == true);
|
||||
Assert.IsTrue(SettingsSection.Content.CloneXmlContent);
|
||||
}
|
||||
[Test]
|
||||
public void EnsureUniqueNaming()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.EnsureUniqueNaming == true);
|
||||
Assert.IsTrue(SettingsSection.Content.EnsureUniqueNaming);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void ForceSafeAliases()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.ForceSafeAliases == true);
|
||||
Assert.IsTrue(SettingsSection.Content.ForceSafeAliases);
|
||||
}
|
||||
[Test]
|
||||
public void XmlCacheEnabled()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.XmlCacheEnabled == true);
|
||||
Assert.IsTrue(SettingsSection.Content.XmlCacheEnabled);
|
||||
}
|
||||
[Test]
|
||||
public void ContinouslyUpdateXmlDiskCache()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.ContinouslyUpdateXmlDiskCache == true);
|
||||
Assert.IsTrue(SettingsSection.Content.ContinouslyUpdateXmlDiskCache);
|
||||
}
|
||||
[Test]
|
||||
public virtual void XmlContentCheckForDiskChanges()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.XmlContentCheckForDiskChanges == true);
|
||||
Assert.IsTrue(SettingsSection.Content.XmlContentCheckForDiskChanges);
|
||||
}
|
||||
[Test]
|
||||
public void EnableSplashWhileLoading()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.EnableSplashWhileLoading == false);
|
||||
Assert.IsFalse(SettingsSection.Content.EnableSplashWhileLoading);
|
||||
}
|
||||
[Test]
|
||||
public void PropertyContextHelpOption()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.PropertyContextHelpOption == "text");
|
||||
Assert.AreEqual(SettingsSection.Content.PropertyContextHelpOption, "text");
|
||||
}
|
||||
[Test]
|
||||
public void PreviewBadge()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.PreviewBadge == @"<a id=""umbracoPreviewBadge"" style=""position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{0}/assets/img/preview-mode-badge.png') no-repeat;"" href=""{0}/endPreview.aspx?redir={1}""><span style=""display:none;"">In Preview Mode - click to end</span></a>");
|
||||
Assert.AreEqual(SettingsSection.Content.PreviewBadge, @"<a id=""umbracoPreviewBadge"" style=""z-index:99999; position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{0}/assets/img/preview-mode-badge.png') no-repeat;"" href=""#"" OnClick=""javascript:window.top.location.href = '{0}/endPreview.aspx?redir={1}'""><span style=""display:none;"">In Preview Mode - click to end</span></a>");
|
||||
}
|
||||
[Test]
|
||||
public void ResolveUrlsFromTextString()
|
||||
@@ -154,7 +153,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
|
||||
[Test]
|
||||
public void MacroErrors()
|
||||
{
|
||||
Assert.IsTrue(SettingsSection.Content.MacroErrorBehaviour == MacroErrorBehaviour.Inline);
|
||||
Assert.AreEqual(SettingsSection.Content.MacroErrorBehaviour, MacroErrorBehaviour.Inline);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -75,7 +75,10 @@
|
||||
|
||||
<ForceSafeAliases>true</ForceSafeAliases>
|
||||
|
||||
<PreviewBadge><![CDATA[<a id="umbracoPreviewBadge" style="position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{0}/assets/img/preview-mode-badge.png') no-repeat;" href="{0}/endPreview.aspx?redir={1}"><span style="display:none;">In Preview Mode - click to end</span></a>]]></PreviewBadge>
|
||||
<PreviewBadge>
|
||||
<![CDATA[
|
||||
<a id="umbracoPreviewBadge" style="z-index:99999; position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{0}/assets/img/preview-mode-badge.png') no-repeat;" href="#" OnClick="javascript:window.top.location.href = '{0}/endPreview.aspx?redir={1}'"><span style="display:none;">In Preview Mode - click to end</span></a>
|
||||
]]></PreviewBadge>
|
||||
|
||||
<!-- How Umbraco should handle errors during macro execution. Can be one of the following values:
|
||||
- inline - show an inline error within the macro but allow the page to continue rendering. Historial Umbraco behaviour.
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace Umbraco.Tests.FrontEnd
|
||||
{
|
||||
Current.Reset();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void Truncate_Simple()
|
||||
{
|
||||
@@ -165,7 +164,7 @@ namespace Umbraco.Tests.FrontEnd
|
||||
|
||||
var result = helper.StripHtml(SampleWithBoldAndAnchorElements, tags).ToString();
|
||||
|
||||
Assert.AreEqual("Hello world, this is some text <a href='blah'>with a link</a>", result);
|
||||
Assert.AreEqual(SampleWithAnchorElement, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -180,236 +179,6 @@ namespace Umbraco.Tests.FrontEnd
|
||||
Assert.AreEqual("Hello world, is some text with a link", result);
|
||||
}
|
||||
|
||||
// ------- Int32 conversion tests
|
||||
[Test]
|
||||
public static void Converting_boxed_34_to_an_int_returns_34()
|
||||
{
|
||||
// Arrange
|
||||
const int sample = 34;
|
||||
|
||||
// Act
|
||||
bool success = UmbracoHelper.ConvertIdObjectToInt(
|
||||
sample,
|
||||
out int result
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(success);
|
||||
Assert.That(result, Is.EqualTo(34));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void Converting_string_54_to_an_int_returns_54()
|
||||
{
|
||||
// Arrange
|
||||
const string sample = "54";
|
||||
|
||||
// Act
|
||||
bool success = UmbracoHelper.ConvertIdObjectToInt(
|
||||
sample,
|
||||
out int result
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(success);
|
||||
Assert.That(result, Is.EqualTo(54));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void Converting_hello_to_an_int_returns_false()
|
||||
{
|
||||
// Arrange
|
||||
const string sample = "Hello";
|
||||
|
||||
// Act
|
||||
bool success = UmbracoHelper.ConvertIdObjectToInt(
|
||||
sample,
|
||||
out int result
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(success);
|
||||
Assert.That(result, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void Converting_unsupported_object_to_an_int_returns_false()
|
||||
{
|
||||
// Arrange
|
||||
var clearlyWillNotConvertToInt = new StringBuilder(0);
|
||||
|
||||
// Act
|
||||
bool success = UmbracoHelper.ConvertIdObjectToInt(
|
||||
clearlyWillNotConvertToInt,
|
||||
out int result
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(success);
|
||||
Assert.That(result, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
// ------- GUID conversion tests
|
||||
[Test]
|
||||
public static void Converting_boxed_guid_to_a_guid_returns_original_guid_value()
|
||||
{
|
||||
// Arrange
|
||||
Guid sample = Guid.NewGuid();
|
||||
|
||||
// Act
|
||||
bool success = UmbracoHelper.ConvertIdObjectToGuid(
|
||||
sample,
|
||||
out Guid result
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(success);
|
||||
Assert.That(result, Is.EqualTo(sample));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void Converting_string_guid_to_a_guid_returns_original_guid_value()
|
||||
{
|
||||
// Arrange
|
||||
Guid sample = Guid.NewGuid();
|
||||
|
||||
// Act
|
||||
bool success = UmbracoHelper.ConvertIdObjectToGuid(
|
||||
sample.ToString(),
|
||||
out Guid result
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(success);
|
||||
Assert.That(result, Is.EqualTo(sample));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void Converting_hello_to_a_guid_returns_false()
|
||||
{
|
||||
// Arrange
|
||||
const string sample = "Hello";
|
||||
|
||||
// Act
|
||||
bool success = UmbracoHelper.ConvertIdObjectToGuid(
|
||||
sample,
|
||||
out Guid result
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(success);
|
||||
Assert.That(result, Is.EqualTo(new Guid("00000000-0000-0000-0000-000000000000")));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void Converting_unsupported_object_to_a_guid_returns_false()
|
||||
{
|
||||
// Arrange
|
||||
var clearlyWillNotConvertToGuid = new StringBuilder(0);
|
||||
|
||||
// Act
|
||||
bool success = UmbracoHelper.ConvertIdObjectToGuid(
|
||||
clearlyWillNotConvertToGuid,
|
||||
out Guid result
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(success);
|
||||
Assert.That(result, Is.EqualTo(new Guid("00000000-0000-0000-0000-000000000000")));
|
||||
}
|
||||
|
||||
// ------- UDI Conversion Tests
|
||||
/// <remarks>
|
||||
/// This requires PluginManager.Current to be initialised before
|
||||
/// running.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public static void Converting_boxed_udi_to_a_udi_returns_original_udi_value()
|
||||
{
|
||||
// Arrange
|
||||
Udi.ResetUdiTypes();
|
||||
Udi sample = new GuidUdi(Constants.UdiEntityType.AnyGuid, Guid.NewGuid());
|
||||
|
||||
// Act
|
||||
bool success = UmbracoHelper.ConvertIdObjectToUdi(
|
||||
sample,
|
||||
out Udi result
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(success);
|
||||
Assert.That(result, Is.EqualTo(sample));
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// This requires PluginManager.Current to be initialised before
|
||||
/// running.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void Converting_string_udi_to_a_udi_returns_original_udi_value()
|
||||
{
|
||||
// Arrange
|
||||
SetUpDependencyContainer();
|
||||
Udi.ResetUdiTypes();
|
||||
Udi sample = new GuidUdi(Constants.UdiEntityType.AnyGuid, Guid.NewGuid());
|
||||
|
||||
// Act
|
||||
bool success = UmbracoHelper.ConvertIdObjectToUdi(
|
||||
sample.ToString(),
|
||||
out Udi result
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(success, "Conversion of UDI failed.");
|
||||
Assert.That(result, Is.EqualTo(sample));
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// This requires PluginManager.Current to be initialised before
|
||||
/// running.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void Converting_hello_to_a_udi_returns_false()
|
||||
{
|
||||
// Arrange
|
||||
SetUpDependencyContainer();
|
||||
Udi.ResetUdiTypes();
|
||||
const string sample = "Hello";
|
||||
|
||||
// Act
|
||||
bool success = UmbracoHelper.ConvertIdObjectToUdi(
|
||||
sample,
|
||||
out Udi result
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(success);
|
||||
Assert.That(result, Is.Null);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// This requires PluginManager.Current to be initialised before
|
||||
/// running.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public static void Converting_unsupported_object_to_a_udi_returns_false()
|
||||
{
|
||||
// Arrange
|
||||
Udi.ResetUdiTypes();
|
||||
|
||||
var clearlyWillNotConvertToGuid = new StringBuilder(0);
|
||||
|
||||
// Act
|
||||
bool success = UmbracoHelper.ConvertIdObjectToUdi(
|
||||
clearlyWillNotConvertToGuid,
|
||||
out Udi result
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(success);
|
||||
Assert.That(result, Is.Null);
|
||||
}
|
||||
|
||||
// ------- Int32 conversion tests
|
||||
[Test]
|
||||
public static void Converting_Boxed_34_To_An_Int_Returns_34()
|
||||
@@ -549,13 +318,14 @@ namespace Umbraco.Tests.FrontEnd
|
||||
}
|
||||
|
||||
// ------- UDI Conversion Tests
|
||||
/// <remarks>
|
||||
/// This requires PluginManager.Current to be initialised before running.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void Converting_Boxed_Udi_To_A_Udi_Returns_Original_Udi_Value()
|
||||
public static void Converting_Boxed_Udi_To_A_Udi_Returns_Original_Udi_Value()
|
||||
{
|
||||
// Arrange
|
||||
SetUpDependencyContainer();
|
||||
Udi.ResetUdiTypes();
|
||||
|
||||
Udi sample = new GuidUdi(Constants.UdiEntityType.AnyGuid, Guid.NewGuid());
|
||||
|
||||
// Act
|
||||
@@ -569,13 +339,15 @@ namespace Umbraco.Tests.FrontEnd
|
||||
Assert.That(result, Is.EqualTo(sample));
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// This requires PluginManager.Current to be initialised before running.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void Converting_String_Udi_To_A_Udi_Returns_Original_Udi_Value()
|
||||
{
|
||||
// Arrange
|
||||
SetUpDependencyContainer();
|
||||
Udi.ResetUdiTypes();
|
||||
|
||||
Udi sample = new GuidUdi(Constants.UdiEntityType.AnyGuid, Guid.NewGuid());
|
||||
|
||||
// Act
|
||||
@@ -585,22 +357,24 @@ namespace Umbraco.Tests.FrontEnd
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(success);
|
||||
Assert.IsTrue(success, "Conversion of UDI failed.");
|
||||
Assert.That(result, Is.EqualTo(sample));
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// This requires PluginManager.Current to be initialised before running.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void Converting_Hello_To_A_Udi_Returns_False()
|
||||
{
|
||||
// Arrange
|
||||
SetUpDependencyContainer();
|
||||
Udi.ResetUdiTypes();
|
||||
|
||||
const string SAMPLE = "Hello";
|
||||
const string sample = "Hello";
|
||||
|
||||
// Act
|
||||
bool success = UmbracoHelper.ConvertIdObjectToUdi(
|
||||
SAMPLE,
|
||||
sample,
|
||||
out Udi result
|
||||
);
|
||||
|
||||
@@ -609,11 +383,13 @@ namespace Umbraco.Tests.FrontEnd
|
||||
Assert.That(result, Is.Null);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// This requires PluginManager.Current to be initialised before running.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void Converting_Unsupported_Object_To_A_Udi_Returns_False()
|
||||
public static void Converting_Unsupported_Object_To_A_Udi_Returns_False()
|
||||
{
|
||||
// Arrange
|
||||
SetUpDependencyContainer();
|
||||
Udi.ResetUdiTypes();
|
||||
|
||||
var clearlyWillNotConvertToGuid = new StringBuilder(0);
|
||||
|
||||
@@ -667,9 +667,9 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RegexAliasTest()
|
||||
public void AliasRegexTest()
|
||||
{
|
||||
var regex = VersionableRepositoryBaseAliasRegex.For(new SqlServerSyntaxProvider(new Lazy<IScopeProvider>(() => null)));
|
||||
var regex = new SqlServerSyntaxProvider(new Lazy<IScopeProvider>(() => null)).AliasRegex;
|
||||
Assert.AreEqual(@"(\[\w+]\.\[\w+])\s+AS\s+(\[\w+])", regex.ToString());
|
||||
const string sql = "SELECT [table].[column1] AS [alias1], [table].[column2] AS [alias2] FROM [table];";
|
||||
var matches = regex.Matches(sql);
|
||||
|
||||
@@ -47,6 +47,9 @@ namespace Umbraco.Tests.Published
|
||||
|
||||
private class SimpleConverter1 : IPropertyValueConverter
|
||||
{
|
||||
public bool IsValue(object value)
|
||||
=> value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
|
||||
|
||||
public bool IsConverter(PublishedPropertyType propertyType)
|
||||
=> propertyType.EditorAlias.InvariantEquals("Umbraco.Void");
|
||||
|
||||
@@ -117,6 +120,9 @@ namespace Umbraco.Tests.Published
|
||||
_cacheLevel = cacheLevel;
|
||||
}
|
||||
|
||||
public bool IsValue(object value)
|
||||
=> value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
|
||||
|
||||
public bool IsConverter(PublishedPropertyType propertyType)
|
||||
=> propertyType.EditorAlias.InvariantEquals("Umbraco.Void");
|
||||
|
||||
|
||||
@@ -210,6 +210,9 @@ namespace Umbraco.Tests.Published
|
||||
public int SourceConverts { get; private set; }
|
||||
public int InterConverts { get; private set; }
|
||||
|
||||
public bool IsValue(object value)
|
||||
=> value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
|
||||
|
||||
public bool IsConverter(PublishedPropertyType propertyType)
|
||||
=> propertyType.EditorAlias.InvariantEquals("Umbraco.Void");
|
||||
|
||||
@@ -235,4 +238,4 @@ namespace Umbraco.Tests.Published
|
||||
=> ((int) inter).ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,9 @@ namespace Umbraco.Tests.PublishedContent
|
||||
<testRecursive><![CDATA[]]></testRecursive>
|
||||
</Home>
|
||||
<CustomDocument id=""1177"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""custom sub 1"" urlName=""custom-sub-1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1177"" isDoc="""" />
|
||||
<CustomDocument id=""1178"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-16T14:23:35"" nodeName=""custom sub 2"" urlName=""custom-sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1178"" isDoc="""" />
|
||||
<CustomDocument id=""1178"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-16T14:23:35"" nodeName=""custom sub 2"" urlName=""custom-sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1178"" isDoc="""">
|
||||
<CustomDocument id=""1179"" parentID=""1178"" level=""4"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""custom sub sub 1"" urlName=""custom-sub-sub-1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1178,1179"" isDoc="""" />
|
||||
</CustomDocument>
|
||||
<Home id=""1176"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-20T18:08:08"" updateDate=""2012-07-20T19:10:52"" nodeName=""Sub 3"" urlName=""sub-3"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1176"" isDoc="""" key=""CDB83BBC-A83B-4BA6-93B8-AADEF67D3C09"">
|
||||
<content><![CDATA[]]></content>
|
||||
<umbracoNaviHide>1</umbracoNaviHide>
|
||||
@@ -322,7 +324,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
{
|
||||
var doc = GetNode(1046);
|
||||
|
||||
var expected = new[] {1046, 1173, 1174, 1177, 1178, 1176, 1175, 4444, 1172};
|
||||
var expected = new[] { 1046, 1173, 1174, 1177, 1178, 1179, 1176, 1175, 4444, 1172 };
|
||||
var exindex = 0;
|
||||
|
||||
// must respect the XPath descendants-or-self axis!
|
||||
@@ -559,6 +561,93 @@ namespace Umbraco.Tests.PublishedContent
|
||||
Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1046 }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsAncestor()
|
||||
{
|
||||
// Structure:
|
||||
// - Root : 1046 (no parent)
|
||||
// -- Home: 1173 (parent 1046)
|
||||
// -- Custom Doc: 1178 (parent 1173)
|
||||
// --- Custom Doc2: 1179 (parent: 1178)
|
||||
// - Custom Doc3: 1172 (no parent)
|
||||
|
||||
var home = GetNode(1173);
|
||||
var root = GetNode(1046);
|
||||
var customDoc = GetNode(1178);
|
||||
var customDoc2 = GetNode(1179);
|
||||
var customDoc3 = GetNode(1172);
|
||||
|
||||
Assert.IsFalse(root.IsAncestor(customDoc3));
|
||||
Assert.IsTrue(root.IsAncestor(customDoc2));
|
||||
Assert.IsTrue(root.IsAncestor(customDoc));
|
||||
Assert.IsTrue(root.IsAncestor(home));
|
||||
Assert.IsFalse(root.IsAncestor(root));
|
||||
|
||||
Assert.IsFalse(home.IsAncestor(customDoc3));
|
||||
Assert.IsTrue(home.IsAncestor(customDoc2));
|
||||
Assert.IsTrue(home.IsAncestor(customDoc));
|
||||
Assert.IsFalse(home.IsAncestor(home));
|
||||
Assert.IsFalse(home.IsAncestor(root));
|
||||
|
||||
Assert.IsFalse(customDoc.IsAncestor(customDoc3));
|
||||
Assert.IsTrue(customDoc.IsAncestor(customDoc2));
|
||||
Assert.IsFalse(customDoc.IsAncestor(customDoc));
|
||||
Assert.IsFalse(customDoc.IsAncestor(home));
|
||||
Assert.IsFalse(customDoc.IsAncestor(root));
|
||||
|
||||
Assert.IsFalse(customDoc2.IsAncestor(customDoc3));
|
||||
Assert.IsFalse(customDoc2.IsAncestor(customDoc2));
|
||||
Assert.IsFalse(customDoc2.IsAncestor(customDoc));
|
||||
Assert.IsFalse(customDoc2.IsAncestor(home));
|
||||
Assert.IsFalse(customDoc2.IsAncestor(root));
|
||||
|
||||
Assert.IsFalse(customDoc3.IsAncestor(customDoc3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsAncestorOrSelf()
|
||||
{
|
||||
// Structure:
|
||||
// - Root : 1046 (no parent)
|
||||
// -- Home: 1173 (parent 1046)
|
||||
// -- Custom Doc: 1178 (parent 1173)
|
||||
// --- Custom Doc2: 1179 (parent: 1178)
|
||||
// - Custom Doc3: 1172 (no parent)
|
||||
|
||||
var home = GetNode(1173);
|
||||
var root = GetNode(1046);
|
||||
var customDoc = GetNode(1178);
|
||||
var customDoc2 = GetNode(1179);
|
||||
var customDoc3 = GetNode(1172);
|
||||
|
||||
Assert.IsFalse(root.IsAncestorOrSelf(customDoc3));
|
||||
Assert.IsTrue(root.IsAncestorOrSelf(customDoc2));
|
||||
Assert.IsTrue(root.IsAncestorOrSelf(customDoc));
|
||||
Assert.IsTrue(root.IsAncestorOrSelf(home));
|
||||
Assert.IsTrue(root.IsAncestorOrSelf(root));
|
||||
|
||||
Assert.IsFalse(home.IsAncestorOrSelf(customDoc3));
|
||||
Assert.IsTrue(home.IsAncestorOrSelf(customDoc2));
|
||||
Assert.IsTrue(home.IsAncestorOrSelf(customDoc));
|
||||
Assert.IsTrue(home.IsAncestorOrSelf(home));
|
||||
Assert.IsFalse(home.IsAncestorOrSelf(root));
|
||||
|
||||
Assert.IsFalse(customDoc.IsAncestorOrSelf(customDoc3));
|
||||
Assert.IsTrue(customDoc.IsAncestorOrSelf(customDoc2));
|
||||
Assert.IsTrue(customDoc.IsAncestorOrSelf(customDoc));
|
||||
Assert.IsFalse(customDoc.IsAncestorOrSelf(home));
|
||||
Assert.IsFalse(customDoc.IsAncestorOrSelf(root));
|
||||
|
||||
Assert.IsFalse(customDoc2.IsAncestorOrSelf(customDoc3));
|
||||
Assert.IsTrue(customDoc2.IsAncestorOrSelf(customDoc2));
|
||||
Assert.IsFalse(customDoc2.IsAncestorOrSelf(customDoc));
|
||||
Assert.IsFalse(customDoc2.IsAncestorOrSelf(home));
|
||||
Assert.IsFalse(customDoc2.IsAncestorOrSelf(root));
|
||||
|
||||
Assert.IsTrue(customDoc3.IsAncestorOrSelf(customDoc3));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Descendants_Or_Self()
|
||||
{
|
||||
@@ -568,7 +657,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
Assert.AreEqual(8, result.Length);
|
||||
Assert.AreEqual(9, result.Count());
|
||||
Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1046, 1173, 1174, 1176, 1175 }));
|
||||
}
|
||||
|
||||
@@ -581,10 +670,96 @@ namespace Umbraco.Tests.PublishedContent
|
||||
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
Assert.AreEqual(7, result.Length);
|
||||
Assert.AreEqual(8, result.Count());
|
||||
Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1174, 1176, 1175, 4444 }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDescendant()
|
||||
{
|
||||
// Structure:
|
||||
// - Root : 1046 (no parent)
|
||||
// -- Home: 1173 (parent 1046)
|
||||
// -- Custom Doc: 1178 (parent 1173)
|
||||
// --- Custom Doc2: 1179 (parent: 1178)
|
||||
// - Custom Doc3: 1172 (no parent)
|
||||
|
||||
var home = GetNode(1173);
|
||||
var root = GetNode(1046);
|
||||
var customDoc = GetNode(1178);
|
||||
var customDoc2 = GetNode(1179);
|
||||
var customDoc3 = GetNode(1172);
|
||||
|
||||
Assert.IsFalse(root.IsDescendant(root));
|
||||
Assert.IsFalse(root.IsDescendant(home));
|
||||
Assert.IsFalse(root.IsDescendant(customDoc));
|
||||
Assert.IsFalse(root.IsDescendant(customDoc2));
|
||||
Assert.IsFalse(root.IsDescendant(customDoc3));
|
||||
|
||||
Assert.IsTrue(home.IsDescendant(root));
|
||||
Assert.IsFalse(home.IsDescendant(home));
|
||||
Assert.IsFalse(home.IsDescendant(customDoc));
|
||||
Assert.IsFalse(home.IsDescendant(customDoc2));
|
||||
Assert.IsFalse(home.IsDescendant(customDoc3));
|
||||
|
||||
Assert.IsTrue(customDoc.IsDescendant(root));
|
||||
Assert.IsTrue(customDoc.IsDescendant(home));
|
||||
Assert.IsFalse(customDoc.IsDescendant(customDoc));
|
||||
Assert.IsFalse(customDoc.IsDescendant(customDoc2));
|
||||
Assert.IsFalse(customDoc.IsDescendant(customDoc3));
|
||||
|
||||
Assert.IsTrue(customDoc2.IsDescendant(root));
|
||||
Assert.IsTrue(customDoc2.IsDescendant(home));
|
||||
Assert.IsTrue(customDoc2.IsDescendant(customDoc));
|
||||
Assert.IsFalse(customDoc2.IsDescendant(customDoc2));
|
||||
Assert.IsFalse(customDoc2.IsDescendant(customDoc3));
|
||||
|
||||
Assert.IsFalse(customDoc3.IsDescendant(customDoc3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDescendantOrSelf()
|
||||
{
|
||||
// Structure:
|
||||
// - Root : 1046 (no parent)
|
||||
// -- Home: 1173 (parent 1046)
|
||||
// -- Custom Doc: 1178 (parent 1173)
|
||||
// --- Custom Doc2: 1179 (parent: 1178)
|
||||
// - Custom Doc3: 1172 (no parent)
|
||||
|
||||
var home = GetNode(1173);
|
||||
var root = GetNode(1046);
|
||||
var customDoc = GetNode(1178);
|
||||
var customDoc2 = GetNode(1179);
|
||||
var customDoc3 = GetNode(1172);
|
||||
|
||||
Assert.IsTrue(root.IsDescendantOrSelf(root));
|
||||
Assert.IsFalse(root.IsDescendantOrSelf(home));
|
||||
Assert.IsFalse(root.IsDescendantOrSelf(customDoc));
|
||||
Assert.IsFalse(root.IsDescendantOrSelf(customDoc2));
|
||||
Assert.IsFalse(root.IsDescendantOrSelf(customDoc3));
|
||||
|
||||
Assert.IsTrue(home.IsDescendantOrSelf(root));
|
||||
Assert.IsTrue(home.IsDescendantOrSelf(home));
|
||||
Assert.IsFalse(home.IsDescendantOrSelf(customDoc));
|
||||
Assert.IsFalse(home.IsDescendantOrSelf(customDoc2));
|
||||
Assert.IsFalse(home.IsDescendantOrSelf(customDoc3));
|
||||
|
||||
Assert.IsTrue(customDoc.IsDescendantOrSelf(root));
|
||||
Assert.IsTrue(customDoc.IsDescendantOrSelf(home));
|
||||
Assert.IsTrue(customDoc.IsDescendantOrSelf(customDoc));
|
||||
Assert.IsFalse(customDoc.IsDescendantOrSelf(customDoc2));
|
||||
Assert.IsFalse(customDoc.IsDescendantOrSelf(customDoc3));
|
||||
|
||||
Assert.IsTrue(customDoc2.IsDescendantOrSelf(root));
|
||||
Assert.IsTrue(customDoc2.IsDescendantOrSelf(home));
|
||||
Assert.IsTrue(customDoc2.IsDescendantOrSelf(customDoc));
|
||||
Assert.IsTrue(customDoc2.IsDescendantOrSelf(customDoc2));
|
||||
Assert.IsFalse(customDoc2.IsDescendantOrSelf(customDoc3));
|
||||
|
||||
Assert.IsTrue(customDoc3.IsDescendantOrSelf(customDoc3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Up()
|
||||
{
|
||||
|
||||
@@ -1732,6 +1732,39 @@ namespace Umbraco.Tests.Services
|
||||
Assert.IsNull(mediaType2.Description);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Variations_In_Compositions()
|
||||
{
|
||||
var service = ServiceContext.ContentTypeService;
|
||||
var typeA = MockedContentTypes.CreateSimpleContentType("a", "A");
|
||||
typeA.Variations = ContentVariation.Culture; // make it variant
|
||||
typeA.PropertyTypes.First(x => x.Alias.InvariantEquals("title")).Variations = ContentVariation.Culture; // with a variant property
|
||||
service.Save(typeA);
|
||||
|
||||
var typeB = MockedContentTypes.CreateSimpleContentType("b", "B", typeA, true);
|
||||
typeB.Variations = ContentVariation.Nothing; // make it invariant
|
||||
service.Save(typeB);
|
||||
|
||||
var typeC = MockedContentTypes.CreateSimpleContentType("c", "C", typeA, true);
|
||||
typeC.Variations = ContentVariation.Culture; // make it variant
|
||||
service.Save(typeC);
|
||||
|
||||
// property is variant on A
|
||||
var test = service.Get(typeA.Id);
|
||||
Assert.AreEqual(ContentVariation.Culture, test.CompositionPropertyTypes.First(x => x.Alias.InvariantEquals("title")).Variations);
|
||||
Assert.AreEqual(ContentVariation.Culture, test.CompositionPropertyGroups.First().PropertyTypes.First(x => x.Alias.InvariantEquals("title")).Variations);
|
||||
|
||||
// but not on B
|
||||
test = service.Get(typeB.Id);
|
||||
Assert.AreEqual(ContentVariation.Nothing, test.CompositionPropertyTypes.First(x => x.Alias.InvariantEquals("title")).Variations);
|
||||
Assert.AreEqual(ContentVariation.Nothing, test.CompositionPropertyGroups.First().PropertyTypes.First(x => x.Alias.InvariantEquals("title")).Variations);
|
||||
|
||||
// but on C
|
||||
test = service.Get(typeC.Id);
|
||||
Assert.AreEqual(ContentVariation.Culture, test.CompositionPropertyTypes.First(x => x.Alias.InvariantEquals("title")).Variations);
|
||||
Assert.AreEqual(ContentVariation.Culture, test.CompositionPropertyGroups.First().PropertyTypes.First(x => x.Alias.InvariantEquals("title")).Variations);
|
||||
}
|
||||
|
||||
private ContentType CreateComponent()
|
||||
{
|
||||
var component = new ContentType(-1)
|
||||
|
||||
@@ -39,6 +39,7 @@ using Umbraco.Tests.Testing.Objects.Accessors;
|
||||
using Umbraco.Web.Composing.CompositionRoots;
|
||||
using Umbraco.Web._Legacy.Actions;
|
||||
using Current = Umbraco.Core.Composing.Current;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Tests.Testing
|
||||
{
|
||||
@@ -137,6 +138,7 @@ namespace Umbraco.Tests.Testing
|
||||
ComposeApplication(Options.WithApplication);
|
||||
|
||||
// etc
|
||||
ComposeWeb();
|
||||
ComposeWtf();
|
||||
|
||||
// not sure really
|
||||
@@ -173,12 +175,24 @@ namespace Umbraco.Tests.Testing
|
||||
Container.RegisterSingleton(f => new ProfilingLogger(f.GetInstance<ILogger>(), f.GetInstance<IProfiler>()));
|
||||
}
|
||||
|
||||
protected virtual void ComposeWtf()
|
||||
protected virtual void ComposeWeb()
|
||||
{
|
||||
//TODO: Should we 'just' register the WebRuntimeComponent?
|
||||
|
||||
// imported from TestWithSettingsBase
|
||||
// which was inherited by TestWithApplicationBase so pretty much used everywhere
|
||||
Umbraco.Web.Composing.Current.UmbracoContextAccessor = new TestUmbracoContextAccessor();
|
||||
|
||||
// web
|
||||
Container.Register(_ => Umbraco.Web.Composing.Current.UmbracoContextAccessor);
|
||||
Container.RegisterSingleton<PublishedRouter>();
|
||||
Container.RegisterCollectionBuilder<ContentFinderCollectionBuilder>();
|
||||
Container.Register<IContentLastChanceFinder, TestLastChanceFinder>();
|
||||
Container.Register<IVariationContextAccessor, TestVariationContextAccessor>();
|
||||
}
|
||||
|
||||
protected virtual void ComposeWtf()
|
||||
{
|
||||
// what else?
|
||||
var runtimeStateMock = new Mock<IRuntimeState>();
|
||||
runtimeStateMock.Setup(x => x.Level).Returns(RuntimeLevel.Run);
|
||||
@@ -271,7 +285,8 @@ namespace Umbraco.Tests.Testing
|
||||
Container.RegisterSingleton(factory => globalSettings);
|
||||
Container.RegisterSingleton(factory => umbracoSettings.Content);
|
||||
Container.RegisterSingleton(factory => umbracoSettings.Templates);
|
||||
|
||||
Container.RegisterSingleton(factory => umbracoSettings.WebRouting);
|
||||
|
||||
// fixme - The whole MediaFileSystem coupling thing seems broken.
|
||||
Container.Register<IFileSystem, MediaFileSystem>((factory, fileSystem) => new MediaFileSystem(fileSystem, factory.GetInstance<IContentSection>(), factory.GetInstance<IMediaPathScheme>(), factory.GetInstance<ILogger>()));
|
||||
Container.RegisterConstructorDependency((factory, parameterInfo) => factory.GetInstance<FileSystems>().MediaFileSystem);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -531,6 +531,13 @@ input.umb-group-builder__group-title-input {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.editor-validation-pattern{
|
||||
border: 1px solid @gray-7;
|
||||
margin: 10px 0 0;
|
||||
padding: 6px;
|
||||
max-height: 32px;
|
||||
}
|
||||
|
||||
.umb-dropdown {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,24 @@
|
||||
// --------------------------------------------------
|
||||
.umb-property-editor {
|
||||
min-width:66.6%;
|
||||
|
||||
&-pull {
|
||||
float:left;
|
||||
width:66.6%;
|
||||
}
|
||||
|
||||
&-push {
|
||||
float:right;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-property-editor-tiny {
|
||||
width: 60px;
|
||||
|
||||
&.umb-editor-push {
|
||||
width:30%;
|
||||
min-width:0;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-property-editor-small {
|
||||
@@ -28,6 +42,27 @@
|
||||
width: 99%;
|
||||
}
|
||||
|
||||
// displays property inline with preceeding
|
||||
.umb-property {
|
||||
&--pull {
|
||||
float:left;
|
||||
width:60%;
|
||||
}
|
||||
|
||||
&--push {
|
||||
float:right;
|
||||
width:35%;
|
||||
}
|
||||
|
||||
&--pull, &--push {
|
||||
.umb-editor {
|
||||
min-width:0;
|
||||
width:100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Content picker
|
||||
// --------------------------------------------------
|
||||
|
||||
@@ -147,13 +147,32 @@ app.config(function ($routeProvider) {
|
||||
resolve: canRoute(true)
|
||||
})
|
||||
.when('/:section/:tree/:method?', {
|
||||
templateUrl: function (rp) {
|
||||
//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 dynamnically based on the current tree.
|
||||
controller: function ($scope, $route, $routeParams, treeService) {
|
||||
|
||||
//if there is no method registered for this then show the dashboard
|
||||
if (!rp.method)
|
||||
return "views/common/dashboard.html";
|
||||
|
||||
return ('views/' + rp.tree + '/' + rp.method + '.html');
|
||||
if (!$routeParams.method) {
|
||||
$scope.templateUrl = "views/common/dashboard.html";
|
||||
}
|
||||
|
||||
// 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');
|
||||
}
|
||||
},
|
||||
reloadOnSearch: false,
|
||||
resolve: canRoute(true)
|
||||
|
||||
+131
-116
@@ -1,155 +1,170 @@
|
||||
//used for the media picker dialog
|
||||
angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController",
|
||||
function ($scope, eventsService, dialogService, entityResource, contentResource, mediaHelper, userService, localizationService) {
|
||||
var dialogOptions = $scope.model;
|
||||
function ($scope, eventsService, dialogService, entityResource, contentResource, mediaHelper, userService, localizationService, tinyMceService) {
|
||||
var dialogOptions = $scope.model;
|
||||
|
||||
var searchText = "Search...";
|
||||
localizationService.localize("general_search").then(function (value) {
|
||||
searchText = value + "...";
|
||||
});
|
||||
var anchorPattern = /<a id=\\"(.*?)\\">/gi;
|
||||
|
||||
if(!$scope.model.title) {
|
||||
localizationService.localize("defaultdialogs_selectLink").then(function(value){
|
||||
$scope.model.title = value;
|
||||
});
|
||||
}
|
||||
var searchText = "Search...";
|
||||
localizationService.localize("general_search").then(function (value) {
|
||||
searchText = value + "...";
|
||||
});
|
||||
|
||||
$scope.dialogTreeApi = {};
|
||||
$scope.model.target = {};
|
||||
$scope.searchInfo = {
|
||||
searchFromId: null,
|
||||
searchFromName: null,
|
||||
showSearch: false,
|
||||
results: [],
|
||||
selectedSearchResults: []
|
||||
};
|
||||
if (!$scope.model.title) {
|
||||
$scope.model.title = localizationService.localize("defaultdialogs_selectLink");
|
||||
}
|
||||
|
||||
$scope.showTarget = $scope.model.hideTarget !== true;
|
||||
$scope.dialogTreeApi = {};
|
||||
$scope.model.target = {};
|
||||
$scope.searchInfo = {
|
||||
searchFromId: null,
|
||||
searchFromName: null,
|
||||
showSearch: false,
|
||||
results: [],
|
||||
selectedSearchResults: []
|
||||
};
|
||||
|
||||
if (dialogOptions.currentTarget) {
|
||||
$scope.model.target = dialogOptions.currentTarget;
|
||||
$scope.showTarget = $scope.model.hideTarget !== true;
|
||||
|
||||
//if we have a node ID, we fetch the current node to build the form data
|
||||
if ($scope.model.target.id || $scope.model.target.udi) {
|
||||
if (dialogOptions.currentTarget) {
|
||||
$scope.model.target = dialogOptions.currentTarget;
|
||||
//if we have a node ID, we fetch the current node to build the form data
|
||||
if ($scope.model.target.id || $scope.model.target.udi) {
|
||||
|
||||
//will be either a udi or an int
|
||||
var id = $scope.model.target.udi ? $scope.model.target.udi : $scope.model.target.id;
|
||||
|
||||
if (!$scope.model.target.path) {
|
||||
|
||||
|
||||
entityResource.getPath(id, "Document").then(function (path) {
|
||||
$scope.model.target.path = path;
|
||||
//now sync the tree to this path
|
||||
$scope.dialogTreeApi.syncTree({ path: $scope.model.target.path, tree: "content" });
|
||||
});
|
||||
}
|
||||
$scope.model.target.path = path;
|
||||
//now sync the tree to this path
|
||||
$scope.dialogTreeApi.syncTree({
|
||||
path: $scope.model.target.path,
|
||||
tree: "content"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
contentResource.getNiceUrl(id).then(function (url) {
|
||||
$scope.model.target.url = url;
|
||||
});
|
||||
}
|
||||
}
|
||||
// if a link exists, get the properties to build the anchor name list
|
||||
contentResource.getById(id).then(function (resp) {
|
||||
$scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties));
|
||||
$scope.model.target.url = resp.urls[0];
|
||||
});
|
||||
} else if ($scope.model.target.url.length) {
|
||||
// a url but no id/udi indicates an external link - trim the url to remove the anchor/qs
|
||||
$scope.model.target.url = $scope.model.target.url.substring(0, $scope.model.target.url.search(/(#|\?)/));
|
||||
}
|
||||
} else if (dialogOptions.anchors) {
|
||||
$scope.anchorValues = dialogOptions.anchors;
|
||||
}
|
||||
|
||||
function nodeSelectHandler(args) {
|
||||
function nodeSelectHandler(args) {
|
||||
if (args && args.event) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
}
|
||||
|
||||
if(args && args.event) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
}
|
||||
eventsService.emit("dialogs.linkPicker.select", args);
|
||||
|
||||
eventsService.emit("dialogs.linkPicker.select", args);
|
||||
if ($scope.currentNode) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.currentNode.selected = false;
|
||||
}
|
||||
|
||||
if ($scope.currentNode) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.currentNode.selected = false;
|
||||
}
|
||||
$scope.currentNode = args.node;
|
||||
$scope.currentNode.selected = true;
|
||||
$scope.model.target.id = args.node.id;
|
||||
$scope.model.target.udi = args.node.udi;
|
||||
$scope.model.target.name = args.node.name;
|
||||
|
||||
$scope.currentNode = args.node;
|
||||
$scope.currentNode.selected = true;
|
||||
$scope.model.target.id = args.node.id;
|
||||
$scope.model.target.udi = args.node.udi;
|
||||
$scope.model.target.name = args.node.name;
|
||||
if (args.node.id < 0) {
|
||||
$scope.model.target.url = "/";
|
||||
} else {
|
||||
contentResource.getById(args.node.id).then(function (resp) {
|
||||
$scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties));
|
||||
$scope.model.target.url = resp.urls[0];
|
||||
});
|
||||
}
|
||||
|
||||
if (args.node.id < 0) {
|
||||
$scope.model.target.url = "/";
|
||||
}
|
||||
else {
|
||||
contentResource.getNiceUrl(args.node.id).then(function (url) {
|
||||
$scope.model.target.url = url;
|
||||
});
|
||||
}
|
||||
if (!angular.isUndefined($scope.model.target.isMedia)) {
|
||||
delete $scope.model.target.isMedia;
|
||||
}
|
||||
}
|
||||
|
||||
if (!angular.isUndefined($scope.model.target.isMedia)) {
|
||||
delete $scope.model.target.isMedia;
|
||||
}
|
||||
}
|
||||
function nodeExpandedHandler(args) {
|
||||
// open mini list view for list views
|
||||
if (args.node.metaData.isContainer) {
|
||||
openMiniListView(args.node);
|
||||
}
|
||||
}
|
||||
|
||||
function nodeExpandedHandler(args) {
|
||||
// open mini list view for list views
|
||||
if (args.node.metaData.isContainer) {
|
||||
openMiniListView(args.node);
|
||||
}
|
||||
}
|
||||
$scope.switchToMediaPicker = function () {
|
||||
userService.getCurrentUser().then(function (userData) {
|
||||
$scope.mediaPickerOverlay = {
|
||||
view: "mediapicker",
|
||||
startNodeId: userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0],
|
||||
startNodeIsVirtual: userData.startMediaIds.length !== 1,
|
||||
show: true,
|
||||
submit: function (model) {
|
||||
var media = model.selectedImages[0];
|
||||
|
||||
$scope.switchToMediaPicker = function () {
|
||||
userService.getCurrentUser().then(function (userData) {
|
||||
$scope.mediaPickerOverlay = {
|
||||
view: "mediapicker",
|
||||
startNodeId: userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0],
|
||||
startNodeIsVirtual: userData.startMediaIds.length !== 1,
|
||||
show: true,
|
||||
submit: function(model) {
|
||||
var media = model.selectedImages[0];
|
||||
$scope.model.target.id = media.id;
|
||||
$scope.model.target.udi = media.udi;
|
||||
$scope.model.target.isMedia = true;
|
||||
$scope.model.target.name = media.name;
|
||||
$scope.model.target.url = mediaHelper.resolveFile(media);
|
||||
|
||||
$scope.model.target.id = media.id;
|
||||
$scope.model.target.udi = media.udi;
|
||||
$scope.model.target.isMedia = true;
|
||||
$scope.model.target.name = media.name;
|
||||
$scope.model.target.url = mediaHelper.resolveFile(media);
|
||||
debugger;
|
||||
|
||||
$scope.mediaPickerOverlay.show = false;
|
||||
$scope.mediaPickerOverlay = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
$scope.mediaPickerOverlay.show = false;
|
||||
$scope.mediaPickerOverlay = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
$scope.hideSearch = function () {
|
||||
$scope.searchInfo.showSearch = false;
|
||||
$scope.searchInfo.searchFromId = null;
|
||||
$scope.searchInfo.searchFromName = null;
|
||||
$scope.searchInfo.results = [];
|
||||
}
|
||||
$scope.hideSearch = function () {
|
||||
$scope.searchInfo.showSearch = false;
|
||||
$scope.searchInfo.searchFromId = null;
|
||||
$scope.searchInfo.searchFromName = null;
|
||||
$scope.searchInfo.results = [];
|
||||
}
|
||||
|
||||
// method to select a search result
|
||||
$scope.selectResult = function (evt, result) {
|
||||
result.selected = result.selected === true ? false : true;
|
||||
nodeSelectHandler(evt, {event: evt, node: result});
|
||||
};
|
||||
// method to select a search result
|
||||
$scope.selectResult = function (evt, result) {
|
||||
result.selected = result.selected === true ? false : true;
|
||||
nodeSelectHandler(evt, {
|
||||
event: evt,
|
||||
node: result
|
||||
});
|
||||
};
|
||||
|
||||
//callback when there are search results
|
||||
$scope.onSearchResults = function (results) {
|
||||
$scope.searchInfo.results = results;
|
||||
$scope.onSearchResults = function (results) {
|
||||
$scope.searchInfo.results = results;
|
||||
$scope.searchInfo.showSearch = true;
|
||||
};
|
||||
};
|
||||
|
||||
$scope.onTreeInit = function () {
|
||||
$scope.dialogTreeApi.callbacks.treeNodeSelect(nodeSelectHandler);
|
||||
$scope.dialogTreeApi.callbacks.treeNodeExpanded(nodeExpandedHandler);
|
||||
}
|
||||
|
||||
// Mini list view
|
||||
$scope.selectListViewNode = function (node) {
|
||||
node.selected = node.selected === true ? false : true;
|
||||
nodeSelectHandler({}, { node: node });
|
||||
};
|
||||
|
||||
$scope.closeMiniListView = function () {
|
||||
$scope.miniListView = undefined;
|
||||
};
|
||||
// Mini list view
|
||||
$scope.selectListViewNode = function (node) {
|
||||
node.selected = node.selected === true ? false : true;
|
||||
nodeSelectHandler({}, {
|
||||
node: node
|
||||
});
|
||||
};
|
||||
|
||||
function openMiniListView(node) {
|
||||
$scope.miniListView = node;
|
||||
}
|
||||
$scope.closeMiniListView = function () {
|
||||
$scope.miniListView = undefined;
|
||||
};
|
||||
|
||||
});
|
||||
function openMiniListView(node) {
|
||||
$scope.miniListView = node;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -1,26 +1,41 @@
|
||||
<div ng-controller="Umbraco.Overlays.LinkPickerController">
|
||||
|
||||
<umb-control-group label="@defaultdialogs_urlLinkPicker">
|
||||
<umb-control-group label="@defaultdialogs_urlLinkPicker" class="umb-property--pull">
|
||||
<input type="text"
|
||||
localize="placeholder"
|
||||
placeholder="@general_url"
|
||||
class="umb-property-editor umb-textstring"
|
||||
ng-model="model.target.url"
|
||||
ng-disabled="model.target.id"
|
||||
focus-when="{{true}} "/>
|
||||
localize="placeholder"
|
||||
placeholder="@general_url"
|
||||
class="umb-property-editor umb-textstring"
|
||||
ng-model="model.target.url"
|
||||
ng-disabled="model.target.id" />
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group label="@defaultdialogs_anchorLinkPicker" class="umb-property--push">
|
||||
<input type="text"
|
||||
list="anchors"
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_anchor"
|
||||
class="umb-editor umb-textstring"
|
||||
ng-model="model.target.anchor" />
|
||||
|
||||
<datalist id="anchors">
|
||||
<option value="{{a}}" ng-repeat="a in anchorValues"></option>
|
||||
</datalist>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group label="@defaultdialogs_nodeNameLinkPicker">
|
||||
<input type="text"
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_entername"
|
||||
class="umb-property-editor umb-textstring"
|
||||
ng-model="model.target.name" />
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_entername"
|
||||
class="umb-property-editor umb-textstring"
|
||||
ng-model="model.target.name" />
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group ng-if="showTarget" label="@content_target">
|
||||
<label class="checkbox no-indent">
|
||||
<input type="checkbox" ng-model="model.target.target" ng-true-value="_blank" ng-false-value="" /> <localize key="defaultdialogs_openInNewWindow">Opens the linked document in a new window or tab</localize>
|
||||
<input type="checkbox"
|
||||
ng-model="model.target.target"
|
||||
ng-true-value="_blank"
|
||||
ng-false-value="" /> <localize key="defaultdialogs_openInNewWindow">Opens the linked document in a new window or tab</localize>
|
||||
</label>
|
||||
</umb-control-group>
|
||||
|
||||
@@ -30,43 +45,39 @@
|
||||
</h5>
|
||||
|
||||
<div ng-hide="miniListView">
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="{{section}}">
|
||||
<umb-tree-search-box hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="{{section}}">
|
||||
</umb-tree-search-box>
|
||||
|
||||
<br/>
|
||||
<br />
|
||||
|
||||
<umb-tree-search-results
|
||||
ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
<umb-tree-search-results ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
|
||||
<div ng-hide="searchInfo.showSearch">
|
||||
<umb-tree
|
||||
section="content"
|
||||
hideheader="true"
|
||||
hideoptions="true"
|
||||
api="dialogTreeApi"
|
||||
on-init="onTreeInit()"
|
||||
enablelistviewexpand="true"
|
||||
isdialog="true"
|
||||
enablecheckboxes="true">
|
||||
<umb-tree section="content"
|
||||
hideheader="true"
|
||||
hideoptions="true"
|
||||
api="dialogTreeApi"
|
||||
on-init="onTreeInit()"
|
||||
enablelistviewexpand="true"
|
||||
isdialog="true"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-mini-list-view
|
||||
ng-if="miniListView"
|
||||
node="miniListView"
|
||||
entity-type="Document"
|
||||
on-select="selectListViewNode(node)"
|
||||
on-close="closeMiniListView()">
|
||||
<umb-mini-list-view ng-if="miniListView"
|
||||
node="miniListView"
|
||||
entity-type="Document"
|
||||
on-select="selectListViewNode(node)"
|
||||
on-close="closeMiniListView()">
|
||||
</umb-mini-list-view>
|
||||
|
||||
</div>
|
||||
@@ -80,11 +91,10 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<umb-overlay
|
||||
ng-if="mediaPickerOverlay.show"
|
||||
model="mediaPickerOverlay"
|
||||
view="mediaPickerOverlay.view"
|
||||
position="right">
|
||||
<umb-overlay ng-if="mediaPickerOverlay.show"
|
||||
model="mediaPickerOverlay"
|
||||
view="mediaPickerOverlay.view"
|
||||
position="right">
|
||||
</umb-overlay>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -20,9 +20,10 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController",
|
||||
}
|
||||
$scope.treeModel = {
|
||||
hideHeader: false
|
||||
}
|
||||
}
|
||||
$scope.toggle = toggleHandler;
|
||||
userService.getCurrentUser().then(function (userData) {
|
||||
$scope.treeModel.hideHeader = userData.startContentIds.length > 0 && userData.startContentIds.indexOf(-1) == -1;
|
||||
$scope.treeModel.hideHeader = userData.startContentIds.length > 0 && userData.startContentIds.indexOf(-1) == -1;
|
||||
});
|
||||
|
||||
var node = dialogOptions.currentNode;
|
||||
@@ -57,7 +58,27 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController",
|
||||
if (args.node.metaData.isContainer) {
|
||||
openMiniListView(args.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleHandler(type){
|
||||
// If the relateToOriginal toggle is clicked
|
||||
if(type === "relate"){
|
||||
if($scope.relateToOriginal){
|
||||
$scope.relateToOriginal = false;
|
||||
return;
|
||||
}
|
||||
$scope.relateToOriginal = true;
|
||||
}
|
||||
|
||||
// If the recurvise toggle is clicked
|
||||
if(type === "recursive"){
|
||||
if($scope.recursive){
|
||||
$scope.recursive = false;
|
||||
return;
|
||||
}
|
||||
$scope.recursive = true;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.hideSearch = function () {
|
||||
$scope.searchInfo.showSearch = false;
|
||||
|
||||
@@ -69,13 +69,13 @@
|
||||
|
||||
<umb-pane>
|
||||
<umb-control-group localize="label" label="@defaultdialogs_relateToOriginalLabel">
|
||||
<input type="checkbox" ng-model="$parent.$parent.relateToOriginal"/>
|
||||
<umb-toggle checked="$parent.$parent.relateToOriginal" on-click="$parent.$parent.toggle('relate')"></umb-toggle>
|
||||
</umb-control-group>
|
||||
</umb-pane>
|
||||
|
||||
<umb-pane>
|
||||
<umb-control-group localize="label" label="@defaultdialogs_includeDescendants">
|
||||
<input type="checkbox" ng-model="$parent.$parent.recursive" />
|
||||
<umb-control-group localize="label" label="@defaultdialogs_includeDescendants">
|
||||
<umb-toggle checked="$parent.$parent.recursive" on-click="$parent.$parent.toggle('recursive')"></umb-toggle>
|
||||
</umb-control-group>
|
||||
</umb-pane>
|
||||
|
||||
|
||||
+3
@@ -88,7 +88,10 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
|
||||
|
||||
// sortable options
|
||||
$scope.sortableOptions = {
|
||||
axis: "y",
|
||||
containment: "parent",
|
||||
distance: 10,
|
||||
opacity: 0.7,
|
||||
tolerance: "pointer",
|
||||
scroll: true,
|
||||
zIndex: 6000
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
function GridRichTextEditorController($scope, tinyMceService, macroService) {
|
||||
function GridRichTextEditorController($scope, tinyMceService, macroService, editorState) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
vm.openEmbed = openEmbed;
|
||||
|
||||
function openLinkPicker(editor, currentTarget, anchorElement) {
|
||||
|
||||
vm.linkPickerOverlay = {
|
||||
view: "linkpicker",
|
||||
currentTarget: currentTarget,
|
||||
anchors: tinyMceService.getAnchorNames(JSON.stringify(editorState.current.properties)),
|
||||
show: true,
|
||||
submit: function(model) {
|
||||
tinyMceService.insertLinkInEditor(editor, model.target, anchorElement);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
angular.module("umbraco")
|
||||
.controller("Umbraco.PropertyEditors.RTEController",
|
||||
function ($rootScope, $scope, $q, $locale, dialogService, $log, imageHelper, assetsService, $timeout, tinyMceService, angularHelper, stylesheetResource, macroService) {
|
||||
function ($rootScope, $scope, $q, $locale, dialogService, $log, imageHelper, assetsService, $timeout, tinyMceService, angularHelper, stylesheetResource, macroService, editorState) {
|
||||
|
||||
$scope.isLoading = true;
|
||||
|
||||
@@ -273,11 +273,12 @@ angular.module("umbraco")
|
||||
|
||||
syncContent(editor);
|
||||
});
|
||||
|
||||
|
||||
tinyMceService.createLinkPicker(editor, $scope, function(currentTarget, anchorElement) {
|
||||
$scope.linkPickerOverlay = {
|
||||
view: "linkpicker",
|
||||
currentTarget: currentTarget,
|
||||
anchors: tinyMceService.getAnchorNames(JSON.stringify(editorState.current.properties)),
|
||||
show: true,
|
||||
submit: function(model) {
|
||||
tinyMceService.insertLinkInEditor(editor, model.target, anchorElement);
|
||||
|
||||
@@ -5,15 +5,12 @@ angular.module("umbraco")
|
||||
var $typeahead;
|
||||
|
||||
$scope.isLoading = true;
|
||||
$scope.tagToAdd = "";
|
||||
|
||||
assetsService.loadJs("lib/typeahead.js/typeahead.bundle.min.js", $scope).then(function () {
|
||||
|
||||
$scope.isLoading = false;
|
||||
|
||||
//load current value
|
||||
|
||||
if ($scope.model.value) {
|
||||
$scope.tagToAdd = "";
|
||||
|
||||
function setModelValue(val) {
|
||||
|
||||
$scope.model.value = val || $scope.model.value;
|
||||
if ($scope.model.value) {
|
||||
if (!$scope.model.config.storageType || $scope.model.config.storageType !== "Json") {
|
||||
//it is csv
|
||||
if (!$scope.model.value) {
|
||||
@@ -21,7 +18,14 @@ angular.module("umbraco")
|
||||
}
|
||||
else {
|
||||
if($scope.model.value.length > 0) {
|
||||
$scope.model.value = $scope.model.value.split(",");
|
||||
// split the csv string, and remove any duplicate values
|
||||
var tempArray = $scope.model.value.split(',').map(function(v) {
|
||||
return v.trim();
|
||||
});
|
||||
|
||||
$scope.model.value = tempArray.filter(function(v, i, self) {
|
||||
return self.indexOf(v) === i;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +33,14 @@ angular.module("umbraco")
|
||||
else {
|
||||
$scope.model.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
assetsService.loadJs("lib/typeahead.js/typeahead.bundle.min.js", $scope).then(function () {
|
||||
|
||||
$scope.isLoading = false;
|
||||
|
||||
//load current value
|
||||
setModelValue();
|
||||
|
||||
// Method required by the valPropertyValidator directive (returns true if the property editor has at least one tag selected)
|
||||
$scope.validateMandatory = function () {
|
||||
@@ -85,17 +97,7 @@ angular.module("umbraco")
|
||||
//vice versa
|
||||
$scope.model.onValueChanged = function (newVal, oldVal) {
|
||||
//update the display val again if it has changed from the server
|
||||
$scope.model.value = newVal;
|
||||
|
||||
if (!$scope.model.config.storageType || $scope.model.config.storageType !== "Json") {
|
||||
//it is csv
|
||||
if (!$scope.model.value) {
|
||||
$scope.model.value = [];
|
||||
}
|
||||
else {
|
||||
$scope.model.value = $scope.model.value.split(",");
|
||||
}
|
||||
}
|
||||
setModelValue(newVal);
|
||||
};
|
||||
|
||||
//configure the tags data source
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<input type="hidden" name="tagCount" ng-model="model.value.length" val-property-validator="validateMandatory" />
|
||||
|
||||
<span ng-repeat="tag in model.value" ng-click="$parent.removeTag(tag)" class="label label-primary tag">
|
||||
<span ng-repeat="tag in model.value track by $index" ng-click="$parent.removeTag(tag)" class="label label-primary tag">
|
||||
<span ng-bind-html="tag"></span>
|
||||
<i class="icon icon-delete"></i>
|
||||
</span>
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
});
|
||||
|
||||
|
||||
vm.save = function () {
|
||||
vm.save = function (suppressNotification) {
|
||||
vm.page.saveButtonState = "busy";
|
||||
|
||||
vm.template.content = vm.editor.getValue();
|
||||
@@ -83,11 +83,13 @@
|
||||
rebindCallback: function (orignal, saved) {}
|
||||
}).then(function (saved) {
|
||||
|
||||
if (!suppressNotification) {
|
||||
localizationService.localizeMany(["speechBubbles_templateSavedHeader", "speechBubbles_templateSavedText"]).then(function(data){
|
||||
var header = data[0];
|
||||
var message = data[1];
|
||||
notificationsService.success(header, message);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
vm.page.saveButtonState = "success";
|
||||
@@ -173,6 +175,21 @@
|
||||
vm.page.loading = false;
|
||||
vm.template = template;
|
||||
|
||||
// if this is a new template, bind to the blur event on the name
|
||||
if ($routeParams.create) {
|
||||
$timeout(function() {
|
||||
var nameField = angular.element(document.querySelector('[data-element="editor-name-field"]'));
|
||||
if (nameField) {
|
||||
nameField.bind('blur', function(event) {
|
||||
if (event.target.value) {
|
||||
vm.save(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//sync state
|
||||
editorState.set(vm.template);
|
||||
navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) {
|
||||
|
||||
@@ -329,8 +329,10 @@
|
||||
vm.unlockUserButtonState = "busy";
|
||||
usersResource.unlockUsers([vm.user.id]).then(function (data) {
|
||||
vm.user.userState = 0;
|
||||
vm.user.failedPasswordAttempts = 0;
|
||||
setUserDisplayState();
|
||||
vm.unlockUserButtonState = "success";
|
||||
|
||||
}, function (error) {
|
||||
vm.unlockUserButtonState = "error";
|
||||
});
|
||||
|
||||
@@ -344,6 +344,7 @@
|
||||
<area alias="defaultdialogs">
|
||||
<key alias="nodeNameLinkPicker">Link title</key>
|
||||
<key alias="urlLinkPicker">Link</key>
|
||||
<key alias="anchorLinkPicker">Anchor / querystring</key>
|
||||
<key alias="anchorInsert">Name</key>
|
||||
<key alias="assignDomain">Manage hostnames</key>
|
||||
<key alias="closeThisWindow">Close this window</key>
|
||||
@@ -462,6 +463,7 @@
|
||||
<key alias="email">Enter your email...</key>
|
||||
<key alias="enterMessage">Enter a message...</key>
|
||||
<key alias="usernameHint">Your username is usually your email</key>
|
||||
<key alias="anchor">#value or ?key=value</key>
|
||||
</area>
|
||||
<area alias="editcontenttype">
|
||||
<key alias="allowAtRoot" version="7.2">Allow at root</key>
|
||||
|
||||
@@ -352,6 +352,7 @@
|
||||
<area alias="defaultdialogs">
|
||||
<key alias="nodeNameLinkPicker">Link title</key>
|
||||
<key alias="urlLinkPicker">Link</key>
|
||||
<key alias="anchorLinkPicker">Anchor / querystring</key>
|
||||
<key alias="anchorInsert">Name</key>
|
||||
<key alias="closeThisWindow">Close this window</key>
|
||||
<key alias="confirmdelete">Are you sure you want to delete</key>
|
||||
@@ -470,6 +471,7 @@
|
||||
<key alias="email">Enter your email...</key>
|
||||
<key alias="enterMessage">Enter a message...</key>
|
||||
<key alias="usernameHint">Your username is usually your email</key>
|
||||
<key alias="anchor">#value or ?key=value</key>
|
||||
</area>
|
||||
<area alias="editcontenttype">
|
||||
<key alias="allowAtRoot" version="7.2">Allow at root</key>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -39,14 +39,14 @@
|
||||
<h2>Easy start with Umbraco.tv</h2>
|
||||
<p>We have created a bunch of 'how-to' videos, to get you easily started with Umbraco. Learn how to build projects in just a couple of minutes. Easiest CMS in the world.</p>
|
||||
|
||||
<a href="http://umbraco.tv?ref=tvFromInstaller" target="_blank">Umbraco.tv →</a>
|
||||
<a href="https://umbraco.tv?ref=tvFromInstaller" target="_blank">Umbraco.tv →</a>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<h2>Be a part of the community</h2>
|
||||
<p>The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we're sure that you can get your answers from the community.</p>
|
||||
|
||||
<a href="http://our.umbraco.org?ref=ourFromInstaller" target="_blank">our.Umbraco →</a>
|
||||
<a href="https://our.umbraco.com/?ref=ourFromInstaller" target="_blank">our.Umbraco →</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -40,7 +40,9 @@
|
||||
|
||||
<!-- The html injected into a (x)html page if Umbraco is running in preview mode -->
|
||||
<PreviewBadge>
|
||||
<![CDATA[<a id="umbracoPreviewBadge" style="position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{0}/assets/img/preview-mode-badge.png') no-repeat;z-index: 9999999;" href="#" OnClick="javascript:window.top.location.href = '{0}/endPreview.aspx?redir={1}'"><span style="display:none;">In Preview Mode - click to end</span></a>]]></PreviewBadge>
|
||||
<![CDATA[
|
||||
<a id="umbracoPreviewBadge" style="z-index:99999; position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{0}/assets/img/preview-mode-badge.png') no-repeat;" href="#" OnClick="javascript:window.top.location.href = '{0}/endPreview.aspx?redir={1}'"><span style="display:none;">In Preview Mode - click to end</span></a>
|
||||
]]></PreviewBadge>
|
||||
|
||||
<!-- How Umbraco should handle errors during macro execution. Can be one of the following values:
|
||||
- inline - show an inline error within the macro but allow the page to continue rendering. Historial Umbraco behaviour.
|
||||
|
||||
@@ -75,7 +75,8 @@
|
||||
|
||||
<!-- The html injected into a (x)html page if Umbraco is running in preview mode -->
|
||||
<PreviewBadge>
|
||||
<![CDATA[<a id="umbracoPreviewBadge" style="position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{0}/assets/img/preview-mode-badge.png') no-repeat;" href="#" OnClick="javascript:window.top.location.href = '{0}/endPreview.aspx?redir={1}'"><span style="display:none;">In Preview Mode - click to end</span></a>
|
||||
<![CDATA[
|
||||
<a id="umbracoPreviewBadge" style="z-index:99999; position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{0}/assets/img/preview-mode-badge.png') no-repeat;" href="#" OnClick="javascript:window.top.location.href = '{0}/endPreview.aspx?redir={1}'"><span style="display:none;">In Preview Mode - click to end</span></a>
|
||||
]]></PreviewBadge>
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using LightInject;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Models.Mapping;
|
||||
using Umbraco.Web.Trees;
|
||||
|
||||
namespace Umbraco.Web.Composing.CompositionRoots
|
||||
{
|
||||
@@ -7,6 +10,7 @@ namespace Umbraco.Web.Composing.CompositionRoots
|
||||
{
|
||||
public void Compose(IServiceRegistry container)
|
||||
{
|
||||
//register the profiles
|
||||
container.Register<AuditMapperProfile>();
|
||||
container.Register<CodeFileMapperProfile>();
|
||||
container.Register<ContentMapperProfile>();
|
||||
@@ -25,6 +29,16 @@ namespace Umbraco.Web.Composing.CompositionRoots
|
||||
container.Register<TemplateMapperProfile>();
|
||||
container.Register<UserMapperProfile>();
|
||||
container.Register<LanguageMapperProfile>();
|
||||
|
||||
//register any resolvers, etc.. that the profiles use
|
||||
container.Register<ContentUrlResolver>();
|
||||
container.Register<ContentTreeNodeUrlResolver<IContent, ContentTreeController>>();
|
||||
container.Register<TabsAndPropertiesResolver<IContent, ContentItemDisplay>>();
|
||||
container.Register<TabsAndPropertiesResolver<IMedia, MediaItemDisplay>>();
|
||||
container.Register<ContentTreeNodeUrlResolver<IMedia, MediaTreeController>>();
|
||||
container.Register<MemberTabsAndPropertiesResolver>();
|
||||
container.Register<MemberTreeNodeUrlResolver>();
|
||||
container.Register<MemberBasicPropertiesResolver>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,9 @@ namespace Umbraco.Web.Editors
|
||||
public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
|
||||
{
|
||||
controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector(
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetNiceUrl", "id", typeof(int), typeof(Guid), typeof(Udi))));
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetNiceUrl", "id", typeof(int), typeof(Guid), typeof(Udi)),
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,6 +275,25 @@ namespace Umbraco.Web.Editors
|
||||
[OutgoingEditorModelEvent]
|
||||
[EnsureUserPermissionForContent("id")]
|
||||
public ContentItemDisplay GetById(int id, string culture = null)
|
||||
{
|
||||
var foundContent = GetObjectFromRequest(() => Services.ContentService.GetById(id));
|
||||
if (foundContent == null)
|
||||
{
|
||||
HandleContentNotFound(id);
|
||||
return null;//irrelevant since the above throws
|
||||
}
|
||||
var content = MapToDisplay(foundContent, culture);
|
||||
return content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content json for the content id
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
[OutgoingEditorModelEvent]
|
||||
[EnsureUserPermissionForContent("id")]
|
||||
public ContentItemDisplay GetById(Guid id, string culture = null)
|
||||
{
|
||||
var foundContent = GetObjectFromRequest(() => Services.ContentService.GetById(id));
|
||||
if (foundContent == null)
|
||||
@@ -285,6 +306,24 @@ namespace Umbraco.Web.Editors
|
||||
return content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content json for the content id
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
[OutgoingEditorModelEvent]
|
||||
[EnsureUserPermissionForContent("id")]
|
||||
public ContentItemDisplay GetById(Udi id, string culture = null)
|
||||
{
|
||||
var guidUdi = id as GuidUdi;
|
||||
if (guidUdi != null)
|
||||
{
|
||||
return GetById(guidUdi.Guid, culture);
|
||||
}
|
||||
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an empty content item for the
|
||||
/// </summary>
|
||||
@@ -1283,7 +1322,7 @@ namespace Umbraco.Web.Editors
|
||||
culture = Services.LocalizationService.GetDefaultLanguageIsoCode();
|
||||
}
|
||||
|
||||
var display = ContextMapper.Map<IContent, ContentItemDisplay>(content, UmbracoContext,
|
||||
var display = ContextMapper.Map<IContent, ContentItemDisplay>(content,
|
||||
new Dictionary<string, object> { { ContextMapper.CultureKey, culture } });
|
||||
|
||||
return display;
|
||||
|
||||
@@ -21,11 +21,12 @@ namespace Umbraco.Web.Editors
|
||||
/// An API controller used for dealing with member types
|
||||
/// </summary>
|
||||
[PluginController("UmbracoApi")]
|
||||
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
|
||||
[UmbracoTreeAuthorize(new string[] { Constants.Trees.MemberTypes, Constants.Trees.Members})]
|
||||
public class MemberTypeController : ContentTypeControllerBase<IMemberType>
|
||||
{
|
||||
private readonly MembershipProvider _provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
|
||||
public MemberTypeDisplay GetById(int id)
|
||||
{
|
||||
var ct = Services.MemberTypeService.Get(id);
|
||||
@@ -45,6 +46,7 @@ namespace Umbraco.Web.Editors
|
||||
/// <returns></returns>
|
||||
[HttpDelete]
|
||||
[HttpPost]
|
||||
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
|
||||
public HttpResponseMessage DeleteById(int id)
|
||||
{
|
||||
var foundType = Services.MemberTypeService.Get(id);
|
||||
@@ -71,6 +73,8 @@ namespace Umbraco.Web.Editors
|
||||
/// be looked up via the db, they need to be passed in.
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
|
||||
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
|
||||
public HttpResponseMessage GetAvailableCompositeMemberTypes(int contentTypeId,
|
||||
[FromUri]string[] filterContentTypes,
|
||||
[FromUri]string[] filterPropertyTypes)
|
||||
@@ -84,6 +88,7 @@ namespace Umbraco.Web.Editors
|
||||
return Request.CreateResponse(result);
|
||||
}
|
||||
|
||||
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
|
||||
public MemberTypeDisplay GetEmpty()
|
||||
{
|
||||
var ct = new MemberType(-1);
|
||||
@@ -107,6 +112,7 @@ namespace Umbraco.Web.Editors
|
||||
return Enumerable.Empty<ContentTypeBasic>();
|
||||
}
|
||||
|
||||
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
|
||||
public MemberTypeDisplay PostSave(MemberTypeSave contentTypeSave)
|
||||
{
|
||||
//get the persisted member type
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using AutoMapper;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
|
||||
|
||||
@@ -14,18 +14,25 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// </summary>
|
||||
internal class ContentMapperProfile : Profile
|
||||
{
|
||||
public ContentMapperProfile(IUserService userService, ILocalizedTextService textService, IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, ILocalizationService localizationService, ILogger logger)
|
||||
public ContentMapperProfile(
|
||||
ContentUrlResolver contentUrlResolver,
|
||||
ContentTreeNodeUrlResolver<IContent, ContentTreeController> contentTreeNodeUrlResolver,
|
||||
TabsAndPropertiesResolver<IContent, ContentItemDisplay> tabsAndPropertiesResolver,
|
||||
IUserService userService,
|
||||
ILocalizedTextService textService,
|
||||
IContentService contentService,
|
||||
IContentTypeService contentTypeService,
|
||||
IDataTypeService dataTypeService,
|
||||
ILocalizationService localizationService,
|
||||
ILogger logger)
|
||||
{
|
||||
// create, capture, cache
|
||||
var contentOwnerResolver = new OwnerResolver<IContent>(userService);
|
||||
var creatorResolver = new CreatorResolver(userService);
|
||||
var actionButtonsResolver = new ActionButtonsResolver(userService, contentService);
|
||||
var tabsAndPropertiesResolver = new TabsAndPropertiesResolver<IContent, ContentItemDisplay>(textService);
|
||||
var childOfListViewResolver = new ContentChildOfListViewResolver(contentService, contentTypeService);
|
||||
var contentTypeBasicResolver = new ContentTypeBasicResolver<IContent, ContentItemDisplay>();
|
||||
var contentTreeNodeUrlResolver = new ContentTreeNodeUrlResolver<IContent, ContentTreeController>();
|
||||
var defaultTemplateResolver = new DefaultTemplateResolver();
|
||||
var contentUrlResolver = new ContentUrlResolver(textService, contentService, logger);
|
||||
var variantResolver = new ContentItemDisplayVariationResolver(localizationService);
|
||||
|
||||
//FROM IContent TO ContentItemDisplay
|
||||
|
||||
@@ -12,9 +12,16 @@ namespace Umbraco.Web.Models.Mapping
|
||||
where TSource : IContentBase
|
||||
where TController : ContentTreeControllerBase
|
||||
{
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
|
||||
public ContentTreeNodeUrlResolver(IUmbracoContextAccessor umbracoContextAccessor)
|
||||
{
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
}
|
||||
|
||||
public string Resolve(TSource source, object destination, string destMember, ResolutionContext context)
|
||||
{
|
||||
var umbracoContext = context.GetUmbracoContext(throwIfMissing: false);
|
||||
var umbracoContext = _umbracoContextAccessor.UmbracoContext;
|
||||
if (umbracoContext == null) return null;
|
||||
|
||||
var urlHelper = new UrlHelper(umbracoContext.HttpContext.Request.RequestContext);
|
||||
|
||||
@@ -10,24 +10,36 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class ContentUrlResolver : IValueResolver<IContent, ContentItemDisplay, UrlInfo[]>
|
||||
{
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly PublishedRouter _publishedRouter;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly IContentService _contentService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ContentUrlResolver(ILocalizedTextService textService, IContentService contentService, ILogger logger)
|
||||
public ContentUrlResolver(
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
PublishedRouter publishedRouter,
|
||||
ILocalizationService localizationService,
|
||||
ILocalizedTextService textService,
|
||||
IContentService contentService,
|
||||
ILogger logger)
|
||||
{
|
||||
_textService = textService;
|
||||
_contentService = contentService;
|
||||
_logger = logger;
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
_publishedRouter = publishedRouter ?? throw new System.ArgumentNullException(nameof(publishedRouter));
|
||||
_localizationService = localizationService ?? throw new System.ArgumentNullException(nameof(localizationService));
|
||||
_textService = textService ?? throw new System.ArgumentNullException(nameof(textService));
|
||||
_contentService = contentService ?? throw new System.ArgumentNullException(nameof(contentService));
|
||||
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public UrlInfo[] Resolve(IContent source, ContentItemDisplay destination, UrlInfo[] destMember, ResolutionContext context)
|
||||
{
|
||||
var umbracoContext = context.GetUmbracoContext(throwIfMissing: false);
|
||||
var umbracoContext = _umbracoContextAccessor.UmbracoContext;
|
||||
|
||||
var urls = umbracoContext == null
|
||||
? new[] { UrlInfo.Message("Cannot generate urls without a current Umbraco Context") }
|
||||
: source.GetContentUrls(umbracoContext.UrlProvider, _textService, _contentService, _logger).ToArray();
|
||||
: source.GetContentUrls(_publishedRouter, umbracoContext, _localizationService, _textService, _contentService, _logger).ToArray();
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
@@ -10,42 +10,9 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// </summary>
|
||||
internal static class ContextMapper
|
||||
{
|
||||
public const string UmbracoContextKey = "ContextMapper.UmbracoContext";
|
||||
//public const string UmbracoContextKey = "ContextMapper.UmbracoContext";
|
||||
public const string CultureKey = "ContextMapper.Culture";
|
||||
|
||||
public static TDestination Map<TSource, TDestination>(TSource obj, UmbracoContext umbracoContext)
|
||||
=> Mapper.Map<TSource, TDestination>(obj, opt => opt.Items[UmbracoContextKey] = umbracoContext);
|
||||
|
||||
public static TDestination Map<TSource, TDestination>(TSource obj, UmbracoContext umbracoContext, IDictionary<string, object> contextVals)
|
||||
=> Mapper.Map<TSource, TDestination>(obj, opt =>
|
||||
{
|
||||
//set the umb ctx
|
||||
opt.Items[UmbracoContextKey] = umbracoContext;
|
||||
//set other supplied context vals
|
||||
if (contextVals != null)
|
||||
{
|
||||
foreach (var contextVal in contextVals)
|
||||
{
|
||||
opt.Items[contextVal.Key] = contextVal.Value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
public static TDestination Map<TSource, TDestination>(TSource obj, UmbracoContext umbracoContext, object contextVals)
|
||||
=> Mapper.Map<TSource, TDestination>(obj, opt =>
|
||||
{
|
||||
//set the umb ctx
|
||||
opt.Items[UmbracoContextKey] = umbracoContext;
|
||||
//set other supplied context vals
|
||||
if (contextVals != null)
|
||||
{
|
||||
foreach (var contextVal in contextVals.ToDictionary())
|
||||
{
|
||||
opt.Items[contextVal.Key] = contextVal.Value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
public static TDestination Map<TSource, TDestination>(TSource obj, IDictionary<string, object> contextVals)
|
||||
=> Mapper.Map<TSource, TDestination>(obj, opt =>
|
||||
{
|
||||
@@ -86,27 +53,6 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="UmbracoContext"/> in the mapping context if one is found
|
||||
/// </summary>
|
||||
/// <param name="resolutionContext"></param>
|
||||
/// <param name="throwIfMissing"></param>
|
||||
/// <returns></returns>
|
||||
public static UmbracoContext GetUmbracoContext(this ResolutionContext resolutionContext, bool throwIfMissing = true)
|
||||
{
|
||||
if (resolutionContext.Options.Items.TryGetValue(UmbracoContextKey, out var obj) && obj is UmbracoContext umbracoContext)
|
||||
return umbracoContext;
|
||||
|
||||
// better fail fast
|
||||
if (throwIfMissing)
|
||||
throw new InvalidOperationException("AutoMapper ResolutionContext does not contain an UmbracoContext.");
|
||||
|
||||
// fixme - not a good idea at all
|
||||
// because this falls back to magic singletons
|
||||
// so really we should remove this line, but then some tests+app breaks ;(
|
||||
return Umbraco.Web.Composing.Current.UmbracoContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,13 +14,19 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// </summary>
|
||||
internal class MediaMapperProfile : Profile
|
||||
{
|
||||
public MediaMapperProfile(IUserService userService, ILocalizedTextService textService, IDataTypeService dataTypeService, IMediaService mediaService, IMediaTypeService mediaTypeService, ILogger logger)
|
||||
public MediaMapperProfile(
|
||||
TabsAndPropertiesResolver<IMedia, MediaItemDisplay> tabsAndPropertiesResolver,
|
||||
ContentTreeNodeUrlResolver<IMedia, MediaTreeController> contentTreeNodeUrlResolver,
|
||||
IUserService userService,
|
||||
ILocalizedTextService textService,
|
||||
IDataTypeService dataTypeService,
|
||||
IMediaService mediaService,
|
||||
IMediaTypeService mediaTypeService,
|
||||
ILogger logger)
|
||||
{
|
||||
// create, capture, cache
|
||||
var mediaOwnerResolver = new OwnerResolver<IMedia>(userService);
|
||||
var tabsAndPropertiesResolver = new TabsAndPropertiesResolver<IMedia, MediaItemDisplay>(textService);
|
||||
var childOfListViewResolver = new MediaChildOfListViewResolver(mediaService, mediaTypeService);
|
||||
var contentTreeNodeUrlResolver = new ContentTreeNodeUrlResolver<IMedia, MediaTreeController>();
|
||||
var mediaTypeBasicResolver = new ContentTypeBasicResolver<IMedia, MediaItemDisplay>();
|
||||
|
||||
//FROM IMedia TO MediaItemDisplay
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -11,9 +12,17 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// </summary>
|
||||
internal class MemberBasicPropertiesResolver : IValueResolver<IMember, MemberBasic, IEnumerable<ContentPropertyBasic>>
|
||||
{
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
|
||||
public MemberBasicPropertiesResolver(IUmbracoContextAccessor umbracoContextAccessor)
|
||||
{
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
}
|
||||
|
||||
public IEnumerable<ContentPropertyBasic> Resolve(IMember source, MemberBasic destination, IEnumerable<ContentPropertyBasic> destMember, ResolutionContext context)
|
||||
{
|
||||
var umbracoContext = context.GetUmbracoContext();
|
||||
var umbracoContext = _umbracoContextAccessor.UmbracoContext;
|
||||
if (umbracoContext == null) throw new InvalidOperationException("Cannot resolve value without an UmbracoContext available");
|
||||
|
||||
var result = Mapper.Map<IEnumerable<Property>, IEnumerable<ContentPropertyBasic>>(
|
||||
// Sort properties so items from different compositions appear in correct order (see U4-9298). Map sorted properties.
|
||||
@@ -39,4 +48,4 @@ namespace Umbraco.Web.Models.Mapping
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,16 +14,19 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// </summary>
|
||||
internal class MemberMapperProfile : Profile
|
||||
{
|
||||
public MemberMapperProfile(IUserService userService, ILocalizedTextService textService, IMemberTypeService memberTypeService, IMemberService memberService)
|
||||
public MemberMapperProfile(
|
||||
MemberTabsAndPropertiesResolver tabsAndPropertiesResolver,
|
||||
MemberTreeNodeUrlResolver memberTreeNodeUrlResolver,
|
||||
MemberBasicPropertiesResolver memberBasicPropertiesResolver,
|
||||
IUserService userService,
|
||||
IMemberTypeService memberTypeService,
|
||||
IMemberService memberService)
|
||||
{
|
||||
// create, capture, cache
|
||||
var memberOwnerResolver = new OwnerResolver<IMember>(userService);
|
||||
var tabsAndPropertiesResolver = new MemberTabsAndPropertiesResolver(textService, memberService, userService);
|
||||
var memberProfiderFieldMappingResolver = new MemberProviderFieldResolver();
|
||||
var membershipScenarioMappingResolver = new MembershipScenarioResolver(memberTypeService);
|
||||
var memberDtoPropertiesResolver = new MemberDtoPropertiesResolver();
|
||||
var memberTreeNodeUrlResolver = new MemberTreeNodeUrlResolver();
|
||||
var memberBasicPropertiesResolver = new MemberBasicPropertiesResolver();
|
||||
|
||||
//FROM MembershipUser TO MediaItemDisplay - used when using a non-umbraco membership provider
|
||||
CreateMap<MembershipUser, MemberDisplay>().ConvertUsing<MembershipUserTypeConverter>();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Security;
|
||||
using AutoMapper;
|
||||
@@ -22,24 +23,27 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// </remarks>
|
||||
internal class MemberTabsAndPropertiesResolver : TabsAndPropertiesResolver<IMember, MemberDisplay>
|
||||
{
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public MemberTabsAndPropertiesResolver(ILocalizedTextService localizedTextService, IMemberService memberService, IUserService userService)
|
||||
public MemberTabsAndPropertiesResolver(IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService localizedTextService, IMemberService memberService, IUserService userService)
|
||||
: base(localizedTextService)
|
||||
{
|
||||
_localizedTextService = localizedTextService;
|
||||
_memberService = memberService;
|
||||
_userService = userService;
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
_localizedTextService = localizedTextService ?? throw new System.ArgumentNullException(nameof(localizedTextService));
|
||||
_memberService = memberService ?? throw new System.ArgumentNullException(nameof(memberService));
|
||||
_userService = userService ?? throw new System.ArgumentNullException(nameof(userService));
|
||||
}
|
||||
|
||||
public MemberTabsAndPropertiesResolver(ILocalizedTextService localizedTextService, IEnumerable<string> ignoreProperties, IMemberService memberService, IUserService userService)
|
||||
public MemberTabsAndPropertiesResolver(IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService localizedTextService, IEnumerable<string> ignoreProperties, IMemberService memberService, IUserService userService)
|
||||
: base(localizedTextService, ignoreProperties)
|
||||
{
|
||||
_localizedTextService = localizedTextService;
|
||||
_memberService = memberService;
|
||||
_userService = userService;
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
_localizedTextService = localizedTextService ?? throw new System.ArgumentNullException(nameof(localizedTextService));
|
||||
_memberService = memberService ?? throw new System.ArgumentNullException(nameof(memberService));
|
||||
_userService = userService ?? throw new System.ArgumentNullException(nameof(userService));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -80,7 +84,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
}
|
||||
}
|
||||
|
||||
var umbracoContext = context.GetUmbracoContext();
|
||||
var umbracoContext = _umbracoContextAccessor.UmbracoContext;
|
||||
if (umbracoContext != null
|
||||
&& umbracoContext.Security.CurrentUser != null
|
||||
&& umbracoContext.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings)))
|
||||
@@ -166,24 +170,25 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// <summary>
|
||||
/// Overridden to assign the IsSensitive property values
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="properties"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
protected override List<ContentPropertyDisplay> MapProperties(UmbracoContext umbracoContext, IContentBase content, List<Property> properties, ResolutionContext context)
|
||||
protected override List<ContentPropertyDisplay> MapProperties(IContentBase content, List<Property> properties, ResolutionContext context)
|
||||
{
|
||||
var result = base.MapProperties(umbracoContext, content, properties, context);
|
||||
var result = base.MapProperties(content, properties, context);
|
||||
var member = (IMember)content;
|
||||
var memberType = member.ContentType;
|
||||
|
||||
var umbracoContext = _umbracoContextAccessor.UmbracoContext;
|
||||
|
||||
//now update the IsSensitive value
|
||||
foreach (var prop in result)
|
||||
{
|
||||
//check if this property is flagged as sensitive
|
||||
var isSensitiveProperty = memberType.IsSensitiveProperty(prop.Alias);
|
||||
//check permissions for viewing sensitive data
|
||||
if (isSensitiveProperty && umbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false)
|
||||
if (isSensitiveProperty && (umbracoContext == null || umbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false))
|
||||
{
|
||||
//mark this property as sensitive
|
||||
prop.IsSensitive = true;
|
||||
|
||||
@@ -11,9 +11,16 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// </summary>
|
||||
internal class MemberTreeNodeUrlResolver : IValueResolver<IMember, MemberDisplay, string>
|
||||
{
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
|
||||
public MemberTreeNodeUrlResolver(IUmbracoContextAccessor umbracoContextAccessor)
|
||||
{
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
}
|
||||
|
||||
public string Resolve(IMember source, MemberDisplay destination, string destMember, ResolutionContext context)
|
||||
{
|
||||
var umbracoContext = context.GetUmbracoContext(throwIfMissing: false);
|
||||
var umbracoContext = _umbracoContextAccessor.UmbracoContext;
|
||||
if (umbracoContext == null) return null;
|
||||
|
||||
var urlHelper = new UrlHelper(umbracoContext.HttpContext.Request.RequestContext);
|
||||
|
||||
@@ -13,9 +13,9 @@ namespace Umbraco.Web.Models.Mapping
|
||||
public MemberDisplay Convert(MembershipUser source, MemberDisplay destination, ResolutionContext context)
|
||||
{
|
||||
//first convert to IMember
|
||||
var member = Mapper.Map<MembershipUser, IMember>(source);
|
||||
var member = Mapper.Map<IMember>(source);
|
||||
//then convert to MemberDisplay
|
||||
return ContextMapper.Map<IMember, MemberDisplay>(member, context.GetUmbracoContext());
|
||||
return Mapper.Map<MemberDisplay>(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,6 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// <summary>
|
||||
/// Maps properties on to the generic properties tab
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="tabs"></param>
|
||||
/// <param name="context"></param>
|
||||
@@ -172,14 +171,14 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// The generic properties tab is responsible for
|
||||
/// setting up the properties such as Created date, updated date, template selected, etc...
|
||||
/// </remarks>
|
||||
protected virtual void MapGenericProperties(UmbracoContext umbracoContext, IContentBase content, List<Tab<ContentPropertyDisplay>> tabs, ResolutionContext context)
|
||||
protected virtual void MapGenericProperties(IContentBase content, List<Tab<ContentPropertyDisplay>> tabs, ResolutionContext context)
|
||||
{
|
||||
// add the generic properties tab, for properties that don't belong to a tab
|
||||
// get the properties, map and translate them, then add the tab
|
||||
var noGroupProperties = content.GetNonGroupedProperties()
|
||||
.Where(x => IgnoreProperties.Contains(x.Alias) == false) // skip ignored
|
||||
.ToList();
|
||||
var genericproperties = MapProperties(umbracoContext, content, noGroupProperties, context);
|
||||
var genericproperties = MapProperties(content, noGroupProperties, context);
|
||||
|
||||
tabs.Add(new Tab<ContentPropertyDisplay>
|
||||
{
|
||||
@@ -225,12 +224,11 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// <summary>
|
||||
/// Maps a list of <see cref="Property"/> to a list of <see cref="ContentPropertyDisplay"/>
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="properties"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual List<ContentPropertyDisplay> MapProperties(UmbracoContext umbracoContext, IContentBase content, List<Property> properties, ResolutionContext context)
|
||||
protected virtual List<ContentPropertyDisplay> MapProperties(IContentBase content, List<Property> properties, ResolutionContext context)
|
||||
{
|
||||
//we need to map this way to pass the context through, I don't like it but we'll see what AutoMapper says: https://github.com/AutoMapper/AutoMapper/issues/2588
|
||||
var result = context.Mapper.Map<IEnumerable<Property>, IEnumerable<ContentPropertyDisplay>>(
|
||||
@@ -252,15 +250,16 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
public TabsAndPropertiesResolver(ILocalizedTextService localizedTextService)
|
||||
: base(localizedTextService)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
public TabsAndPropertiesResolver(ILocalizedTextService localizedTextService, IEnumerable<string> ignoreProperties)
|
||||
: base(localizedTextService, ignoreProperties)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
public virtual IEnumerable<Tab<ContentPropertyDisplay>> Resolve(TSource source, TDestination destination, IEnumerable<Tab<ContentPropertyDisplay>> destMember, ResolutionContext context)
|
||||
{
|
||||
var umbracoContext = context.GetUmbracoContext(throwIfMissing: false); // fixme
|
||||
var tabs = new List<Tab<ContentPropertyDisplay>>();
|
||||
|
||||
// add the tabs, for properties that belong to a tab
|
||||
@@ -285,7 +284,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
continue;
|
||||
|
||||
//map the properties
|
||||
var mappedProperties = MapProperties(umbracoContext, source, properties, context);
|
||||
var mappedProperties = MapProperties(source, properties, context);
|
||||
|
||||
// add the tab
|
||||
// we need to pick an identifier... there is no "right" way...
|
||||
@@ -303,7 +302,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
});
|
||||
}
|
||||
|
||||
MapGenericProperties(umbracoContext, source, tabs, context);
|
||||
MapGenericProperties(source, tabs, context);
|
||||
|
||||
// activate the first tab, if any
|
||||
if (tabs.Count > 0)
|
||||
|
||||
@@ -90,11 +90,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
public override bool HasValue(string culture = null, string segment = null)
|
||||
{
|
||||
ContextualizeVariation(ref culture, ref segment);
|
||||
|
||||
var sourceValue = GetSourceValue(culture, segment);
|
||||
|
||||
return sourceValue != null &&
|
||||
(!(sourceValue is string) || string.IsNullOrWhiteSpace((string) sourceValue) == false);
|
||||
return PropertyType.IsValue(GetSourceValue(culture, segment));
|
||||
}
|
||||
|
||||
// used to cache the CacheValues of this property
|
||||
|
||||
@@ -385,7 +385,7 @@ namespace Umbraco.Web
|
||||
|
||||
public static bool IsDescendant(this IPublishedContent content, IPublishedContent other)
|
||||
{
|
||||
return content.Ancestors().Any(x => x.Id == other.Id);
|
||||
return other.Level < content.Level && content.Path.InvariantStartsWith(other.Path);
|
||||
}
|
||||
|
||||
public static HtmlString IsDescendant(this IPublishedContent content, IPublishedContent other, string valueIfTrue)
|
||||
@@ -400,7 +400,7 @@ namespace Umbraco.Web
|
||||
|
||||
public static bool IsDescendantOrSelf(this IPublishedContent content, IPublishedContent other)
|
||||
{
|
||||
return content.AncestorsOrSelf().Any(x => x.Id == other.Id);
|
||||
return content.Path.InvariantStartsWith(other.Path);
|
||||
}
|
||||
|
||||
public static HtmlString IsDescendantOrSelf(this IPublishedContent content, IPublishedContent other, string valueIfTrue)
|
||||
@@ -415,8 +415,8 @@ namespace Umbraco.Web
|
||||
|
||||
public static bool IsAncestor(this IPublishedContent content, IPublishedContent other)
|
||||
{
|
||||
// avoid using Descendants(), that's expensive
|
||||
return other.Ancestors().Any(x => x.Id == content.Id);
|
||||
// avoid using Descendants(), or Ancestors(), they're expensive
|
||||
return content.Level < other.Level && other.Path.InvariantStartsWith(content.Path);
|
||||
}
|
||||
|
||||
public static HtmlString IsAncestor(this IPublishedContent content, IPublishedContent other, string valueIfTrue)
|
||||
@@ -431,8 +431,8 @@ namespace Umbraco.Web
|
||||
|
||||
public static bool IsAncestorOrSelf(this IPublishedContent content, IPublishedContent other)
|
||||
{
|
||||
// avoid using DescendantsOrSelf(), that's expensive
|
||||
return other.AncestorsOrSelf().Any(x => x.Id == content.Id);
|
||||
// avoid using DescendantsOrSelf() or AncestorsOrSelf(), they're expensive
|
||||
return other.Path.InvariantStartsWith(content.Path);
|
||||
}
|
||||
|
||||
public static HtmlString IsAncestorOrSelf(this IPublishedContent content, IPublishedContent other, string valueIfTrue)
|
||||
|
||||
@@ -78,6 +78,11 @@ namespace Umbraco.Web.Routing
|
||||
_publishedRouter.PrepareRequest(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the Umbraco Backoffice should ignore a collision for this request.
|
||||
/// </summary>
|
||||
public bool IgnorePublishedContentCollisions { get; set; }
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -18,10 +18,18 @@ namespace Umbraco.Web.Routing
|
||||
/// <para>Use when displaying Urls. If errors occur when generating the Urls, they will show in the list.</para>
|
||||
/// <para>Contains all the Urls that we can figure out (based upon domains, etc).</para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<UrlInfo> GetContentUrls(this IContent content, UrlProvider urlProvider, ILocalizedTextService textService, IContentService contentService, ILogger logger)
|
||||
public static IEnumerable<UrlInfo> GetContentUrls(this IContent content,
|
||||
PublishedRouter publishedRouter,
|
||||
UmbracoContext umbracoContext,
|
||||
ILocalizationService localizationService,
|
||||
ILocalizedTextService textService,
|
||||
IContentService contentService,
|
||||
ILogger logger)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
if (urlProvider == null) throw new ArgumentNullException(nameof(urlProvider));
|
||||
if (publishedRouter == null) throw new ArgumentNullException(nameof(publishedRouter));
|
||||
if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext));
|
||||
if (localizationService == null) throw new ArgumentNullException(nameof(localizationService));
|
||||
if (textService == null) throw new ArgumentNullException(nameof(textService));
|
||||
if (contentService == null) throw new ArgumentNullException(nameof(contentService));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
@@ -33,12 +41,7 @@ namespace Umbraco.Web.Routing
|
||||
urls.Add(UrlInfo.Message(textService.Localize("content/itemNotPublished")));
|
||||
return urls;
|
||||
}
|
||||
|
||||
// fixme inject
|
||||
// fixme PublishedRouter is stateless and should be a singleton!
|
||||
var localizationService = Core.Composing.Current.Services.LocalizationService;
|
||||
var publishedRouter = Core.Composing.Current.Container.GetInstance<PublishedRouter>();
|
||||
|
||||
|
||||
// build a list of urls, for the back-office
|
||||
// which will contain
|
||||
// - the 'main' urls, which is what .Url would return, for each culture
|
||||
@@ -61,7 +64,7 @@ namespace Umbraco.Web.Routing
|
||||
string url;
|
||||
try
|
||||
{
|
||||
url = urlProvider.GetUrl(content.Id, culture);
|
||||
url = umbracoContext.UrlProvider.GetUrl(content.Id, culture);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -83,7 +86,7 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
// got a url, deal with collisions, add url
|
||||
default:
|
||||
if (!DetectCollision(content, url, urls, culture, publishedRouter, textService)) // detect collisions, etc
|
||||
if (!DetectCollision(content, url, urls, culture, umbracoContext, publishedRouter, textService)) // detect collisions, etc
|
||||
urls.Add(UrlInfo.Url(url, culture));
|
||||
break;
|
||||
}
|
||||
@@ -137,13 +140,13 @@ namespace Umbraco.Web.Routing
|
||||
urls.Add(UrlInfo.Message(textService.Localize("content/parentCultureNotPublished", new[] { parent.Name }), culture));
|
||||
}
|
||||
|
||||
private static bool DetectCollision(IContent content, string url, List<UrlInfo> urls, string culture, PublishedRouter publishedRouter, ILocalizedTextService textService)
|
||||
private static bool DetectCollision(IContent content, string url, List<UrlInfo> urls, string culture, UmbracoContext umbracoContext, PublishedRouter publishedRouter, ILocalizedTextService textService)
|
||||
{
|
||||
// test for collisions on the 'main' url
|
||||
var uri = new Uri(url.TrimEnd('/'), UriKind.RelativeOrAbsolute);
|
||||
if (uri.IsAbsoluteUri == false) uri = uri.MakeAbsolute(UmbracoContext.Current.CleanedUmbracoUrl);
|
||||
if (uri.IsAbsoluteUri == false) uri = uri.MakeAbsolute(umbracoContext.CleanedUmbracoUrl);
|
||||
uri = UriUtility.UriToUmbraco(uri);
|
||||
var pcr = publishedRouter.CreateRequest(UmbracoContext.Current, uri);
|
||||
var pcr = publishedRouter.CreateRequest(umbracoContext, uri);
|
||||
publishedRouter.TryRouteRequest(pcr);
|
||||
|
||||
if (pcr.HasPublishedContent == false)
|
||||
@@ -152,6 +155,9 @@ namespace Umbraco.Web.Routing
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pcr.IgnorePublishedContentCollisions)
|
||||
return false;
|
||||
|
||||
if (pcr.PublishedContent.Id != content.Id)
|
||||
{
|
||||
var o = pcr.PublishedContent;
|
||||
|
||||
@@ -194,7 +194,7 @@ namespace Umbraco.Web.Runtime
|
||||
composition.Container.RegisterAuto(typeof(UmbracoViewPage<>));
|
||||
|
||||
// register published router
|
||||
composition.Container.Register<PublishedRouter>();
|
||||
composition.Container.RegisterSingleton<PublishedRouter>();
|
||||
composition.Container.Register(_ => UmbracoConfig.For.UmbracoSettings().WebRouting);
|
||||
|
||||
// register preview SignalR hub
|
||||
|
||||
@@ -587,7 +587,6 @@ namespace Umbraco.Web
|
||||
return ContentQuery.ContentAtRoot();
|
||||
}
|
||||
|
||||
/// <remarks>Had to change to internal for testing.</remarks>
|
||||
internal static bool ConvertIdObjectToInt(object id, out int intId)
|
||||
{
|
||||
switch (id)
|
||||
@@ -605,7 +604,6 @@ namespace Umbraco.Web
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>Had to change to internal for testing.</remarks>
|
||||
internal static bool ConvertIdObjectToGuid(object id, out Guid guidId)
|
||||
{
|
||||
switch (id)
|
||||
|
||||
@@ -6,6 +6,8 @@ using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Editors;
|
||||
using Umbraco.Web._Legacy.Actions;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Filters
|
||||
{
|
||||
@@ -68,7 +70,25 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
|
||||
if (parts.Length == 1)
|
||||
{
|
||||
nodeId = (int)actionContext.ActionArguments[parts[0]];
|
||||
var argument = actionContext.ActionArguments[parts[0]].ToString();
|
||||
// if the argument is an int, it will parse and can be assigned to nodeId
|
||||
// if might be a udi, so check that next
|
||||
// otherwise treat it as a guid - unlikely we ever get here
|
||||
if (int.TryParse(argument, out int parsedId))
|
||||
{
|
||||
nodeId = parsedId;
|
||||
}
|
||||
else if (Udi.TryParse(argument, true, out Udi udi))
|
||||
{
|
||||
//fixme: inject? we can't because this is an attribute but we could provide ctors and empty ctors that pass in the required services
|
||||
nodeId = Current.Services.EntityService.GetId(udi).Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
Guid.TryParse(argument, out Guid key);
|
||||
//fixme: inject? we can't because this is an attribute but we could provide ctors and empty ctors that pass in the required services
|
||||
nodeId = Current.Services.EntityService.GetId(key, UmbracoObjectTypes.Document).Result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -89,7 +109,8 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
|
||||
if (ContentController.CheckPermissions(
|
||||
actionContext.Request.Properties,
|
||||
UmbracoContext.Current.Security.CurrentUser,
|
||||
//fixme: inject? we can't because this is an attribute but we could provide ctors and empty ctors that pass in the required services
|
||||
Current.UmbracoContext.Security.CurrentUser,
|
||||
Current.Services.UserService,
|
||||
Current.Services.ContentService,
|
||||
Current.Services.EntityService,
|
||||
|
||||
Reference in New Issue
Block a user