Merge branch 'dev-v7.9' into temp-u4-10794
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
<dependencies>
|
||||
<dependency id="UmbracoCms.Core" version="[$version$]" />
|
||||
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.8, 4.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.10, 4.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.1, 3.0.0)" />
|
||||
</dependencies>
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Collections
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CompositeTypeTypeKey"/> struct.
|
||||
/// </summary>
|
||||
public CompositeTypeTypeKey(Type type1, Type type2)
|
||||
public CompositeTypeTypeKey(Type type1, Type type2) : this()
|
||||
{
|
||||
Type1 = type1;
|
||||
Type2 = type2;
|
||||
@@ -19,26 +19,35 @@ namespace Umbraco.Core.Collections
|
||||
/// <summary>
|
||||
/// Gets the first type.
|
||||
/// </summary>
|
||||
public Type Type1 { get; }
|
||||
public Type Type1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the second type.
|
||||
/// </summary>
|
||||
public Type Type2 { get; }
|
||||
public Type Type2 { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(CompositeTypeTypeKey other)
|
||||
=> Type1 == other.Type1 && Type2 == other.Type2;
|
||||
{
|
||||
return Type1 == other.Type1 && Type2 == other.Type2;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object obj)
|
||||
=> obj is CompositeTypeTypeKey other && Type1 == other.Type1 && Type2 == other.Type2;
|
||||
{
|
||||
var other = obj is CompositeTypeTypeKey ? (CompositeTypeTypeKey)obj : default(CompositeTypeTypeKey);
|
||||
return Type1 == other.Type1 && Type2 == other.Type2;
|
||||
}
|
||||
|
||||
public static bool operator ==(CompositeTypeTypeKey key1, CompositeTypeTypeKey key2)
|
||||
=> key1.Type1 == key2.Type1 && key1.Type2 == key2.Type2;
|
||||
{
|
||||
return key1.Type1 == key2.Type1 && key1.Type2 == key2.Type2;
|
||||
}
|
||||
|
||||
public static bool operator !=(CompositeTypeTypeKey key1, CompositeTypeTypeKey key2)
|
||||
=> key1.Type1 != key2.Type1 || key1.Type2 != key2.Type2;
|
||||
{
|
||||
return key1.Type1 != key2.Type1 || key1.Type2 != key2.Type2;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
|
||||
@@ -40,4 +40,4 @@ namespace Umbraco.Core.Configuration
|
||||
Current.Revision > 0 ? Current.Revision.ToInvariantString() : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
|
||||
public const string AdminGroupAlias = "admin";
|
||||
public const string SensitiveDataGroupAlias = "sensitiveData";
|
||||
public const string TranslatorGroupAlias = "translator";
|
||||
|
||||
public const string BackOfficeAuthenticationType = "UmbracoBackOffice";
|
||||
@@ -36,4 +37,4 @@ namespace Umbraco.Core
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,13 @@
|
||||
/// <returns></returns>
|
||||
bool MemberCanViewProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is marked as storing sensitive values on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
bool IsSensitiveProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is editable by the Member.
|
||||
/// </summary>
|
||||
@@ -32,5 +39,12 @@
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
void SetMemberCanViewProperty(string propertyTypeAlias, bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is a sensitive value on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
void SetIsSensitiveProperty(string propertyTypeAlias, bool value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile) by the PropertyTypes' alias.
|
||||
/// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile, IsSensitive) by the PropertyTypes' alias.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
internal IDictionary<string, MemberTypePropertyProfileAccess> MemberTypePropertyTypes { get; private set; }
|
||||
@@ -76,11 +76,11 @@ namespace Umbraco.Core.Models
|
||||
/// <returns></returns>
|
||||
public bool MemberCanEditProperty(string propertyTypeAlias)
|
||||
{
|
||||
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
|
||||
MemberTypePropertyProfileAccess propertyProfile;
|
||||
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out propertyProfile))
|
||||
{
|
||||
return MemberTypePropertyTypes[propertyTypeAlias].IsEditable;
|
||||
return propertyProfile.IsEditable;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -91,11 +91,26 @@ namespace Umbraco.Core.Models
|
||||
/// <returns></returns>
|
||||
public bool MemberCanViewProperty(string propertyTypeAlias)
|
||||
{
|
||||
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
|
||||
MemberTypePropertyProfileAccess propertyProfile;
|
||||
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out propertyProfile))
|
||||
{
|
||||
return MemberTypePropertyTypes[propertyTypeAlias].IsVisible;
|
||||
return propertyProfile.IsVisible;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is marked as storing sensitive values on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
public bool IsSensitiveProperty(string propertyTypeAlias)
|
||||
{
|
||||
MemberTypePropertyProfileAccess propertyProfile;
|
||||
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out propertyProfile))
|
||||
{
|
||||
return propertyProfile.IsSensitive;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -106,13 +121,14 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
public void SetMemberCanEditProperty(string propertyTypeAlias, bool value)
|
||||
{
|
||||
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
|
||||
MemberTypePropertyProfileAccess propertyProfile;
|
||||
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out propertyProfile))
|
||||
{
|
||||
MemberTypePropertyTypes[propertyTypeAlias].IsEditable = value;
|
||||
propertyProfile.IsEditable = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var tuple = new MemberTypePropertyProfileAccess(false, value);
|
||||
var tuple = new MemberTypePropertyProfileAccess(false, value, false);
|
||||
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
|
||||
}
|
||||
}
|
||||
@@ -124,15 +140,35 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
public void SetMemberCanViewProperty(string propertyTypeAlias, bool value)
|
||||
{
|
||||
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
|
||||
MemberTypePropertyProfileAccess propertyProfile;
|
||||
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out propertyProfile))
|
||||
{
|
||||
MemberTypePropertyTypes[propertyTypeAlias].IsVisible = value;
|
||||
propertyProfile.IsVisible = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var tuple = new MemberTypePropertyProfileAccess(value, false);
|
||||
var tuple = new MemberTypePropertyProfileAccess(value, false, false);
|
||||
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is a sensitive value on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
public void SetIsSensitiveProperty(string propertyTypeAlias, bool value)
|
||||
{
|
||||
MemberTypePropertyProfileAccess propertyProfile;
|
||||
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out propertyProfile))
|
||||
{
|
||||
propertyProfile.IsSensitive = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var tuple = new MemberTypePropertyProfileAccess(false, false, true);
|
||||
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,15 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
internal class MemberTypePropertyProfileAccess
|
||||
{
|
||||
public MemberTypePropertyProfileAccess(bool isVisible, bool isEditable)
|
||||
public MemberTypePropertyProfileAccess(bool isVisible, bool isEditable, bool isSenstive)
|
||||
{
|
||||
IsVisible = isVisible;
|
||||
IsEditable = isEditable;
|
||||
IsSensitive = isSenstive;
|
||||
}
|
||||
|
||||
public bool IsVisible { get; set; }
|
||||
public bool IsEditable { get; set; }
|
||||
public bool IsSensitive { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,5 +27,9 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[Column("viewOnProfile")]
|
||||
[Constraint(Default = "0")]
|
||||
public bool ViewOnProfile { get; set; }
|
||||
|
||||
[Column("isSensitive")]
|
||||
[Constraint(Default = "0")]
|
||||
public bool IsSensitive { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,9 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[Column("viewOnProfile")]
|
||||
public bool ViewOnProfile { get; set; }
|
||||
|
||||
[Column("isSensitive")]
|
||||
public bool IsSensitive { get; set; }
|
||||
|
||||
/* cmsDataType */
|
||||
[Column("propertyEditorAlias")]
|
||||
public string PropertyEditorAlias { get; set; }
|
||||
@@ -52,7 +55,8 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[Column("dbType")]
|
||||
public string DbType { get; set; }
|
||||
|
||||
[Column("UniqueID")]
|
||||
[Column("UniqueID")]
|
||||
public Guid UniqueId { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
}
|
||||
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn(IdentitySeed = 5)]
|
||||
[PrimaryKeyColumn(IdentitySeed = 6)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Column("userGroupAlias")]
|
||||
@@ -68,4 +68,4 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[ResultColumn]
|
||||
public int UserCount { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,6 +261,16 @@ namespace Umbraco.Core.Models
|
||||
return user.Groups != null && user.Groups.Any(x => x.Alias == Constants.Security.AdminGroupAlias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this user has access to view sensitive data
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
public static bool HasAccessToSensitiveData(this IUser user)
|
||||
{
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
return user.Groups != null && user.Groups.Any(x => x.Alias == Constants.Security.SensitiveDataGroupAlias);
|
||||
}
|
||||
|
||||
// calc. start nodes, combining groups' and user's, and excluding what's in the bin
|
||||
public static int[] CalculateContentStartNodeIds(this IUser user, IEntityService entityService)
|
||||
{
|
||||
@@ -413,4 +423,4 @@ namespace Umbraco.Core.Models
|
||||
return lsn.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +140,8 @@ namespace Umbraco.Core
|
||||
if (underlying != null)
|
||||
{
|
||||
// Special case for empty strings for bools/dates which should return null if an empty string.
|
||||
if (input is string inputString)
|
||||
var inputString = input as string;
|
||||
if (inputString != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(inputString) && (underlying == typeof(DateTime) || underlying == typeof(bool)))
|
||||
{
|
||||
@@ -166,7 +167,8 @@ namespace Umbraco.Core
|
||||
{
|
||||
// target is not a generic type
|
||||
|
||||
if (input is string inputString)
|
||||
var inputString = input as string;
|
||||
if (inputString != null)
|
||||
{
|
||||
// Try convert from string, returns an Attempt if the string could be
|
||||
// processed (either succeeded or failed), else null if we need to try
|
||||
@@ -207,7 +209,8 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
// Re-check convertables since we altered the input through recursion
|
||||
if (input is IConvertible convertible2)
|
||||
var convertible2 = input as IConvertible;
|
||||
if (convertible2 != null)
|
||||
{
|
||||
return Attempt.Succeed(Convert.ChangeType(convertible2, target));
|
||||
}
|
||||
@@ -265,7 +268,8 @@ namespace Umbraco.Core
|
||||
{
|
||||
if (target == typeof(int))
|
||||
{
|
||||
if (int.TryParse(input, out var value))
|
||||
int value;
|
||||
if (int.TryParse(input, out value))
|
||||
{
|
||||
return Attempt<object>.Succeed(value);
|
||||
}
|
||||
@@ -273,26 +277,30 @@ namespace Umbraco.Core
|
||||
// Because decimal 100.01m will happily convert to integer 100, it
|
||||
// makes sense that string "100.01" *also* converts to integer 100.
|
||||
var input2 = NormalizeNumberDecimalSeparator(input);
|
||||
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out var value2), Convert.ToInt32(value2));
|
||||
decimal value2;
|
||||
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt32(value2));
|
||||
}
|
||||
|
||||
if (target == typeof(long))
|
||||
{
|
||||
if (long.TryParse(input, out var value))
|
||||
long value;
|
||||
if (long.TryParse(input, out value))
|
||||
{
|
||||
return Attempt<object>.Succeed(value);
|
||||
}
|
||||
|
||||
// Same as int
|
||||
var input2 = NormalizeNumberDecimalSeparator(input);
|
||||
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out var value2), Convert.ToInt64(value2));
|
||||
decimal value2;
|
||||
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt64(value2));
|
||||
}
|
||||
|
||||
// TODO: Should we do the decimal trick for short, byte, unsigned?
|
||||
|
||||
if (target == typeof(bool))
|
||||
{
|
||||
if (bool.TryParse(input, out var value))
|
||||
bool value;
|
||||
if (bool.TryParse(input, out value))
|
||||
{
|
||||
return Attempt<object>.Succeed(value);
|
||||
}
|
||||
@@ -305,42 +313,53 @@ namespace Umbraco.Core
|
||||
switch (Type.GetTypeCode(target))
|
||||
{
|
||||
case TypeCode.Int16:
|
||||
return Attempt<object>.SucceedIf(short.TryParse(input, out var value), value);
|
||||
short value;
|
||||
return Attempt<object>.SucceedIf(short.TryParse(input, out value), value);
|
||||
|
||||
case TypeCode.Double:
|
||||
var input2 = NormalizeNumberDecimalSeparator(input);
|
||||
return Attempt<object>.SucceedIf(double.TryParse(input2, out var valueD), valueD);
|
||||
double valueD;
|
||||
return Attempt<object>.SucceedIf(double.TryParse(input2, out valueD), valueD);
|
||||
|
||||
case TypeCode.Single:
|
||||
var input3 = NormalizeNumberDecimalSeparator(input);
|
||||
return Attempt<object>.SucceedIf(float.TryParse(input3, out var valueF), valueF);
|
||||
float valueF;
|
||||
return Attempt<object>.SucceedIf(float.TryParse(input3, out valueF), valueF);
|
||||
|
||||
case TypeCode.Char:
|
||||
return Attempt<object>.SucceedIf(char.TryParse(input, out var valueC), valueC);
|
||||
char valueC;
|
||||
return Attempt<object>.SucceedIf(char.TryParse(input, out valueC), valueC);
|
||||
|
||||
case TypeCode.Byte:
|
||||
return Attempt<object>.SucceedIf(byte.TryParse(input, out var valueB), valueB);
|
||||
byte valueB;
|
||||
return Attempt<object>.SucceedIf(byte.TryParse(input, out valueB), valueB);
|
||||
|
||||
case TypeCode.SByte:
|
||||
return Attempt<object>.SucceedIf(sbyte.TryParse(input, out var valueSb), valueSb);
|
||||
sbyte valueSb;
|
||||
return Attempt<object>.SucceedIf(sbyte.TryParse(input, out valueSb), valueSb);
|
||||
|
||||
case TypeCode.UInt32:
|
||||
return Attempt<object>.SucceedIf(uint.TryParse(input, out var valueU), valueU);
|
||||
uint valueU;
|
||||
return Attempt<object>.SucceedIf(uint.TryParse(input, out valueU), valueU);
|
||||
|
||||
case TypeCode.UInt16:
|
||||
return Attempt<object>.SucceedIf(ushort.TryParse(input, out var valueUs), valueUs);
|
||||
ushort valueUs;
|
||||
return Attempt<object>.SucceedIf(ushort.TryParse(input, out valueUs), valueUs);
|
||||
|
||||
case TypeCode.UInt64:
|
||||
return Attempt<object>.SucceedIf(ulong.TryParse(input, out var valueUl), valueUl);
|
||||
ulong valueUl;
|
||||
return Attempt<object>.SucceedIf(ulong.TryParse(input, out valueUl), valueUl);
|
||||
}
|
||||
}
|
||||
else if (target == typeof(Guid))
|
||||
{
|
||||
return Attempt<object>.SucceedIf(Guid.TryParse(input, out var value), value);
|
||||
Guid value;
|
||||
return Attempt<object>.SucceedIf(Guid.TryParse(input, out value), value);
|
||||
}
|
||||
else if (target == typeof(DateTime))
|
||||
{
|
||||
if (DateTime.TryParse(input, out var value))
|
||||
DateTime value;
|
||||
if (DateTime.TryParse(input, out value))
|
||||
{
|
||||
switch (value.Kind)
|
||||
{
|
||||
@@ -360,20 +379,24 @@ namespace Umbraco.Core
|
||||
}
|
||||
else if (target == typeof(DateTimeOffset))
|
||||
{
|
||||
return Attempt<object>.SucceedIf(DateTimeOffset.TryParse(input, out var value), value);
|
||||
DateTimeOffset value;
|
||||
return Attempt<object>.SucceedIf(DateTimeOffset.TryParse(input, out value), value);
|
||||
}
|
||||
else if (target == typeof(TimeSpan))
|
||||
{
|
||||
return Attempt<object>.SucceedIf(TimeSpan.TryParse(input, out var value), value);
|
||||
TimeSpan value;
|
||||
return Attempt<object>.SucceedIf(TimeSpan.TryParse(input, out value), value);
|
||||
}
|
||||
else if (target == typeof(decimal))
|
||||
{
|
||||
var input2 = NormalizeNumberDecimalSeparator(input);
|
||||
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out var value), value);
|
||||
decimal value;
|
||||
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out value), value);
|
||||
}
|
||||
else if (input != null && target == typeof(Version))
|
||||
{
|
||||
return Attempt<object>.SucceedIf(Version.TryParse(input, out var value), value);
|
||||
Version value;
|
||||
return Attempt<object>.SucceedIf(Version.TryParse(input, out value), value);
|
||||
}
|
||||
|
||||
// E_NOTIMPL IPAddress, BigInteger
|
||||
@@ -658,7 +681,8 @@ namespace Umbraco.Core
|
||||
{
|
||||
var key = new CompositeTypeTypeKey(source, target);
|
||||
|
||||
if (InputTypeConverterCache.TryGetValue(key, out TypeConverter typeConverter))
|
||||
TypeConverter typeConverter;
|
||||
if (InputTypeConverterCache.TryGetValue(key, out typeConverter))
|
||||
{
|
||||
return typeConverter;
|
||||
}
|
||||
@@ -678,7 +702,8 @@ namespace Umbraco.Core
|
||||
{
|
||||
var key = new CompositeTypeTypeKey(source, target);
|
||||
|
||||
if (DestinationTypeConverterCache.TryGetValue(key, out TypeConverter typeConverter))
|
||||
TypeConverter typeConverter;
|
||||
if (DestinationTypeConverterCache.TryGetValue(key, out typeConverter))
|
||||
{
|
||||
return typeConverter;
|
||||
}
|
||||
@@ -696,7 +721,8 @@ namespace Umbraco.Core
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Type GetCachedGenericNullableType(Type type)
|
||||
{
|
||||
if (NullableGenericCache.TryGetValue(type, out Type underlyingType))
|
||||
Type underlyingType;
|
||||
if (NullableGenericCache.TryGetValue(type, out underlyingType))
|
||||
{
|
||||
return underlyingType;
|
||||
}
|
||||
@@ -715,7 +741,8 @@ namespace Umbraco.Core
|
||||
private static bool GetCachedCanAssign(object input, Type source, Type target)
|
||||
{
|
||||
var key = new CompositeTypeTypeKey(source, target);
|
||||
if (AssignableTypeCache.TryGetValue(key, out bool canConvert))
|
||||
bool canConvert;
|
||||
if (AssignableTypeCache.TryGetValue(key, out canConvert))
|
||||
{
|
||||
return canConvert;
|
||||
}
|
||||
@@ -734,7 +761,8 @@ namespace Umbraco.Core
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool GetCachedCanConvertToBoolean(Type type)
|
||||
{
|
||||
if (BoolConvertCache.TryGetValue(type, out bool result))
|
||||
bool result;
|
||||
if (BoolConvertCache.TryGetValue(type, out result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
@@ -747,4 +775,4 @@ namespace Umbraco.Core
|
||||
return BoolConvertCache[type] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,8 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
NodeId = entity.Id,
|
||||
PropertyTypeId = x.Id,
|
||||
CanEdit = memberType.MemberCanEditProperty(x.Alias),
|
||||
ViewOnProfile = memberType.MemberCanViewProperty(x.Alias)
|
||||
ViewOnProfile = memberType.MemberCanViewProperty(x.Alias),
|
||||
IsSensitive = memberType.IsSensitiveProperty(x.Alias)
|
||||
}).ToList();
|
||||
return dtos;
|
||||
}
|
||||
@@ -159,4 +160,4 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,10 +56,10 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
//Add the standard PropertyType to the current list
|
||||
propertyTypes.Add(standardPropertyType.Value);
|
||||
|
||||
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
|
||||
|
||||
//Internal dictionary for adding "MemberCanEdit", "VisibleOnProfile", "IsSensitive" properties to each PropertyType
|
||||
memberType.MemberTypePropertyTypes.Add(standardPropertyType.Key,
|
||||
new MemberTypePropertyProfileAccess(false, false));
|
||||
new MemberTypePropertyProfileAccess(false, false, false));
|
||||
}
|
||||
memberType.NoGroupPropertyTypes = propertyTypes;
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
|
||||
memberType.MemberTypePropertyTypes.Add(typeDto.Alias,
|
||||
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit));
|
||||
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit, typeDto.IsSensitive));
|
||||
|
||||
var tempGroupDto = groupDto;
|
||||
|
||||
@@ -157,7 +157,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
|
||||
memberType.MemberTypePropertyTypes.Add(typeDto.Alias,
|
||||
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit));
|
||||
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit, typeDto.IsSensitive));
|
||||
|
||||
//ensures that any built-in membership properties have their correct dbtype assigned no matter
|
||||
//what the underlying data type is
|
||||
@@ -198,4 +198,4 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,11 +178,13 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 2, StartMediaId = -1, StartContentId = -1, Alias = "writer", Name = "Writers", DefaultPermissions = "CAH:F", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-edit" });
|
||||
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 3, StartMediaId = -1, StartContentId = -1, Alias = "editor", Name = "Editors", DefaultPermissions = "CADMOSKTPUZ:5Fï", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-tools" });
|
||||
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 4, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.TranslatorGroupAlias, Name = "Translators", DefaultPermissions = "AF", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-globe" });
|
||||
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 5, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.SensitiveDataGroupAlias, Name = "Sensitive data", DefaultPermissions = "", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-lock" });
|
||||
}
|
||||
|
||||
private void CreateUmbracoUser2UserGroupData()
|
||||
{
|
||||
_database.Insert(new User2UserGroupDto { UserGroupId = 1, UserId = 0 });
|
||||
_database.Insert(new User2UserGroupDto { UserGroupId = 1, UserId = 0 }); //add admin to admins
|
||||
_database.Insert(new User2UserGroupDto { UserGroupId = 5, UserId = 0 }); //add admin to sensitive data
|
||||
}
|
||||
|
||||
private void CreateUmbracoUserGroup2AppData()
|
||||
@@ -337,4 +339,4 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
_database.Insert("umbracoMigration", "pk", false, dto);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +154,12 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
return new Version(7, 7, 0);
|
||||
}
|
||||
|
||||
//if the error is for isSensitive column it must be the previous version to 7.9 since that is when it is added
|
||||
if (Errors.Any(x => x.Item1.Equals("Column") && (x.Item2.InvariantEquals("cmsMemberType,isSensitive"))))
|
||||
{
|
||||
return new Version(7, 8, 0);
|
||||
}
|
||||
|
||||
return UmbracoVersion.Current;
|
||||
}
|
||||
|
||||
@@ -214,4 +220,4 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenEightZero
|
||||
{
|
||||
[Migration("7.9.0", 1, Constants.System.UmbracoMigrationName)]
|
||||
public class AddIsSensitiveMemberTypeColumn : MigrationBase
|
||||
{
|
||||
public AddIsSensitiveMemberTypeColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
//Don't exeucte if the column is already there
|
||||
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
|
||||
|
||||
if (columns.Any(x => x.TableName.InvariantEquals("cmsMemberType") && x.ColumnName.InvariantEquals("isSensitive")) == false)
|
||||
{
|
||||
Create.Column("isSensitive").OnTable("cmsMemberType").AsBoolean().WithDefaultValue(0).NotNullable();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenNineZero
|
||||
{
|
||||
[Migration("7.9.0", 2, Constants.System.UmbracoMigrationName)]
|
||||
public class CreateSensitiveDataUserGroup : MigrationBase
|
||||
{
|
||||
public CreateSensitiveDataUserGroup(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
Execute.Code(database =>
|
||||
{
|
||||
//Don't exeucte if the group is already there
|
||||
var exists = database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoUserGroup WHERE userGroupAlias = @userGroupAlias",
|
||||
new {userGroupAlias = Constants.Security.SensitiveDataGroupAlias });
|
||||
if (exists == 0)
|
||||
{
|
||||
var resultId = database.Insert("umbracoUserGroup", "id", new UserGroupDto { StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.SensitiveDataGroupAlias, Name = "Sensitive data", DefaultPermissions = "", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-lock" });
|
||||
database.Insert(new User2UserGroupDto { UserGroupId = Convert.ToInt32(resultId), UserId = 0 }); //add admin to sensitive data
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
});
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
sql.Select("umbracoNode.*", "cmsContentType.*", "cmsPropertyType.id AS PropertyTypeId", "cmsPropertyType.Alias",
|
||||
"cmsPropertyType.Name", "cmsPropertyType.Description", "cmsPropertyType.mandatory", "cmsPropertyType.UniqueID",
|
||||
"cmsPropertyType.validationRegExp", "cmsPropertyType.dataTypeId", "cmsPropertyType.sortOrder AS PropertyTypeSortOrder",
|
||||
"cmsPropertyType.propertyTypeGroupId AS PropertyTypesGroupId", "cmsMemberType.memberCanEdit", "cmsMemberType.viewOnProfile",
|
||||
"cmsPropertyType.propertyTypeGroupId AS PropertyTypesGroupId",
|
||||
"cmsMemberType.memberCanEdit", "cmsMemberType.viewOnProfile", "cmsMemberType.isSensitive",
|
||||
"cmsDataType.propertyEditorAlias", "cmsDataType.dbType", "cmsPropertyTypeGroup.id AS PropertyTypeGroupId",
|
||||
"cmsPropertyTypeGroup.text AS PropertyGroupName", "cmsPropertyTypeGroup.uniqueID AS PropertyGroupUniqueID",
|
||||
"cmsPropertyTypeGroup.sortorder AS PropertyGroupSortOrder", "cmsPropertyTypeGroup.contenttypeNodeId")
|
||||
@@ -362,4 +363,4 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return Attempt<Guid>.Fail(propertyEditor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,9 +401,10 @@ namespace Umbraco.Core.Publishing
|
||||
content.Name, content.Id));
|
||||
}
|
||||
|
||||
// if newest is published, unpublish
|
||||
if (content.Published)
|
||||
content.ChangePublishedState(PublishedState.Unpublished);
|
||||
// make sure we dirty .Published and always unpublish
|
||||
// the version we have here could be the newest, !Published
|
||||
content.ChangePublishedState(PublishedState.Published);
|
||||
content.ChangePublishedState(PublishedState.Unpublished);
|
||||
|
||||
_logger.Info<PublishingStrategy>(
|
||||
string.Format("Content '{0}' with Id '{1}' has been unpublished.",
|
||||
|
||||
@@ -884,8 +884,8 @@ namespace Umbraco.Core.Services
|
||||
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
|
||||
{
|
||||
var repository = RepositoryFactory.CreateContentRepository(uow);
|
||||
var query = Query<IContent>.Builder.Where(x => x.Published && x.ExpireDate <= DateTime.Now);
|
||||
return repository.GetByQuery(query);
|
||||
var query = Query<IContent>.Builder.Where(x => x.ExpireDate <= DateTime.Now);
|
||||
return repository.GetByQuery(query).Where(x => x.HasPublishedVersion);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Umbraco.Core.Sync
|
||||
if (newApplicationUrl)
|
||||
{
|
||||
appContext._umbracoApplicationDomains.Add(applicationUrl);
|
||||
LogHelper.Info(typeof(ApplicationUrlHelper), $"New ApplicationUrl detected: {applicationUrl}");
|
||||
LogHelper.Info(typeof(ApplicationUrlHelper), string.Format("New ApplicationUrl detected: {0}", applicationUrl));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,4 +169,4 @@ namespace Umbraco.Core.Sync
|
||||
return url.TrimEnd('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,6 +560,8 @@
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenEightZero\AddCmsMediaTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenEightZero\AddUserLoginTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenNineZero\AddUmbracoConsentTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenNineZero\AddIsSensitiveMemberTypeColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenNineZero\CreateSensitiveDataUserGroup.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSevenZero\AddIndexToDictionaryKeyColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSevenZero\EnsureContentTemplatePermissions.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSevenZero\ReduceDictionaryKeyColumnsSize.cs" />
|
||||
|
||||
@@ -37,7 +37,8 @@ namespace Umbraco.Tests.Benchmarks
|
||||
{
|
||||
// This method is 10% faster
|
||||
var key = new CompositeTypeTypeKey(source, target);
|
||||
if (AssignableTypeCache.TryGetValue(key, out bool canConvert))
|
||||
bool canConvert;
|
||||
if (AssignableTypeCache.TryGetValue(key, out canConvert))
|
||||
{
|
||||
return canConvert;
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
Assert.AreEqual(propTypes.ElementAt(j).DataTypeId, result.PropertyTypes.ElementAt(j).DataTypeDefinitionId);
|
||||
Assert.AreEqual(propTypes.ElementAt(j).MemberCanViewProperty, result.MemberCanViewProperty(result.PropertyTypes.ElementAt(j).Alias));
|
||||
Assert.AreEqual(propTypes.ElementAt(j).MemberCanEditProperty, result.MemberCanEditProperty(result.PropertyTypes.ElementAt(j).Alias));
|
||||
Assert.AreEqual(propTypes.ElementAt(j).IsSensitiveData, result.IsSensitiveProperty(result.PropertyTypes.ElementAt(j).Alias));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +336,7 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
.Returns(new[] { new TextboxPropertyEditor() });
|
||||
|
||||
var memberType = MockedContentTypes.CreateSimpleMemberType();
|
||||
memberType.MemberTypePropertyTypes[memberType.PropertyTypes.Last().Alias] = new MemberTypePropertyProfileAccess(true, true);
|
||||
memberType.MemberTypePropertyTypes[memberType.PropertyTypes.Last().Alias] = new MemberTypePropertyProfileAccess(true, true, true);
|
||||
|
||||
MockedContentTypes.EnsureAllIds(memberType, 8888);
|
||||
|
||||
@@ -539,6 +540,7 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
{
|
||||
MemberCanEditProperty = true,
|
||||
MemberCanViewProperty = true,
|
||||
IsSensitiveData = true,
|
||||
Id = 33,
|
||||
SortOrder = 1,
|
||||
Alias = "prop1",
|
||||
@@ -556,6 +558,7 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
{
|
||||
MemberCanViewProperty = false,
|
||||
MemberCanEditProperty = false,
|
||||
IsSensitiveData = false,
|
||||
Id = 34,
|
||||
SortOrder = 2,
|
||||
Alias = "prop2",
|
||||
@@ -944,6 +947,7 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
Label = "Prop 1",
|
||||
MemberCanViewProperty = true,
|
||||
MemberCanEditProperty = true,
|
||||
IsSensitiveData = true,
|
||||
Validation = new PropertyTypeValidation()
|
||||
{
|
||||
Mandatory = true,
|
||||
@@ -963,6 +967,7 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
Assert.AreEqual(basic.Validation, result.Validation);
|
||||
Assert.AreEqual(basic.MemberCanViewProperty, result.MemberCanViewProperty);
|
||||
Assert.AreEqual(basic.MemberCanEditProperty, result.MemberCanEditProperty);
|
||||
Assert.AreEqual(basic.IsSensitiveData, result.IsSensitiveData);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -1029,6 +1034,7 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
{
|
||||
MemberCanEditProperty = true,
|
||||
MemberCanViewProperty = true,
|
||||
IsSensitiveData = true,
|
||||
Alias = "property1",
|
||||
Description = "this is property 1",
|
||||
Inherited = false,
|
||||
|
||||
@@ -47,6 +47,10 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
|
||||
var mockedContentService = Mock.Of<IContentService>();
|
||||
var mockedMediaService = Mock.Of<IMediaService>();
|
||||
var mockedEntityService = Mock.Of<IEntityService>();
|
||||
var mockedMemberService = Mock.Of<IMemberService>();
|
||||
var mockedMemberTypeService = Mock.Of<IMemberTypeService>();
|
||||
var mockedDataTypeService = Mock.Of<IDataTypeService>();
|
||||
var mockedContentTypeService = Mock.Of<IContentTypeService>();
|
||||
|
||||
var mockedMigrationService = new Mock<IMigrationEntryService>();
|
||||
//set it up to return anything so that the app ctx is 'Configured'
|
||||
@@ -57,6 +61,10 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
|
||||
contentService: mockedContentService,
|
||||
mediaService: mockedMediaService,
|
||||
entityService: mockedEntityService,
|
||||
memberService: mockedMemberService,
|
||||
memberTypeService: mockedMemberTypeService,
|
||||
dataTypeService: mockedDataTypeService,
|
||||
contentTypeService: mockedContentTypeService,
|
||||
migrationEntryService: mockedMigrationService.Object,
|
||||
localizedTextService:Mock.Of<ILocalizedTextService>(),
|
||||
sectionService:Mock.Of<ISectionService>());
|
||||
@@ -156,4 +164,4 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
|
||||
|
||||
protected abstract ApiController CreateController(Type controllerType, HttpRequestMessage msg, UmbracoHelper helper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 440 KiB After Width: | Height: | Size: 386 KiB |
+1
@@ -25,6 +25,7 @@ The tour object consist of two parts - The overall tour configuration and a list
|
||||
"group": "My Custom Group" // Used to group tours in the help drawer
|
||||
"groupOrder": 200 // Control the order of tour groups
|
||||
"allowDisable": // Adds a "Don't" show this tour again"-button to the intro step
|
||||
"requiredSections":["content", "media", "mySection"] // Sections that the tour will access while running, if the user does not have access to the required tour sections, the tour will not load.
|
||||
"steps": [] // tour steps - see next example
|
||||
}
|
||||
</pre>
|
||||
|
||||
+2
-1
@@ -489,7 +489,7 @@
|
||||
|
||||
scope.editPropertyTypeSettings = function(property, group) {
|
||||
|
||||
if (!property.inherited && !property.locked) {
|
||||
if (!property.inherited) {
|
||||
|
||||
scope.propertySettingsDialogModel = {};
|
||||
scope.propertySettingsDialogModel.title = "Property settings";
|
||||
@@ -547,6 +547,7 @@
|
||||
property.validation.pattern = oldModel.property.validation.pattern;
|
||||
property.showOnMemberProfile = oldModel.property.showOnMemberProfile;
|
||||
property.memberCanEdit = oldModel.property.memberCanEdit;
|
||||
property.isSensitiveValue = oldModel.property.isSensitiveValue;
|
||||
|
||||
// because we set state to active, to show a preview, we have to check if has been filled out
|
||||
// label is required so if it is not filled we know it is a placeholder
|
||||
|
||||
@@ -212,6 +212,9 @@
|
||||
throw "Tour " + tour.alias + " is missing tour steps";
|
||||
}
|
||||
|
||||
if (tour.requiredSections.length === 0) {
|
||||
throw "Tour " + tour.alias + " is missing the required sections";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,4 +278,4 @@
|
||||
|
||||
angular.module("umbraco.services").factory("tourService", tourService);
|
||||
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
});
|
||||
|
||||
var saveProperties = _.map(realProperties, function (p) {
|
||||
var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId', 'memberCanEdit', 'showOnMemberProfile');
|
||||
var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId', 'memberCanEdit', 'showOnMemberProfile', 'isSensitiveData');
|
||||
return saveProperty;
|
||||
});
|
||||
|
||||
@@ -304,14 +304,14 @@
|
||||
_.each(tab.properties, function (prop) {
|
||||
|
||||
//don't include the custom generic tab properties
|
||||
if (!prop.alias.startsWith("_umb_")) {
|
||||
//don't include a property that is marked readonly
|
||||
if (!prop.alias.startsWith("_umb_") && !prop.readonly) {
|
||||
saveModel.properties.push({
|
||||
id: prop.id,
|
||||
alias: prop.alias,
|
||||
value: prop.value
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -338,4 +338,4 @@
|
||||
}
|
||||
angular.module('umbraco.services').factory('umbDataFormatter', umbDataFormatter);
|
||||
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -14,7 +14,6 @@ function SearchController($scope, searchService, $log, $location, navigationServ
|
||||
$scope.isSearching = false;
|
||||
$scope.selectedResult = -1;
|
||||
|
||||
|
||||
$scope.navigateResults = function (ev) {
|
||||
//38: up 40: down, 13: enter
|
||||
|
||||
@@ -34,24 +33,37 @@ function SearchController($scope, searchService, $log, $location, navigationServ
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var group = undefined;
|
||||
var groupNames = [];
|
||||
var groupIndex = -1;
|
||||
var itemIndex = -1;
|
||||
$scope.selectedItem = undefined;
|
||||
|
||||
|
||||
$scope.clearSearch = function () {
|
||||
$scope.searchTerm = null;
|
||||
};
|
||||
function iterateResults(up) {
|
||||
//default group
|
||||
if (!group) {
|
||||
group = $scope.groups[0];
|
||||
|
||||
for (var g in $scope.groups) {
|
||||
if ($scope.groups.hasOwnProperty(g)) {
|
||||
groupNames.push(g);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//Sorting to match the groups order
|
||||
groupNames.sort();
|
||||
|
||||
group = $scope.groups[groupNames[0]];
|
||||
groupIndex = 0;
|
||||
}
|
||||
|
||||
if (up) {
|
||||
if (itemIndex === 0) {
|
||||
if (groupIndex === 0) {
|
||||
gotoGroup($scope.groups.length - 1, true);
|
||||
gotoGroup(Object.keys($scope.groups).length - 1, true);
|
||||
} else {
|
||||
gotoGroup(groupIndex - 1, true);
|
||||
}
|
||||
@@ -62,7 +74,7 @@ function SearchController($scope, searchService, $log, $location, navigationServ
|
||||
if (itemIndex < group.results.length - 1) {
|
||||
gotoItem(itemIndex + 1);
|
||||
} else {
|
||||
if (groupIndex === $scope.groups.length - 1) {
|
||||
if (groupIndex === Object.keys($scope.groups).length - 1) {
|
||||
gotoGroup(0);
|
||||
} else {
|
||||
gotoGroup(groupIndex + 1);
|
||||
@@ -73,7 +85,7 @@ function SearchController($scope, searchService, $log, $location, navigationServ
|
||||
|
||||
function gotoGroup(index, up) {
|
||||
groupIndex = index;
|
||||
group = $scope.groups[groupIndex];
|
||||
group = $scope.groups[groupNames[groupIndex]];
|
||||
|
||||
if (up) {
|
||||
gotoItem(group.results.length - 1);
|
||||
@@ -95,6 +107,13 @@ function SearchController($scope, searchService, $log, $location, navigationServ
|
||||
$scope.hasResults = false;
|
||||
if ($scope.searchTerm) {
|
||||
if (newVal !== null && newVal !== undefined && newVal !== oldVal) {
|
||||
|
||||
//Resetting for brand new search
|
||||
group = undefined;
|
||||
groupNames = [];
|
||||
groupIndex = -1;
|
||||
itemIndex = -1;
|
||||
|
||||
$scope.isSearching = true;
|
||||
navigationService.showSearch();
|
||||
$scope.selectedItem = undefined;
|
||||
@@ -114,7 +133,7 @@ function SearchController($scope, searchService, $log, $location, navigationServ
|
||||
var filtered = {};
|
||||
_.each(result, function (value, key) {
|
||||
if (value.results.length > 0) {
|
||||
filtered[key] = value;
|
||||
filtered[key] = value;
|
||||
}
|
||||
});
|
||||
$scope.groups = filtered;
|
||||
|
||||
@@ -426,103 +426,114 @@ input.umb-group-builder__group-title-input {
|
||||
|
||||
.content-type-editor-dialog.edit-property-settings {
|
||||
|
||||
.validation-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.validation-label {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
font-size: 12px;
|
||||
color: @red;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
textarea.editor-label {
|
||||
border-color:transparent;
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 0;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
resize: none;
|
||||
line-height: 1.5em;
|
||||
padding-left: 0;
|
||||
border: none;
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: none !important;
|
||||
.validation-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-placeholder {
|
||||
border: 1px dashed @gray-8;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
line-height: 80px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
border-radius: 5px;
|
||||
color: @gray-3;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
color: @turquoise-d1;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.editor {
|
||||
margin-bottom: 10px;
|
||||
.editor-icon-wrapper {
|
||||
border: 1px solid @gray-8;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
text-align: center;
|
||||
line-height: 60px;
|
||||
border-radius: 5px;
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
.icon {
|
||||
font-size: 26px;
|
||||
}
|
||||
}
|
||||
.editor-details {
|
||||
float: left;
|
||||
margin-top: 10px;
|
||||
.editor-name {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
.editor-editor {
|
||||
display: block;
|
||||
.validation-label {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
color: @red;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
.editor-settings-icon {
|
||||
font-size: 18px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
textarea.editor-label {
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 0;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
resize: none;
|
||||
line-height: 1.5em;
|
||||
padding-left: 0;
|
||||
border: none;
|
||||
|
||||
.editor-description,
|
||||
.editor-validation-pattern {
|
||||
min-width: 100%;
|
||||
min-height: 25px;
|
||||
resize: none;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-dropdown {
|
||||
width: 100%;
|
||||
}
|
||||
.editor-placeholder {
|
||||
border: 1px dashed @gray-8;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
line-height: 80px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
border-radius: 5px;
|
||||
color: @gray-3;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
color: @turquoise-d1;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.editor {
|
||||
margin-bottom: 10px;
|
||||
|
||||
.editor-icon-wrapper {
|
||||
border: 1px solid @gray-8;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
text-align: center;
|
||||
line-height: 60px;
|
||||
border-radius: 5px;
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
|
||||
.icon {
|
||||
font-size: 26px;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-details {
|
||||
float: left;
|
||||
margin-top: 10px;
|
||||
|
||||
.editor-name {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.editor-editor {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-settings-icon {
|
||||
font-size: 18px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.editor-description,
|
||||
.editor-validation-pattern {
|
||||
min-width: 100%;
|
||||
min-height: 25px;
|
||||
resize: none;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.umb-dropdown {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
label.checkbox.no-indent {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
+157
-153
@@ -7,196 +7,200 @@
|
||||
* The controller for the content type editor property dialog
|
||||
*/
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function PropertySettingsOverlay($scope, contentTypeResource, dataTypeResource, dataTypeHelper, localizationService) {
|
||||
function PropertySettingsOverlay($scope, contentTypeResource, dataTypeResource, dataTypeHelper, localizationService, userService) {
|
||||
|
||||
var vm = this;
|
||||
var vm = this;
|
||||
|
||||
vm.showValidationPattern = false;
|
||||
vm.focusOnPatternField = false;
|
||||
vm.focusOnMandatoryField = false;
|
||||
vm.selectedValidationType = {};
|
||||
vm.validationTypes = [
|
||||
{
|
||||
"name": localizationService.localize("validation_validateAsEmail"),
|
||||
"key": "email",
|
||||
"pattern": "[a-zA-Z0-9_\.\+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-\.]+",
|
||||
"enableEditing": true
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("validation_validateAsNumber"),
|
||||
"key": "number",
|
||||
"pattern": "^[0-9]*$",
|
||||
"enableEditing": true
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("validation_validateAsUrl"),
|
||||
"key": "url",
|
||||
"pattern": "https?\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}",
|
||||
"enableEditing": true
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("validation_enterCustomValidation"),
|
||||
"key": "custom",
|
||||
"pattern": "",
|
||||
"enableEditing": true
|
||||
}
|
||||
];
|
||||
vm.showValidationPattern = false;
|
||||
vm.focusOnPatternField = false;
|
||||
vm.focusOnMandatoryField = false;
|
||||
vm.selectedValidationType = {};
|
||||
vm.validationTypes = [
|
||||
{
|
||||
"name": localizationService.localize("validation_validateAsEmail"),
|
||||
"key": "email",
|
||||
"pattern": "[a-zA-Z0-9_\.\+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-\.]+",
|
||||
"enableEditing": true
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("validation_validateAsNumber"),
|
||||
"key": "number",
|
||||
"pattern": "^[0-9]*$",
|
||||
"enableEditing": true
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("validation_validateAsUrl"),
|
||||
"key": "url",
|
||||
"pattern": "https?\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}",
|
||||
"enableEditing": true
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("validation_enterCustomValidation"),
|
||||
"key": "custom",
|
||||
"pattern": "",
|
||||
"enableEditing": true
|
||||
}
|
||||
];
|
||||
|
||||
vm.changeValidationType = changeValidationType;
|
||||
vm.changeValidationPattern = changeValidationPattern;
|
||||
vm.openEditorPickerOverlay = openEditorPickerOverlay;
|
||||
vm.openEditorSettingsOverlay = openEditorSettingsOverlay;
|
||||
vm.changeValidationType = changeValidationType;
|
||||
vm.changeValidationPattern = changeValidationPattern;
|
||||
vm.openEditorPickerOverlay = openEditorPickerOverlay;
|
||||
vm.openEditorSettingsOverlay = openEditorSettingsOverlay;
|
||||
|
||||
function activate() {
|
||||
userService.getCurrentUser().then(function(user) {
|
||||
vm.showSensitiveData = user.userGroups.indexOf("sensitiveData") != -1;
|
||||
});
|
||||
|
||||
matchValidationType();
|
||||
function activate() {
|
||||
|
||||
}
|
||||
matchValidationType();
|
||||
|
||||
function changeValidationPattern() {
|
||||
matchValidationType();
|
||||
}
|
||||
}
|
||||
|
||||
function openEditorPickerOverlay(property) {
|
||||
function changeValidationPattern() {
|
||||
matchValidationType();
|
||||
}
|
||||
|
||||
vm.focusOnMandatoryField = false;
|
||||
function openEditorPickerOverlay(property) {
|
||||
|
||||
vm.editorPickerOverlay = {};
|
||||
vm.editorPickerOverlay.property = $scope.model.property;
|
||||
vm.editorPickerOverlay.contentTypeName = $scope.model.contentTypeName;
|
||||
vm.editorPickerOverlay.view = "views/common/overlays/contenttypeeditor/editorpicker/editorpicker.html";
|
||||
vm.editorPickerOverlay.show = true;
|
||||
vm.focusOnMandatoryField = false;
|
||||
|
||||
vm.editorPickerOverlay.submit = function(model) {
|
||||
vm.editorPickerOverlay = {};
|
||||
vm.editorPickerOverlay.property = $scope.model.property;
|
||||
vm.editorPickerOverlay.contentTypeName = $scope.model.contentTypeName;
|
||||
vm.editorPickerOverlay.view = "views/common/overlays/contenttypeeditor/editorpicker/editorpicker.html";
|
||||
vm.editorPickerOverlay.show = true;
|
||||
|
||||
$scope.model.updateSameDataTypes = model.updateSameDataTypes;
|
||||
vm.editorPickerOverlay.submit = function (model) {
|
||||
|
||||
vm.focusOnMandatoryField = true;
|
||||
$scope.model.updateSameDataTypes = model.updateSameDataTypes;
|
||||
|
||||
// update property
|
||||
property.config = model.property.config;
|
||||
property.editor = model.property.editor;
|
||||
property.view = model.property.view;
|
||||
property.dataTypeId = model.property.dataTypeId;
|
||||
property.dataTypeIcon = model.property.dataTypeIcon;
|
||||
property.dataTypeName = model.property.dataTypeName;
|
||||
vm.focusOnMandatoryField = true;
|
||||
|
||||
vm.editorPickerOverlay.show = false;
|
||||
vm.editorPickerOverlay = null;
|
||||
};
|
||||
|
||||
vm.editorPickerOverlay.close = function(model) {
|
||||
vm.editorPickerOverlay.show = false;
|
||||
vm.editorPickerOverlay = null;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function openEditorSettingsOverlay(property) {
|
||||
|
||||
vm.focusOnMandatoryField = false;
|
||||
|
||||
// get data type
|
||||
dataTypeResource.getById(property.dataTypeId).then(function(dataType) {
|
||||
|
||||
vm.editorSettingsOverlay = {};
|
||||
vm.editorSettingsOverlay.title = "Editor settings";
|
||||
vm.editorSettingsOverlay.view = "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html";
|
||||
vm.editorSettingsOverlay.dataType = dataType;
|
||||
vm.editorSettingsOverlay.show = true;
|
||||
|
||||
vm.editorSettingsOverlay.submit = function(model) {
|
||||
|
||||
var preValues = dataTypeHelper.createPreValueProps(model.dataType.preValues);
|
||||
|
||||
dataTypeResource.save(model.dataType, preValues, false).then(function(newDataType) {
|
||||
|
||||
contentTypeResource.getPropertyTypeScaffold(newDataType.id).then(function(propertyType) {
|
||||
|
||||
// update editor
|
||||
property.config = propertyType.config;
|
||||
property.editor = propertyType.editor;
|
||||
property.view = propertyType.view;
|
||||
property.dataTypeId = newDataType.id;
|
||||
property.dataTypeIcon = newDataType.icon;
|
||||
property.dataTypeName = newDataType.name;
|
||||
|
||||
// set flag to update same data types
|
||||
$scope.model.updateSameDataTypes = true;
|
||||
|
||||
vm.focusOnMandatoryField = true;
|
||||
|
||||
vm.editorSettingsOverlay.show = false;
|
||||
vm.editorSettingsOverlay = null;
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
// update property
|
||||
property.config = model.property.config;
|
||||
property.editor = model.property.editor;
|
||||
property.view = model.property.view;
|
||||
property.dataTypeId = model.property.dataTypeId;
|
||||
property.dataTypeIcon = model.property.dataTypeIcon;
|
||||
property.dataTypeName = model.property.dataTypeName;
|
||||
|
||||
vm.editorPickerOverlay.show = false;
|
||||
vm.editorPickerOverlay = null;
|
||||
};
|
||||
|
||||
vm.editorSettingsOverlay.close = function(oldModel) {
|
||||
vm.editorSettingsOverlay.show = false;
|
||||
vm.editorSettingsOverlay = null;
|
||||
vm.editorPickerOverlay.close = function (model) {
|
||||
vm.editorPickerOverlay.show = false;
|
||||
vm.editorPickerOverlay = null;
|
||||
};
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
function openEditorSettingsOverlay(property) {
|
||||
|
||||
function matchValidationType() {
|
||||
vm.focusOnMandatoryField = false;
|
||||
|
||||
if($scope.model.property.validation.pattern !== null && $scope.model.property.validation.pattern !== "" && $scope.model.property.validation.pattern !== undefined) {
|
||||
// get data type
|
||||
dataTypeResource.getById(property.dataTypeId).then(function (dataType) {
|
||||
|
||||
var match = false;
|
||||
vm.editorSettingsOverlay = {};
|
||||
vm.editorSettingsOverlay.title = "Editor settings";
|
||||
vm.editorSettingsOverlay.view = "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html";
|
||||
vm.editorSettingsOverlay.dataType = dataType;
|
||||
vm.editorSettingsOverlay.show = true;
|
||||
|
||||
vm.editorSettingsOverlay.submit = function (model) {
|
||||
|
||||
var preValues = dataTypeHelper.createPreValueProps(model.dataType.preValues);
|
||||
|
||||
dataTypeResource.save(model.dataType, preValues, false).then(function (newDataType) {
|
||||
|
||||
contentTypeResource.getPropertyTypeScaffold(newDataType.id).then(function (propertyType) {
|
||||
|
||||
// update editor
|
||||
property.config = propertyType.config;
|
||||
property.editor = propertyType.editor;
|
||||
property.view = propertyType.view;
|
||||
property.dataTypeId = newDataType.id;
|
||||
property.dataTypeIcon = newDataType.icon;
|
||||
property.dataTypeName = newDataType.name;
|
||||
|
||||
// set flag to update same data types
|
||||
$scope.model.updateSameDataTypes = true;
|
||||
|
||||
vm.focusOnMandatoryField = true;
|
||||
|
||||
vm.editorSettingsOverlay.show = false;
|
||||
vm.editorSettingsOverlay = null;
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
vm.editorSettingsOverlay.close = function (oldModel) {
|
||||
vm.editorSettingsOverlay.show = false;
|
||||
vm.editorSettingsOverlay = null;
|
||||
};
|
||||
|
||||
// find and show if a match from the list has been chosen
|
||||
angular.forEach(vm.validationTypes, function(validationType, index){
|
||||
if($scope.model.property.validation.pattern === validationType.pattern) {
|
||||
vm.selectedValidationType = vm.validationTypes[index];
|
||||
vm.showValidationPattern = true;
|
||||
match = true;
|
||||
}
|
||||
});
|
||||
|
||||
// if there is no match - choose the custom validation option.
|
||||
if(!match) {
|
||||
angular.forEach(vm.validationTypes, function(validationType){
|
||||
if(validationType.key === "custom") {
|
||||
vm.selectedValidationType = validationType;
|
||||
vm.showValidationPattern = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
function matchValidationType() {
|
||||
|
||||
function changeValidationType(selectedValidationType) {
|
||||
if ($scope.model.property.validation.pattern !== null && $scope.model.property.validation.pattern !== "" && $scope.model.property.validation.pattern !== undefined) {
|
||||
|
||||
if(selectedValidationType) {
|
||||
$scope.model.property.validation.pattern = selectedValidationType.pattern;
|
||||
vm.showValidationPattern = true;
|
||||
var match = false;
|
||||
|
||||
// set focus on textarea
|
||||
if(selectedValidationType.key === "custom") {
|
||||
vm.focusOnPatternField = true;
|
||||
// find and show if a match from the list has been chosen
|
||||
angular.forEach(vm.validationTypes, function (validationType, index) {
|
||||
if ($scope.model.property.validation.pattern === validationType.pattern) {
|
||||
vm.selectedValidationType = vm.validationTypes[index];
|
||||
vm.showValidationPattern = true;
|
||||
match = true;
|
||||
}
|
||||
});
|
||||
|
||||
// if there is no match - choose the custom validation option.
|
||||
if (!match) {
|
||||
angular.forEach(vm.validationTypes, function (validationType) {
|
||||
if (validationType.key === "custom") {
|
||||
vm.selectedValidationType = validationType;
|
||||
vm.showValidationPattern = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$scope.model.property.validation.pattern = "";
|
||||
vm.showValidationPattern = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
function changeValidationType(selectedValidationType) {
|
||||
|
||||
activate();
|
||||
if (selectedValidationType) {
|
||||
$scope.model.property.validation.pattern = selectedValidationType.pattern;
|
||||
vm.showValidationPattern = true;
|
||||
|
||||
}
|
||||
// set focus on textarea
|
||||
if (selectedValidationType.key === "custom") {
|
||||
vm.focusOnPatternField = true;
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Overlay.PropertySettingsOverlay", PropertySettingsOverlay);
|
||||
} else {
|
||||
$scope.model.property.validation.pattern = "";
|
||||
vm.showValidationPattern = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
activate();
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Overlay.PropertySettingsOverlay", PropertySettingsOverlay);
|
||||
|
||||
})();
|
||||
|
||||
+19
-11
@@ -1,6 +1,6 @@
|
||||
<div class="content-type-editor-dialog edit-property-settings" ng-controller="Umbraco.Overlay.PropertySettingsOverlay as vm">
|
||||
|
||||
<div class="umb-control-group">
|
||||
<div class="umb-control-group" ng-if="!model.property.locked">
|
||||
<div class="control-group">
|
||||
<textarea class="editor-label"
|
||||
data-element="property-name"
|
||||
@@ -33,7 +33,7 @@
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<div class="editor-wrapper umb-control-group control-group" ng-model="model.property.editor" val-require-component>
|
||||
<div class="editor-wrapper umb-control-group control-group" ng-model="model.property.editor" val-require-component ng-if="!model.property.locked">
|
||||
|
||||
<a data-element="editor-add" href="" ng-if="!model.property.editor" class="editor-placeholder" hotkey="alt+shift+e" ng-click="vm.openEditorPickerOverlay(model.property)">
|
||||
<localize key="shortcuts_addEditor"></localize>
|
||||
@@ -61,7 +61,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group clearfix">
|
||||
<div class="umb-control-group clearfix" ng-if="!model.property.locked">
|
||||
|
||||
<h5><localize key="validation_validation"></localize></h5>
|
||||
|
||||
@@ -90,17 +90,25 @@
|
||||
|
||||
<div class="umb-control-group clearfix" ng-if="model.contentType === 'memberType'">
|
||||
|
||||
<h5><localize key="general_rights"></localize></h5>
|
||||
|
||||
<h5><localize key="general_options"></localize></h5>
|
||||
|
||||
<label class="checkbox no-indent">
|
||||
<input type="checkbox" ng-model="model.property.showOnMemberProfile">
|
||||
<localize key="contentTypeEditor_showOnMemberProfile"></localize>
|
||||
<input type="checkbox" ng-model="model.property.showOnMemberProfile">
|
||||
<localize key="contentTypeEditor_showOnMemberProfile"></localize>
|
||||
<small><localize key="contentTypeEditor_showOnMemberProfileDescription"></localize></small>
|
||||
</label>
|
||||
|
||||
<label class="checkbox no-indent">
|
||||
<input type="checkbox" ng-model="model.property.memberCanEdit">
|
||||
<localize key="contentTypeEditor_memberCanEdit"></localize>
|
||||
</label>
|
||||
<label class="checkbox no-indent">
|
||||
<input type="checkbox" ng-model="model.property.memberCanEdit">
|
||||
<localize key="contentTypeEditor_memberCanEdit"></localize>
|
||||
<small><localize key="contentTypeEditor_memberCanEditDescription"></localize></small>
|
||||
</label>
|
||||
|
||||
<label ng-if="vm.showSensitiveData" class="checkbox no-indent">
|
||||
<input type="checkbox" ng-model="model.property.isSensitiveData">
|
||||
<localize key="contentTypeEditor_isSensitiveData"></localize>
|
||||
<small><localize key="contentTypeEditor_isSensitiveDataDescription"></localize></small>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -221,6 +221,11 @@
|
||||
<localize key="contentTypeEditor_memberCanEdit"></localize>
|
||||
</div>
|
||||
|
||||
<div class="umb-group-builder__property-tag -white" ng-if="property.isSensitiveData">
|
||||
<i class="icon-lock umb-group-builder__property-tag-icon"></i>
|
||||
<localize key="contentTypeEditor_isSensitiveData"></localize>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="umb-group-builder__property-tags -right">
|
||||
@@ -251,7 +256,7 @@
|
||||
<!-- row tools -->
|
||||
<div class="umb-group-builder__property-actions" ng-if="!sortingMode">
|
||||
|
||||
<div ng-if="!property.inherited && !property.locked">
|
||||
<div ng-if="!property.inherited">
|
||||
|
||||
<!-- settings for property -->
|
||||
<div class="umb-group-builder__property-action" ng-click="editPropertyTypeSettings(property, tab)">
|
||||
@@ -259,7 +264,7 @@
|
||||
</div>
|
||||
|
||||
<!-- delete property -->
|
||||
<div class="umb-group-builder__property-action">
|
||||
<div ng-if="!property.locked" class="umb-group-builder__property-action">
|
||||
<i class="icon-trash" ng-click="togglePrompt(property)"></i>
|
||||
<umb-confirm-action
|
||||
ng-if="property.deletePrompt"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="umb-editor umb-readonlyvalue">
|
||||
<em class="muted">
|
||||
<localize key="content_isSensitiveValue">Hide this property value from content editors that don't have access to view sensitive information</localize>
|
||||
</em>
|
||||
</div>
|
||||
@@ -332,8 +332,8 @@
|
||||
<Name>umbraco.providers</Name>
|
||||
</ProjectReference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="Umbraco.ModelsBuilder, Version=3.0.8.100, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Umbraco.ModelsBuilder.3.0.8\lib\Umbraco.ModelsBuilder.dll</HintPath>
|
||||
<Reference Include="Umbraco.ModelsBuilder, Version=3.0.10.102, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Umbraco.ModelsBuilder.3.0.10\lib\Umbraco.ModelsBuilder.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -1028,8 +1028,10 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\"
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:7900</IISUrl>
|
||||
<DevelopmentServerPort>7790</DevelopmentServerPort>
|
||||
<IISUrl>http://localhost:7800</IISUrl>
|
||||
<DevelopmentServerPort>7800</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:7790</IISUrl>
|
||||
<IISUrl>http://localhost:7800</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>False</UseCustomServer>
|
||||
<CustomServerUrl>
|
||||
|
||||
@@ -5,6 +5,15 @@
|
||||
"group": "Getting Started",
|
||||
"groupOrder": 100,
|
||||
"allowDisable": true,
|
||||
"requiredSections": [
|
||||
"content",
|
||||
"media",
|
||||
"settings",
|
||||
"developer",
|
||||
"users",
|
||||
"member",
|
||||
"forms"
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"title": "Welcome to Umbraco - The Friendly CMS",
|
||||
@@ -25,7 +34,6 @@
|
||||
"content": "Each area in Umbraco is called a <b>Section</b>. Right now you are in the Content section, when you want to go to another section simply click on the appropriate icon in the main menu and you'll be there in no time.",
|
||||
"backdropOpacity": 0.6
|
||||
},
|
||||
|
||||
{
|
||||
"element": "#tree",
|
||||
"elementPreventClick": true,
|
||||
@@ -88,6 +96,15 @@
|
||||
"alias": "umbIntroCreateDocType",
|
||||
"group": "Getting Started",
|
||||
"groupOrder": 100,
|
||||
"requiredSections": [
|
||||
"content",
|
||||
"media",
|
||||
"settings",
|
||||
"developer",
|
||||
"users",
|
||||
"member",
|
||||
"forms"
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"title": "Create your first Document Type",
|
||||
@@ -203,6 +220,15 @@
|
||||
"alias": "umbIntroCreateContent",
|
||||
"group": "Getting Started",
|
||||
"groupOrder": 100,
|
||||
"requiredSections": [
|
||||
"content",
|
||||
"media",
|
||||
"settings",
|
||||
"developer",
|
||||
"users",
|
||||
"member",
|
||||
"forms"
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"title": "Creating your first content node",
|
||||
@@ -253,6 +279,15 @@
|
||||
"alias": "umbIntroRenderInTemplate",
|
||||
"group": "Getting Started",
|
||||
"groupOrder": 100,
|
||||
"requiredSections": [
|
||||
"content",
|
||||
"media",
|
||||
"settings",
|
||||
"developer",
|
||||
"users",
|
||||
"member",
|
||||
"forms"
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"title": "Render your content in a template",
|
||||
@@ -299,6 +334,15 @@
|
||||
"alias": "umbIntroViewHomePage",
|
||||
"group": "Getting Started",
|
||||
"groupOrder": 100,
|
||||
"requiredSections": [
|
||||
"content",
|
||||
"media",
|
||||
"settings",
|
||||
"developer",
|
||||
"users",
|
||||
"member",
|
||||
"forms"
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"title": "View your Umbraco site",
|
||||
@@ -339,6 +383,15 @@
|
||||
"alias": "umbIntroMediaSection",
|
||||
"group": "Getting Started",
|
||||
"groupOrder": 100,
|
||||
"requiredSections": [
|
||||
"content",
|
||||
"media",
|
||||
"settings",
|
||||
"developer",
|
||||
"users",
|
||||
"member",
|
||||
"forms"
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"title": "How to use the media library",
|
||||
|
||||
@@ -36,5 +36,5 @@
|
||||
<package id="SqlServerCE" version="4.0.0.1" targetFramework="net45" />
|
||||
<package id="System.Collections.Immutable" version="1.1.36" targetFramework="net45" />
|
||||
<package id="System.Reflection.Metadata" version="1.0.21" targetFramework="net45" />
|
||||
<package id="Umbraco.ModelsBuilder" version="3.0.8" targetFramework="net45" />
|
||||
<package id="Umbraco.ModelsBuilder" version="3.0.10" targetFramework="net45" />
|
||||
</packages>
|
||||
@@ -43,6 +43,7 @@
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta name="pinterest" content="nopin" />
|
||||
|
||||
<title ng-bind="$root.locationTitle">Umbraco</title>
|
||||
|
||||
|
||||
@@ -221,6 +221,7 @@
|
||||
<key alias="addTextBox">Add another text box</key>
|
||||
<key alias="removeTextBox">Remove this text box</key>
|
||||
<key alias="contentRoot">Content root</key>
|
||||
<key alias="isSensitiveValue">This value is hidden. If you need access to view this value please contact your website administrator.</key>
|
||||
</area>
|
||||
<area alias="blueprints">
|
||||
<key alias="createBlueprintFrom">Create a new Content Template from '%0%'</key>
|
||||
@@ -510,6 +511,7 @@
|
||||
<key alias="missingPropertyEditorErrorMessage">There is a configuration error with the data type used for this property, please check the data type</key>
|
||||
</area>
|
||||
<area alias="general">
|
||||
<key alias="options">Options</key>
|
||||
<key alias="about">About</key>
|
||||
<key alias="action">Action</key>
|
||||
<key alias="actions">Actions</key>
|
||||
@@ -1609,7 +1611,12 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="thisEditorUpdateSettings">using this editor will get updated with the new settings</key>
|
||||
|
||||
<key alias="memberCanEdit">Member can edit</key>
|
||||
<key alias="memberCanEditDescription">Allow this property value to be edited by the member on their profile page</key>
|
||||
<key alias="isSensitiveData">Is sensitive data</key>
|
||||
<key alias="isSensitiveDataDescription">Hide this property value from content editors that don't have access to view sensitive information</key>
|
||||
<key alias="showOnMemberProfile">Show on member profile</key>
|
||||
<key alias="showOnMemberProfileDescription">Allow this property value to be displayed on the member profile page</key>
|
||||
|
||||
<key alias="tabHasNoSortOrder">tab has no sort order</key>
|
||||
</area>
|
||||
|
||||
|
||||
@@ -227,6 +227,7 @@
|
||||
<key alias="addTextBox">Add another text box</key>
|
||||
<key alias="removeTextBox">Remove this text box</key>
|
||||
<key alias="contentRoot">Content root</key>
|
||||
<key alias="isSensitiveValue">This value is hidden. If you need access to view this value please contact your website administrator.</key>
|
||||
</area>
|
||||
<area alias="blueprints">
|
||||
<key alias="createBlueprintFrom">Create a new Content Template from '%0%'</key>
|
||||
@@ -516,6 +517,7 @@
|
||||
<key alias="missingPropertyEditorErrorMessage">There is a configuration error with the data type used for this property, please check the data type</key>
|
||||
</area>
|
||||
<area alias="general">
|
||||
<key alias="options">Options</key>
|
||||
<key alias="about">About</key>
|
||||
<key alias="action">Action</key>
|
||||
<key alias="actions">Actions</key>
|
||||
@@ -1613,7 +1615,12 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="thisEditorUpdateSettings">using this editor will get updated with the new settings</key>
|
||||
|
||||
<key alias="memberCanEdit">Member can edit</key>
|
||||
<key alias="memberCanEditDescription">Allow this property value to be edited by the member on their profile page</key>
|
||||
<key alias="isSensitiveData">Is sensitive data</key>
|
||||
<key alias="isSensitiveDataDescription">Hide this property value from content editors that don't have access to view sensitive information</key>
|
||||
<key alias="showOnMemberProfile">Show on member profile</key>
|
||||
<key alias="showOnMemberProfileDescription">Allow this property value to be displayed on the member profile page</key>
|
||||
|
||||
<key alias="tabHasNoSortOrder">tab has no sort order</key>
|
||||
</area>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Web;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
@@ -83,36 +83,28 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
public override void Refresh(string jsonPayload)
|
||||
{
|
||||
ClearCache(DeserializeFromJsonPayload(jsonPayload));
|
||||
ClearCache();
|
||||
base.Refresh(jsonPayload);
|
||||
}
|
||||
|
||||
public override void Refresh(int id)
|
||||
{
|
||||
ClearCache(FromMemberGroup(ApplicationContext.Current.Services.MemberGroupService.GetById(id)));
|
||||
ClearCache();
|
||||
base.Refresh(id);
|
||||
}
|
||||
|
||||
public override void Remove(int id)
|
||||
{
|
||||
ClearCache(FromMemberGroup(ApplicationContext.Current.Services.MemberGroupService.GetById(id)));
|
||||
ClearCache();
|
||||
base.Remove(id);
|
||||
}
|
||||
|
||||
private void ClearCache(params JsonPayload[] payloads)
|
||||
private void ClearCache()
|
||||
{
|
||||
if (payloads == null) return;
|
||||
|
||||
var memberGroupCache = ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache<IMemberGroup>();
|
||||
payloads.ForEach(payload =>
|
||||
{
|
||||
if (payload != null && memberGroupCache)
|
||||
{
|
||||
memberGroupCache.Result.ClearCacheByKeySearch(string.Format("{0}.{1}", typeof(IMemberGroup).FullName, payload.Name));
|
||||
memberGroupCache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey<IMemberGroup>(payload.Id));
|
||||
}
|
||||
});
|
||||
|
||||
// Since we cache by group name, it could be problematic when renaming to
|
||||
// previously existing names - see http://issues.umbraco.org/issue/U4-10846.
|
||||
// To work around this, just clear all the cache items
|
||||
ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.ClearCache<IMemberGroup>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace Umbraco.Web.Editors
|
||||
public IEnumerable<ContentItemDisplay> GetByIds([FromUri]int[] ids)
|
||||
{
|
||||
var foundContent = Services.ContentService.GetByIds(ids);
|
||||
return foundContent.Select(Mapper.Map<IContent, ContentItemDisplay>);
|
||||
return foundContent.Select(content => AutoMapperExtensions.MapWithUmbracoContext<IContent, ContentItemDisplay>(content, UmbracoContext));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -219,7 +219,7 @@ namespace Umbraco.Web.Editors
|
||||
Path = "-1," + Constants.System.RecycleBinContent
|
||||
};
|
||||
|
||||
TabsAndPropertiesResolver.AddListView(display, "content", Services.DataTypeService, Services.TextService);
|
||||
TabsAndPropertiesResolver<IContent>.AddListView(display, "content", Services.DataTypeService, Services.TextService);
|
||||
|
||||
return display;
|
||||
}
|
||||
@@ -232,7 +232,7 @@ namespace Umbraco.Web.Editors
|
||||
HandleContentNotFound(id);
|
||||
}
|
||||
|
||||
var content = Mapper.Map<IContent, ContentItemDisplay>(foundContent);
|
||||
var content = AutoMapperExtensions.MapWithUmbracoContext<IContent, ContentItemDisplay>(foundContent, UmbracoContext);
|
||||
|
||||
SetupBlueprint(content, foundContent);
|
||||
|
||||
@@ -270,7 +270,7 @@ namespace Umbraco.Web.Editors
|
||||
HandleContentNotFound(id);
|
||||
}
|
||||
|
||||
var content = Mapper.Map<IContent, ContentItemDisplay>(foundContent);
|
||||
var content = AutoMapperExtensions.MapWithUmbracoContext<IContent, ContentItemDisplay>(foundContent, UmbracoContext);
|
||||
return content;
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ namespace Umbraco.Web.Editors
|
||||
HandleContentNotFound(id);
|
||||
}
|
||||
|
||||
var content = Mapper.Map<IContent, ContentItemDisplay>(foundContent);
|
||||
var content = AutoMapperExtensions.MapWithUmbracoContext<IContent, ContentItemDisplay>(foundContent, UmbracoContext);
|
||||
return content;
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
var emptyContent = Services.ContentService.CreateContent("", parentId, contentType.Alias, UmbracoUser.Id);
|
||||
var mapped = Mapper.Map<IContent, ContentItemDisplay>(emptyContent);
|
||||
var mapped = AutoMapperExtensions.MapWithUmbracoContext<IContent, ContentItemDisplay>(emptyContent, UmbracoContext);
|
||||
|
||||
//remove this tab if it exists: umbContainerView
|
||||
var containerTab = mapped.Tabs.FirstOrDefault(x => x.Alias == Constants.Conventions.PropertyGroups.ListViewGroupName);
|
||||
@@ -563,7 +563,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
//ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
|
||||
// add the modelstate to the outgoing object and throw a validation message
|
||||
var forDisplay = Mapper.Map<IContent, ContentItemDisplay>(contentItem.PersistedContent);
|
||||
var forDisplay = AutoMapperExtensions.MapWithUmbracoContext<IContent, ContentItemDisplay>(contentItem.PersistedContent, UmbracoContext);
|
||||
forDisplay.Errors = ModelState.ToErrorDictionary();
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
|
||||
|
||||
@@ -605,7 +605,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
//return the updated model
|
||||
var display = Mapper.Map<IContent, ContentItemDisplay>(contentItem.PersistedContent);
|
||||
var display = AutoMapperExtensions.MapWithUmbracoContext<IContent, ContentItemDisplay>(contentItem.PersistedContent, UmbracoContext);
|
||||
|
||||
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
|
||||
HandleInvalidModelState(display);
|
||||
@@ -858,7 +858,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
var unpublishResult = Services.ContentService.WithResult().UnPublish(foundContent, Security.CurrentUser.Id);
|
||||
|
||||
var content = Mapper.Map<IContent, ContentItemDisplay>(foundContent);
|
||||
var content = AutoMapperExtensions.MapWithUmbracoContext<IContent, ContentItemDisplay>(foundContent, UmbracoContext);
|
||||
|
||||
if (unpublishResult == false)
|
||||
{
|
||||
@@ -1092,4 +1092,4 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
var emptyContent = Services.MediaService.CreateMedia("", parentId, contentType.Alias, UmbracoUser.Id);
|
||||
var mapped = Mapper.Map<IMedia, MediaItemDisplay>(emptyContent);
|
||||
var mapped = AutoMapperExtensions.MapWithUmbracoContext<IMedia, MediaItemDisplay>(emptyContent, UmbracoContext);
|
||||
|
||||
//remove this tab if it exists: umbContainerView
|
||||
var containerTab = mapped.Tabs.FirstOrDefault(x => x.Alias == Constants.Conventions.PropertyGroups.ListViewGroupName);
|
||||
@@ -115,7 +115,7 @@ namespace Umbraco.Web.Editors
|
||||
Path = "-1," + Constants.System.RecycleBinMedia
|
||||
};
|
||||
|
||||
TabsAndPropertiesResolver.AddListView(display, "media", Services.DataTypeService, Services.TextService);
|
||||
TabsAndPropertiesResolver<IMedia>.AddListView(display, "media", Services.DataTypeService, Services.TextService);
|
||||
|
||||
return display;
|
||||
}
|
||||
@@ -137,7 +137,7 @@ namespace Umbraco.Web.Editors
|
||||
//HandleContentNotFound will throw an exception
|
||||
return null;
|
||||
}
|
||||
return Mapper.Map<IMedia, MediaItemDisplay>(foundContent);
|
||||
return AutoMapperExtensions.MapWithUmbracoContext<IMedia, MediaItemDisplay>(foundContent, UmbracoContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -157,7 +157,7 @@ namespace Umbraco.Web.Editors
|
||||
//HandleContentNotFound will throw an exception
|
||||
return null;
|
||||
}
|
||||
return Mapper.Map<IMedia, MediaItemDisplay>(foundContent);
|
||||
return AutoMapperExtensions.MapWithUmbracoContext<IMedia, MediaItemDisplay>(foundContent, UmbracoContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -186,7 +186,7 @@ namespace Umbraco.Web.Editors
|
||||
public IEnumerable<MediaItemDisplay> GetByIds([FromUri]int[] ids)
|
||||
{
|
||||
var foundMedia = Services.MediaService.GetByIds(ids);
|
||||
return foundMedia.Select(Mapper.Map<IMedia, MediaItemDisplay>);
|
||||
return foundMedia.Select(media => AutoMapperExtensions.MapWithUmbracoContext<IMedia, MediaItemDisplay>(media, UmbracoContext));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -488,7 +488,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
//ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
|
||||
// add the modelstate to the outgoing object and throw validation response
|
||||
var forDisplay = Mapper.Map<IMedia, MediaItemDisplay>(contentItem.PersistedContent);
|
||||
var forDisplay = AutoMapperExtensions.MapWithUmbracoContext<IMedia, MediaItemDisplay>(contentItem.PersistedContent, UmbracoContext);
|
||||
forDisplay.Errors = ModelState.ToErrorDictionary();
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
|
||||
}
|
||||
@@ -498,7 +498,7 @@ namespace Umbraco.Web.Editors
|
||||
var saveStatus = Services.MediaService.WithResult().Save(contentItem.PersistedContent, (int)Security.CurrentUser.Id);
|
||||
|
||||
//return the updated model
|
||||
var display = Mapper.Map<IMedia, MediaItemDisplay>(contentItem.PersistedContent);
|
||||
var display = AutoMapperExtensions.MapWithUmbracoContext<IMedia, MediaItemDisplay>(contentItem.PersistedContent, UmbracoContext);
|
||||
|
||||
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
|
||||
HandleInvalidModelState(display);
|
||||
@@ -607,7 +607,7 @@ namespace Umbraco.Web.Editors
|
||||
var f = mediaService.CreateMedia(folder.Name, intParentId, Constants.Conventions.MediaTypes.Folder);
|
||||
mediaService.Save(f, Security.CurrentUser.Id);
|
||||
|
||||
return Mapper.Map<IMedia, MediaItemDisplay>(f);
|
||||
return AutoMapperExtensions.MapWithUmbracoContext<IMedia, MediaItemDisplay>(f, UmbracoContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace Umbraco.Web.Editors
|
||||
ParentId = -1
|
||||
};
|
||||
|
||||
TabsAndPropertiesResolver.AddListView(display, "member", Services.DataTypeService, Services.TextService);
|
||||
TabsAndPropertiesResolver<IMember>.AddListView(display, "member", Services.DataTypeService, Services.TextService);
|
||||
|
||||
return display;
|
||||
}
|
||||
@@ -178,7 +178,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
HandleContentNotFound(key);
|
||||
}
|
||||
return Mapper.Map<IMember, MemberDisplay>(foundMember);
|
||||
return AutoMapperExtensions.MapWithUmbracoContext<IMember, MemberDisplay>(foundMember, UmbracoContext);
|
||||
case MembershipScenario.CustomProviderWithUmbracoLink:
|
||||
|
||||
//TODO: Support editing custom properties for members with a custom membership provider here.
|
||||
@@ -238,7 +238,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
emptyContent = new Member(contentType);
|
||||
emptyContent.AdditionalData["NewPassword"] = Membership.GeneratePassword(provider.MinRequiredPasswordLength, provider.MinRequiredNonAlphanumericCharacters);
|
||||
return Mapper.Map<IMember, MemberDisplay>(emptyContent);
|
||||
return AutoMapperExtensions.MapWithUmbracoContext<IMember, MemberDisplay>(emptyContent, UmbracoContext);
|
||||
case MembershipScenario.CustomProviderWithUmbracoLink:
|
||||
//TODO: Support editing custom properties for members with a custom membership provider here.
|
||||
|
||||
@@ -247,7 +247,7 @@ namespace Umbraco.Web.Editors
|
||||
//we need to return a scaffold of a 'simple' member - basically just what a membership provider can edit
|
||||
emptyContent = MemberService.CreateGenericMembershipProviderMember("", "", "", "");
|
||||
emptyContent.AdditionalData["NewPassword"] = Membership.GeneratePassword(Membership.MinRequiredPasswordLength, Membership.MinRequiredNonAlphanumericCharacters);
|
||||
return Mapper.Map<IMember, MemberDisplay>(emptyContent);
|
||||
return AutoMapperExtensions.MapWithUmbracoContext<IMember, MemberDisplay>(emptyContent, UmbracoContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +287,7 @@ namespace Umbraco.Web.Editors
|
||||
//Unlike content/media - if there are errors for a member, we do NOT proceed to save them, we cannot so return the errors
|
||||
if (ModelState.IsValid == false)
|
||||
{
|
||||
var forDisplay = Mapper.Map<IMember, MemberDisplay>(contentItem.PersistedContent);
|
||||
var forDisplay = AutoMapperExtensions.MapWithUmbracoContext<IMember, MemberDisplay>(contentItem.PersistedContent, UmbracoContext);
|
||||
forDisplay.Errors = ModelState.ToErrorDictionary();
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
|
||||
}
|
||||
@@ -329,7 +329,7 @@ namespace Umbraco.Web.Editors
|
||||
//If we've had problems creating/updating the user with the provider then return the error
|
||||
if (ModelState.IsValid == false)
|
||||
{
|
||||
var forDisplay = Mapper.Map<IMember, MemberDisplay>(contentItem.PersistedContent);
|
||||
var forDisplay = AutoMapperExtensions.MapWithUmbracoContext<IMember, MemberDisplay>(contentItem.PersistedContent, UmbracoContext);
|
||||
forDisplay.Errors = ModelState.ToErrorDictionary();
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
|
||||
}
|
||||
@@ -367,7 +367,7 @@ namespace Umbraco.Web.Editors
|
||||
contentItem.PersistedContent.AdditionalData["GeneratedPassword"] = generatedPassword;
|
||||
|
||||
//return the updated model
|
||||
var display = Mapper.Map<IMember, MemberDisplay>(contentItem.PersistedContent);
|
||||
var display = AutoMapperExtensions.MapWithUmbracoContext<IMember, MemberDisplay>(contentItem.PersistedContent, UmbracoContext);
|
||||
|
||||
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
|
||||
HandleInvalidModelState(display);
|
||||
|
||||
@@ -131,9 +131,43 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
public MemberTypeDisplay PostSave(MemberTypeSave contentTypeSave)
|
||||
{
|
||||
//get the persisted member type
|
||||
var ctId = Convert.ToInt32(contentTypeSave.Id);
|
||||
var ct = ctId > 0 ? Services.MemberTypeService.Get(ctId) : null;
|
||||
|
||||
if (UmbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false)
|
||||
{
|
||||
//We need to validate if any properties on the contentTypeSave have had their IsSensitiveValue changed,
|
||||
//and if so, we need to check if the current user has access to sensitive values. If not, we have to return an error
|
||||
var props = contentTypeSave.Groups.SelectMany(x => x.Properties);
|
||||
if (ct != null)
|
||||
{
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var foundOnContentType = ct.PropertyTypes.FirstOrDefault(x => x.Id == prop.Id);
|
||||
if (foundOnContentType == null)
|
||||
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, "No property type with id " + prop.Id + " found on the content type"));
|
||||
if (ct.IsSensitiveProperty(foundOnContentType.Alias) && prop.IsSensitiveData == false)
|
||||
{
|
||||
//if these don't match, then we cannot continue, this user is not allowed to change this value
|
||||
throw new HttpResponseException(HttpStatusCode.Forbidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if it is new, then we can just verify if any property has sensitive data turned on which is not allowed
|
||||
if (props.Any(prop => prop.IsSensitiveData))
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.Forbidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var savedCt = PerformPostSave<IMemberType, MemberTypeDisplay, MemberTypeSave, MemberPropertyTypeBasic>(
|
||||
contentTypeSave: contentTypeSave,
|
||||
getContentType: i => Services.MemberTypeService.Get(i),
|
||||
getContentType: i => ct,
|
||||
saveContentType: type => Services.MemberTypeService.Save(type));
|
||||
|
||||
var display = Mapper.Map<MemberTypeDisplay>(savedCt);
|
||||
@@ -145,4 +179,4 @@ namespace Umbraco.Web.Editors
|
||||
return display;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
@@ -23,7 +21,7 @@ namespace Umbraco.Web.Editors
|
||||
return result;
|
||||
|
||||
var filters = TourFilterResolver.Current.Filters.ToList();
|
||||
|
||||
|
||||
//get all filters that will be applied to all tour aliases
|
||||
var aliasOnlyFilters = filters.Where(x => x.PluginName == null && x.TourFileName == null).ToList();
|
||||
|
||||
@@ -64,8 +62,28 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
}
|
||||
}
|
||||
//Get all allowed sections for the current user
|
||||
var allowedSections = UmbracoContext.Current.Security.CurrentUser.AllowedSections.ToList();
|
||||
|
||||
return result.OrderBy(x => x.FileName, StringComparer.InvariantCultureIgnoreCase);
|
||||
var toursToBeRemoved = new List<BackOfficeTourFile>();
|
||||
|
||||
//Checking to see if the user has access to the required tour sections, else we remove the tour
|
||||
foreach (var backOfficeTourFile in result)
|
||||
{
|
||||
foreach (var tour in backOfficeTourFile.Tours)
|
||||
{
|
||||
foreach (var toursRequiredSection in tour.RequiredSections)
|
||||
{
|
||||
if (allowedSections.Contains(toursRequiredSection) == false)
|
||||
{
|
||||
toursToBeRemoved.Add(backOfficeTourFile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.Except(toursToBeRemoved).OrderBy(x => x.FileName, StringComparer.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
private void TryParseTourFile(string tourFile,
|
||||
@@ -79,7 +97,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
//get the filters specific to this file
|
||||
var fileFilters = filters.Where(x => x.TourFileName != null && x.TourFileName.IsMatch(fileName)).ToList();
|
||||
|
||||
|
||||
//If there is any filter applied to match the file only (no tour alias) then ignore the file entirely
|
||||
var isFileFiltered = fileFilters.Any(x => x.TourAlias == null);
|
||||
if (isFileFiltered) return;
|
||||
@@ -117,4 +135,4 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.Serialization;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models
|
||||
{
|
||||
@@ -18,6 +19,8 @@ namespace Umbraco.Web.Models
|
||||
public int GroupOrder { get; set; }
|
||||
[DataMember(Name = "allowDisable")]
|
||||
public bool AllowDisable { get; set; }
|
||||
[DataMember(Name = "requiredSections")]
|
||||
public List<string> RequiredSections { get; set; }
|
||||
[DataMember(Name = "steps")]
|
||||
public BackOfficeTourStep[] Steps { get; set; }
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
public bool HideLabel { get; set; }
|
||||
|
||||
[DataMember(Name = "validation")]
|
||||
public PropertyTypeValidation Validation { get; set; }
|
||||
public PropertyTypeValidation Validation { get; set; }
|
||||
|
||||
[DataMember(Name = "readonly")]
|
||||
public bool Readonly { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,5 +13,8 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
|
||||
[DataMember(Name = "memberCanEdit")]
|
||||
public bool MemberCanEditProperty { get; set; }
|
||||
|
||||
[DataMember(Name = "isSensitiveData")]
|
||||
public bool IsSensitiveData { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,5 +10,8 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
|
||||
[DataMember(Name = "memberCanEdit")]
|
||||
public bool MemberCanEditProperty { get; set; }
|
||||
|
||||
[DataMember(Name = "isSensitiveData")]
|
||||
public bool IsSensitiveData { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal static class AutoMapperExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// This maps an object and passes in the current <see cref="UmbracoContext"/> so the mapping logic can use it
|
||||
/// </summary>
|
||||
/// <typeparam name="TIn"></typeparam>
|
||||
/// <typeparam name="TOut"></typeparam>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="umbCtx"></param>
|
||||
/// <returns></returns>
|
||||
public static TOut MapWithUmbracoContext<TIn, TOut>(TIn obj, UmbracoContext umbCtx)
|
||||
{
|
||||
return Mapper.Map<TIn, TOut>(obj, opt => opt.Items["UmbracoContext"] = umbCtx);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an <see cref="UmbracoContext"/> from the mapping options
|
||||
/// </summary>
|
||||
/// <param name="res"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// If an UmbracoContext is not found in the mapping options, it will try to retrieve it from the singleton
|
||||
/// </remarks>
|
||||
public static UmbracoContext GetUmbracoContext(this ResolutionContext res)
|
||||
{
|
||||
//get the context from the mapping options set during a mapping operation
|
||||
object umbCtx;
|
||||
if (res.Options.Items.TryGetValue("UmbracoContext", out umbCtx))
|
||||
{
|
||||
var umbracoContext = umbCtx as UmbracoContext;
|
||||
if (umbracoContext != null) return umbracoContext;
|
||||
}
|
||||
|
||||
//return the singleton (this could be null)
|
||||
return UmbracoContext.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,19 +35,15 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(display => display.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias))
|
||||
.ForMember(display => display.ContentTypeName, expression => expression.MapFrom(content => content.ContentType.Name))
|
||||
.ForMember(display => display.IsContainer, expression => expression.MapFrom(content => content.ContentType.IsContainer))
|
||||
.ForMember(display => display.IsChildOfListView, expression => expression.Ignore())
|
||||
.ForMember(display => display.IsChildOfListView, expression => expression.ResolveUsing(new ChildOfListViewResolver(applicationContext.Services.ContentService, applicationContext.Services.ContentTypeService)))
|
||||
.ForMember(display => display.Trashed, expression => expression.MapFrom(content => content.Trashed))
|
||||
.ForMember(display => display.PublishDate, expression => expression.MapFrom(content => GetPublishedDate(content)))
|
||||
.ForMember(display => display.TemplateAlias, expression => expression.MapFrom(content => content.Template.Alias))
|
||||
.ForMember(display => display.TemplateAlias, expression => expression.ResolveUsing<DefaultTemplateResolver>())
|
||||
.ForMember(display => display.HasPublishedVersion, expression => expression.MapFrom(content => content.HasPublishedVersion))
|
||||
.ForMember(display => display.Urls,
|
||||
expression => expression.MapFrom(content =>
|
||||
UmbracoContext.Current == null
|
||||
? new[] { "Cannot generate urls without a current Umbraco Context" }
|
||||
: content.GetContentUrls(UmbracoContext.Current)))
|
||||
.ForMember(display => display.Urls, expression => expression.ResolveUsing<ContentUrlResolver>())
|
||||
.ForMember(display => display.Properties, expression => expression.Ignore())
|
||||
.ForMember(display => display.AllowPreview, expression => expression.Ignore())
|
||||
.ForMember(display => display.TreeNodeUrl, expression => expression.Ignore())
|
||||
.ForMember(display => display.TreeNodeUrl, opt => opt.ResolveUsing(new ContentTreeNodeUrlResolver<IContent, ContentTreeController>()))
|
||||
.ForMember(display => display.Notifications, expression => expression.Ignore())
|
||||
.ForMember(display => display.Errors, expression => expression.Ignore())
|
||||
.ForMember(display => display.Alias, expression => expression.Ignore())
|
||||
@@ -56,11 +52,16 @@ namespace Umbraco.Web.Models.Mapping
|
||||
expression.MapFrom(content => content.ContentType.AllowedTemplates
|
||||
.Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false)
|
||||
.ToDictionary(t => t.Alias, t => t.Name)))
|
||||
.ForMember(display => display.Tabs, expression => expression.ResolveUsing(new TabsAndPropertiesResolver(applicationContext.Services.TextService)))
|
||||
.ForMember(display => display.Tabs, expression => expression.ResolveUsing(new TabsAndPropertiesResolver<IContent>(applicationContext.Services.TextService)))
|
||||
.ForMember(display => display.AllowedActions, expression => expression.ResolveUsing(
|
||||
new ActionButtonsResolver(new Lazy<IUserService>(() => applicationContext.Services.UserService), new Lazy<IContentService>(() => applicationContext.Services.ContentService))))
|
||||
.AfterMap((content, display) => AfterMap(content, display, applicationContext.Services.DataTypeService, applicationContext.Services.TextService,
|
||||
applicationContext.Services.ContentTypeService));
|
||||
.AfterMap((content, display) =>
|
||||
{
|
||||
if (content.ContentType.IsContainer)
|
||||
{
|
||||
TabsAndPropertiesResolver<IContent>.AddListView(display, "content", applicationContext.Services.DataTypeService, applicationContext.Services.TextService);
|
||||
}
|
||||
});
|
||||
|
||||
//FROM IContent TO ContentItemBasic<ContentPropertyBasic, IContent>
|
||||
config.CreateMap<IContent, ContentItemBasic<ContentPropertyBasic, IContent>>()
|
||||
@@ -88,42 +89,58 @@ namespace Umbraco.Web.Models.Mapping
|
||||
var date = ((Content)content).PublishedDate;
|
||||
return date == default(DateTime) ? (DateTime?)null : date;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the generic tab with custom properties for content
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="display"></param>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <param name="localizedText"></param>
|
||||
/// <param name="contentTypeService"></param>
|
||||
private static void AfterMap(IContent content, ContentItemDisplay display, IDataTypeService dataTypeService,
|
||||
ILocalizedTextService localizedText, IContentTypeService contentTypeService)
|
||||
|
||||
internal class ContentUrlResolver : IValueResolver
|
||||
{
|
||||
// map the IsChildOfListView (this is actually if it is a descendant of a list view!)
|
||||
var parent = content.Parent();
|
||||
display.IsChildOfListView = parent != null && (parent.ContentType.IsContainer || contentTypeService.HasContainerInPath(parent.Path));
|
||||
|
||||
//map the tree node url
|
||||
if (HttpContext.Current != null)
|
||||
public ResolutionResult Resolve(ResolutionResult source)
|
||||
{
|
||||
var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
|
||||
var url = urlHelper.GetUmbracoApiService<ContentTreeController>(controller => controller.GetTreeNode(display.Id.ToString(), null));
|
||||
display.TreeNodeUrl = url;
|
||||
var content = (IContent)source.Value;
|
||||
|
||||
var umbCtx = source.Context.GetUmbracoContext();
|
||||
|
||||
var urls = umbCtx == null
|
||||
? new[] {"Cannot generate urls without a current Umbraco Context"}
|
||||
: content.GetContentUrls(umbCtx);
|
||||
|
||||
return source.New(urls, typeof(string[]));
|
||||
}
|
||||
}
|
||||
|
||||
internal class DefaultTemplateResolver : ValueResolver<IContent, string>
|
||||
{
|
||||
protected override string ResolveCore(IContent source)
|
||||
{
|
||||
if (source == null || source.Template == null) return null;
|
||||
|
||||
var alias = source.Template.Alias;
|
||||
|
||||
//set default template if template isn't set
|
||||
if (string.IsNullOrEmpty(alias))
|
||||
alias = source.ContentType.DefaultTemplate == null
|
||||
? string.Empty
|
||||
: source.ContentType.DefaultTemplate.Alias;
|
||||
|
||||
return alias;
|
||||
}
|
||||
}
|
||||
|
||||
private class ChildOfListViewResolver : ValueResolver<IContent, bool>
|
||||
{
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
|
||||
public ChildOfListViewResolver(IContentService contentService, IContentTypeService contentTypeService)
|
||||
{
|
||||
_contentService = contentService;
|
||||
_contentTypeService = contentTypeService;
|
||||
}
|
||||
|
||||
//set default template if template isn't set
|
||||
if (string.IsNullOrEmpty(display.TemplateAlias))
|
||||
display.TemplateAlias = content.ContentType.DefaultTemplate == null
|
||||
? string.Empty
|
||||
: content.ContentType.DefaultTemplate.Alias;
|
||||
|
||||
if (content.ContentType.IsContainer)
|
||||
protected override bool ResolveCore(IContent source)
|
||||
{
|
||||
TabsAndPropertiesResolver.AddListView(display, "content", dataTypeService, localizedText);
|
||||
// map the IsChildOfListView (this is actually if it is a descendant of a list view!)
|
||||
var parent = _contentService.GetParent(source);
|
||||
return parent != null && (parent.ContentType.IsContainer || _contentTypeService.HasContainerInPath(parent.Path));
|
||||
}
|
||||
|
||||
TabsAndPropertiesResolver.MapGenericProperties(content, display, localizedText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -134,9 +151,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
protected override ContentTypeBasic ResolveCore(IContent source)
|
||||
{
|
||||
//TODO: This would be much nicer with the IUmbracoContextAccessor so we don't use singletons
|
||||
//If this is a web request and there's a user signed in and the
|
||||
// user has access to the settings section, we will
|
||||
//TODO: We can resolve the UmbracoContext from the IValueResolver options!
|
||||
if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null
|
||||
&& UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings)))
|
||||
{
|
||||
@@ -194,4 +209,4 @@ namespace Umbraco.Web.Models.Mapping
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ namespace Umbraco.Web.Models.Mapping
|
||||
internal class ContentPropertyBasicConverter<T> : TypeConverter<Property, T>
|
||||
where T : ContentPropertyBasic, new()
|
||||
{
|
||||
protected Lazy<IDataTypeService> DataTypeService { get; private set; }
|
||||
protected IDataTypeService DataTypeService { get; private set; }
|
||||
|
||||
public ContentPropertyBasicConverter(Lazy<IDataTypeService> dataTypeService)
|
||||
public ContentPropertyBasicConverter(IDataTypeService dataTypeService)
|
||||
{
|
||||
DataTypeService = dataTypeService;
|
||||
}
|
||||
@@ -42,7 +42,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
var result = new T
|
||||
{
|
||||
Id = property.Id,
|
||||
Value = editor.ValueEditor.ConvertDbToEditor(property, property.PropertyType, DataTypeService.Value),
|
||||
Value = editor.ValueEditor.ConvertDbToEditor(property, property.PropertyType, DataTypeService),
|
||||
Alias = property.Alias,
|
||||
PropertyEditor = editor,
|
||||
Editor = editor.Alias
|
||||
@@ -51,4 +51,4 @@ namespace Umbraco.Web.Models.Mapping
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,17 +13,19 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// </summary>
|
||||
internal class ContentPropertyDisplayConverter : ContentPropertyBasicConverter<ContentPropertyDisplay>
|
||||
{
|
||||
public ContentPropertyDisplayConverter(Lazy<IDataTypeService> dataTypeService)
|
||||
private readonly ILocalizedTextService _textService;
|
||||
|
||||
public ContentPropertyDisplayConverter(IDataTypeService dataTypeService, ILocalizedTextService textService)
|
||||
: base(dataTypeService)
|
||||
{
|
||||
|
||||
_textService = textService;
|
||||
}
|
||||
|
||||
protected override ContentPropertyDisplay ConvertCore(Property originalProp)
|
||||
{
|
||||
var display = base.ConvertCore(originalProp);
|
||||
|
||||
var dataTypeService = DataTypeService.Value;
|
||||
var dataTypeService = DataTypeService;
|
||||
var preVals = dataTypeService.GetPreValuesCollectionByDataTypeId(originalProp.PropertyType.DataTypeDefinitionId);
|
||||
|
||||
//configure the editor for display with the pre-values
|
||||
@@ -54,7 +56,11 @@ namespace Umbraco.Web.Models.Mapping
|
||||
display.View = valEditor.View;
|
||||
}
|
||||
|
||||
//Translate
|
||||
display.Label = _textService.UmbracoDictionaryTranslate(display.Label);
|
||||
display.Description = _textService.UmbracoDictionaryTranslate(display.Description);
|
||||
|
||||
return display;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// </summary>
|
||||
internal class ContentPropertyDtoConverter : ContentPropertyBasicConverter<ContentPropertyDto>
|
||||
{
|
||||
public ContentPropertyDtoConverter(Lazy<IDataTypeService> dataTypeService)
|
||||
public ContentPropertyDtoConverter(IDataTypeService dataTypeService)
|
||||
: base(dataTypeService)
|
||||
{
|
||||
}
|
||||
@@ -21,7 +21,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
var propertyDto = base.ConvertCore(originalProperty);
|
||||
|
||||
var dataTypeService = DataTypeService.Value;
|
||||
var dataTypeService = DataTypeService;
|
||||
|
||||
propertyDto.IsRequired = originalProperty.PropertyType.Mandatory;
|
||||
propertyDto.ValidationRegExp = originalProperty.PropertyType.ValidationRegExp;
|
||||
@@ -35,4 +35,4 @@ namespace Umbraco.Web.Models.Mapping
|
||||
return propertyDto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
var lazyDataTypeService = new Lazy<IDataTypeService>(() => applicationContext.Services.DataTypeService);
|
||||
|
||||
//FROM Property TO ContentPropertyBasic
|
||||
config.CreateMap<PropertyGroup, Tab<ContentPropertyDisplay>>()
|
||||
.ForMember(tab => tab.Label, expression => expression.MapFrom(@group => @group.Name))
|
||||
@@ -27,15 +25,15 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
//FROM Property TO ContentPropertyBasic
|
||||
config.CreateMap<Property, ContentPropertyBasic>()
|
||||
.ConvertUsing(new ContentPropertyBasicConverter<ContentPropertyBasic>(lazyDataTypeService));
|
||||
.ConvertUsing(new ContentPropertyBasicConverter<ContentPropertyBasic>(applicationContext.Services.DataTypeService));
|
||||
|
||||
//FROM Property TO ContentPropertyDto
|
||||
config.CreateMap<Property, ContentPropertyDto>()
|
||||
.ConvertUsing(new ContentPropertyDtoConverter(lazyDataTypeService));
|
||||
.ConvertUsing(new ContentPropertyDtoConverter(applicationContext.Services.DataTypeService));
|
||||
|
||||
//FROM Property TO ContentPropertyDisplay
|
||||
config.CreateMap<Property, ContentPropertyDisplay>()
|
||||
.ConvertUsing(new ContentPropertyDisplayConverter(lazyDataTypeService));
|
||||
.ConvertUsing(new ContentPropertyDisplayConverter(applicationContext.Services.DataTypeService, applicationContext.Services.TextService));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Web.Mvc;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Trees;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the tree node url for the content or media
|
||||
/// </summary>
|
||||
internal class ContentTreeNodeUrlResolver<TSource, TController> : IValueResolver
|
||||
where TSource : IContentBase
|
||||
where TController : ContentTreeControllerBase
|
||||
{
|
||||
|
||||
public ResolutionResult Resolve(ResolutionResult source)
|
||||
{
|
||||
return source.New(ResolveCore(source, (TSource)source.Value), typeof(string));
|
||||
}
|
||||
|
||||
private string ResolveCore(ResolutionResult res, TSource source)
|
||||
{
|
||||
var umbCtx = res.Context.GetUmbracoContext();
|
||||
//map the tree node url
|
||||
if (umbCtx != null)
|
||||
{
|
||||
var urlHelper = new UrlHelper(umbCtx.HttpContext.Request.RequestContext);
|
||||
var url = urlHelper.GetUmbracoApiService<TController>(controller => controller.GetTreeNode(source.Key.ToString("N"), null));
|
||||
return url;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, applicationContext);
|
||||
|
||||
//map the MemberCanEditProperty,MemberCanViewProperty
|
||||
//map the MemberCanEditProperty,MemberCanViewProperty,IsSensitiveData
|
||||
foreach (var propertyType in source.Groups.SelectMany(x => x.Properties))
|
||||
{
|
||||
var localCopy = propertyType;
|
||||
@@ -97,6 +97,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
dest.SetMemberCanEditProperty(localCopy.Alias, localCopy.MemberCanEditProperty);
|
||||
dest.SetMemberCanViewProperty(localCopy.Alias, localCopy.MemberCanViewProperty);
|
||||
dest.SetIsSensitiveProperty(localCopy.Alias, localCopy.IsSensitiveData);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -108,7 +109,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.MapBaseContentTypeEntityToDisplay<IMemberType, MemberTypeDisplay, MemberPropertyTypeDisplay>(applicationContext, _propertyEditorResolver)
|
||||
.AfterMap((memberType, display) =>
|
||||
{
|
||||
//map the MemberCanEditProperty,MemberCanViewProperty
|
||||
//map the MemberCanEditProperty,MemberCanViewProperty,IsSensitiveData
|
||||
foreach (var propertyType in memberType.PropertyTypes)
|
||||
{
|
||||
var localCopy = propertyType;
|
||||
@@ -117,6 +118,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
displayProp.MemberCanEditProperty = memberType.MemberCanEditProperty(localCopy.Alias);
|
||||
displayProp.MemberCanViewProperty = memberType.MemberCanViewProperty(localCopy.Alias);
|
||||
displayProp.IsSensitiveData = memberType.IsSensitiveProperty(localCopy.Alias);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -284,4 +286,4 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
var lazyDataTypeService = new Lazy<IDataTypeService>(() => applicationContext.Services.DataTypeService);
|
||||
|
||||
config.CreateMap<PropertyEditor, PropertyEditorBasic>();
|
||||
config.CreateMap<PropertyEditor, PropertyEditorBasic>();
|
||||
|
||||
//just maps the standard properties, does not map the value!
|
||||
config.CreateMap<PreValueField, PreValueFieldDisplay>()
|
||||
@@ -67,7 +65,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.DataType, content.Key)))
|
||||
.ForMember(display => display.AvailableEditors, expression => expression.ResolveUsing(new AvailablePropertyEditorsResolver(UmbracoConfig.For.UmbracoSettings().Content)))
|
||||
.ForMember(display => display.PreValues, expression => expression.ResolveUsing(
|
||||
new PreValueDisplayResolver(lazyDataTypeService)))
|
||||
new PreValueDisplayResolver(applicationContext.Services.DataTypeService)))
|
||||
.ForMember(display => display.SelectedEditor, expression => expression.MapFrom(
|
||||
definition => definition.PropertyEditorAlias.IsNullOrWhiteSpace() ? null : definition.PropertyEditorAlias))
|
||||
.ForMember(x => x.HasPrevalues, expression => expression.Ignore())
|
||||
@@ -90,7 +88,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
config.CreateMap<IDataTypeDefinition, IEnumerable<PreValueFieldDisplay>>()
|
||||
.ConvertUsing(definition =>
|
||||
{
|
||||
var resolver = new PreValueDisplayResolver(lazyDataTypeService);
|
||||
var resolver = new PreValueDisplayResolver(applicationContext.Services.DataTypeService);
|
||||
return resolver.Convert(definition);
|
||||
});
|
||||
|
||||
@@ -124,4 +122,4 @@ namespace Umbraco.Web.Models.Mapping
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(display => display.Owner, expression => expression.ResolveUsing(new OwnerResolver<IMedia>()))
|
||||
.ForMember(display => display.Icon, expression => expression.MapFrom(content => content.ContentType.Icon))
|
||||
.ForMember(display => display.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias))
|
||||
.ForMember(display => display.IsChildOfListView, expression => expression.Ignore())
|
||||
.ForMember(display => display.IsChildOfListView, expression => expression.ResolveUsing(new ChildOfListViewResolver(applicationContext.Services.MediaService, applicationContext.Services.ContentTypeService)))
|
||||
.ForMember(display => display.Trashed, expression => expression.MapFrom(content => content.Trashed))
|
||||
.ForMember(display => display.ContentTypeName, expression => expression.MapFrom(content => content.ContentType.Name))
|
||||
.ForMember(display => display.Properties, expression => expression.Ignore())
|
||||
.ForMember(display => display.TreeNodeUrl, expression => expression.Ignore())
|
||||
.ForMember(display => display.TreeNodeUrl, opt => opt.ResolveUsing(new ContentTreeNodeUrlResolver<IMedia, MediaTreeController>()))
|
||||
.ForMember(display => display.Notifications, expression => expression.Ignore())
|
||||
.ForMember(display => display.Errors, expression => expression.Ignore())
|
||||
.ForMember(display => display.Published, expression => expression.Ignore())
|
||||
@@ -41,11 +41,17 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(display => display.Alias, expression => expression.Ignore())
|
||||
.ForMember(display => display.IsContainer, expression => expression.Ignore())
|
||||
.ForMember(display => display.HasPublishedVersion, expression => expression.Ignore())
|
||||
.ForMember(display => display.Tabs, expression => expression.ResolveUsing(new TabsAndPropertiesResolver(applicationContext.Services.TextService)))
|
||||
.ForMember(display => display.ContentType, expression => expression.ResolveUsing<MediaTypeBasicResolver>())
|
||||
.ForMember(display => display.MediaLink, expression => expression.ResolveUsing(
|
||||
.ForMember(display => display.Tabs, expression => expression.ResolveUsing(new TabsAndPropertiesResolver<IMedia>(applicationContext.Services.TextService)))
|
||||
.ForMember(display => display.ContentType, expression => expression.ResolveUsing<MediaTypeBasicResolver>())
|
||||
.ForMember(display => display.MediaLink, expression => expression.ResolveUsing(
|
||||
content => string.Join(",", content.GetUrls(UmbracoConfig.For.UmbracoSettings().Content, applicationContext.ProfilingLogger.Logger))))
|
||||
.AfterMap((media, display) => AfterMap(media, display, applicationContext.Services.DataTypeService, applicationContext.Services.TextService, applicationContext.Services.ContentTypeService, applicationContext.ProfilingLogger.Logger));
|
||||
.AfterMap((media, display) =>
|
||||
{
|
||||
if (media.ContentType.IsContainer)
|
||||
{
|
||||
TabsAndPropertiesResolver<IMedia>.AddListView(display, "media", applicationContext.Services.DataTypeService, applicationContext.Services.TextService);
|
||||
}
|
||||
});
|
||||
|
||||
//FROM IMedia TO ContentItemBasic<ContentPropertyBasic, IMedia>
|
||||
config.CreateMap<IMedia, ContentItemBasic<ContentPropertyBasic, IMedia>>()
|
||||
@@ -68,31 +74,28 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(dto => dto.Icon, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.Alias, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore());
|
||||
}
|
||||
}
|
||||
|
||||
private static void AfterMap(IMedia media, MediaItemDisplay display, IDataTypeService dataTypeService, ILocalizedTextService localizedText, IContentTypeService contentTypeService, ILogger logger)
|
||||
private class ChildOfListViewResolver : ValueResolver<IMedia, bool>
|
||||
{
|
||||
// Adapted from ContentModelMapper
|
||||
//map the IsChildOfListView (this is actually if it is a descendant of a list view!)
|
||||
var parent = media.Parent();
|
||||
display.IsChildOfListView = parent != null && (parent.ContentType.IsContainer || contentTypeService.HasContainerInPath(parent.Path));
|
||||
private readonly IMediaService _mediaService;
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
|
||||
//map the tree node url
|
||||
if (HttpContext.Current != null)
|
||||
public ChildOfListViewResolver(IMediaService mediaService, IContentTypeService contentTypeService)
|
||||
{
|
||||
var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
|
||||
var url = urlHelper.GetUmbracoApiService<MediaTreeController>(controller => controller.GetTreeNode(display.Id.ToString(), null));
|
||||
display.TreeNodeUrl = url;
|
||||
_mediaService = mediaService;
|
||||
_contentTypeService = contentTypeService;
|
||||
}
|
||||
|
||||
if (media.ContentType.IsContainer)
|
||||
protected override bool ResolveCore(IMedia source)
|
||||
{
|
||||
TabsAndPropertiesResolver.AddListView(display, "media", dataTypeService, localizedText);
|
||||
// map the IsChildOfListView (this is actually if it is a descendant of a list view!)
|
||||
var parent = _mediaService.GetParent(source);
|
||||
return parent != null && (parent.ContentType.IsContainer || _contentTypeService.HasContainerInPath(parent.Path));
|
||||
}
|
||||
|
||||
TabsAndPropertiesResolver.MapGenericProperties(media, display, localizedText);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resolves a <see cref="ContentTypeBasic"/> from the <see cref="IContent"/> item and checks if the current user
|
||||
/// has access to see this data
|
||||
@@ -101,9 +104,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
protected override ContentTypeBasic ResolveCore(IMedia source)
|
||||
{
|
||||
//TODO: This would be much nicer with the IUmbracoContextAccessor so we don't use singletons
|
||||
//If this is a web request and there's a user signed in and the
|
||||
// user has access to the settings section, we will
|
||||
//TODO: We can resolve the UmbracoContext from the IValueResolver options!
|
||||
if (HttpContext.Current != null && UmbracoContext.Current != null &&
|
||||
UmbracoContext.Current.Security.CurrentUser != null
|
||||
&& UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using System.Web.Security;
|
||||
using AutoMapper;
|
||||
@@ -14,7 +13,6 @@ using Umbraco.Web.Models.ContentEditing;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.Trees;
|
||||
using UserProfile = Umbraco.Web.Models.ContentEditing.UserProfile;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
@@ -27,12 +25,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
//FROM MembershipUser TO MediaItemDisplay - used when using a non-umbraco membership provider
|
||||
config.CreateMap<MembershipUser, MemberDisplay>()
|
||||
.ConvertUsing(user =>
|
||||
{
|
||||
var member = Mapper.Map<MembershipUser, IMember>(user);
|
||||
return Mapper.Map<IMember, MemberDisplay>(member);
|
||||
});
|
||||
config.CreateMap<MembershipUser, MemberDisplay>().ConvertUsing<MembershipUserTypeConverter>();
|
||||
|
||||
//FROM MembershipUser TO IMember - used when using a non-umbraco membership provider
|
||||
config.CreateMap<MembershipUser, IMember>()
|
||||
@@ -56,9 +49,9 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(member => member.SortOrder, expression => expression.Ignore())
|
||||
.ForMember(member => member.AdditionalData, expression => expression.Ignore())
|
||||
.ForMember(member => member.FailedPasswordAttempts, expression => expression.Ignore())
|
||||
.ForMember(member => member.DeletedDate, expression => expression.Ignore())
|
||||
//TODO: Support these eventually
|
||||
.ForMember(member => member.PasswordQuestion, expression => expression.Ignore())
|
||||
.ForMember(member => member.DeletedDate, expression => expression.Ignore())
|
||||
//TODO: Support these eventually
|
||||
.ForMember(member => member.PasswordQuestion, expression => expression.Ignore())
|
||||
.ForMember(member => member.RawPasswordAnswerValue, expression => expression.Ignore());
|
||||
|
||||
//FROM IMember TO MediaItemDisplay
|
||||
@@ -69,10 +62,10 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(display => display.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias))
|
||||
.ForMember(display => display.ContentTypeName, expression => expression.MapFrom(content => content.ContentType.Name))
|
||||
.ForMember(display => display.Properties, expression => expression.Ignore())
|
||||
.ForMember(display => display.Tabs, expression => expression.ResolveUsing(new MemberTabsAndPropertiesResolver(applicationContext.Services.TextService)))
|
||||
.ForMember(display => display.Tabs, expression => expression.ResolveUsing(new MemberTabsAndPropertiesResolver(applicationContext.Services.TextService, applicationContext.Services.MemberService, applicationContext.Services.UserService)))
|
||||
.ForMember(display => display.MemberProviderFieldMapping, expression => expression.ResolveUsing(new MemberProviderFieldMappingResolver()))
|
||||
.ForMember(display => display.MembershipScenario,
|
||||
expression => expression.ResolveUsing(new MembershipScenarioMappingResolver(new Lazy<IMemberTypeService>(() => applicationContext.Services.MemberTypeService))))
|
||||
expression => expression.ResolveUsing(new MembershipScenarioMappingResolver(applicationContext.Services.MemberTypeService)))
|
||||
.ForMember(display => display.Notifications, expression => expression.Ignore())
|
||||
.ForMember(display => display.Errors, expression => expression.Ignore())
|
||||
.ForMember(display => display.Published, expression => expression.Ignore())
|
||||
@@ -81,9 +74,9 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(display => display.IsChildOfListView, expression => expression.Ignore())
|
||||
.ForMember(display => display.Trashed, expression => expression.Ignore())
|
||||
.ForMember(display => display.IsContainer, expression => expression.Ignore())
|
||||
.ForMember(display => display.TreeNodeUrl, expression => expression.Ignore())
|
||||
.ForMember(display => display.HasPublishedVersion, expression => expression.Ignore())
|
||||
.AfterMap((member, display) => MapGenericCustomProperties(applicationContext.Services.MemberService, applicationContext.Services.UserService, member, display, applicationContext.Services.TextService));
|
||||
.ForMember(display => display.TreeNodeUrl, opt => opt.ResolveUsing(new MemberTreeNodeUrlResolver()))
|
||||
.ForMember(display => display.HasPublishedVersion, expression => expression.Ignore());
|
||||
//.AfterMap((member, display) => MapGenericCustomProperties(applicationContext.Services.MemberService, applicationContext.Services.UserService, member, display, applicationContext.Services.TextService));
|
||||
|
||||
//FROM IMember TO MemberBasic
|
||||
config.CreateMap<IMember, MemberBasic>()
|
||||
@@ -100,14 +93,14 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore());
|
||||
|
||||
//FROM MembershipUser TO MemberBasic
|
||||
config.CreateMap<MembershipUser, MemberBasic>()
|
||||
config.CreateMap<MembershipUser, MemberBasic>()
|
||||
//we're giving this entity an ID of 0 - we cannot really map it but it needs an id so the system knows it's not a new entity
|
||||
.ForMember(member => member.Id, expression => expression.MapFrom(user => int.MaxValue))
|
||||
.ForMember(display => display.Udi, expression => expression.Ignore())
|
||||
.ForMember(member => member.CreateDate, expression => expression.MapFrom(user => user.CreationDate))
|
||||
.ForMember(member => member.UpdateDate, expression => expression.MapFrom(user => user.LastActivityDate))
|
||||
.ForMember(member => member.Key, expression => expression.MapFrom(user => user.ProviderUserKey.TryConvertTo<Guid>().Result.ToString("N")))
|
||||
.ForMember(member => member.Owner, expression => expression.UseValue(new UserProfile {Name = "Admin", UserId = 0}))
|
||||
.ForMember(member => member.Owner, expression => expression.UseValue(new UserProfile { Name = "Admin", UserId = 0 }))
|
||||
.ForMember(member => member.Icon, expression => expression.UseValue("icon-user"))
|
||||
.ForMember(member => member.Name, expression => expression.MapFrom(user => user.UserName))
|
||||
.ForMember(member => member.Email, expression => expression.MapFrom(content => content.Email))
|
||||
@@ -137,119 +130,11 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(dto => dto.Properties, expression => expression.ResolveUsing(new MemberDtoPropertiesValueResolver()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the generic tab with custom properties for content
|
||||
/// </summary>
|
||||
/// <param name="memberService"></param>
|
||||
/// <param name="userService"></param>
|
||||
/// <param name="member"></param>
|
||||
/// <param name="display"></param>
|
||||
/// <param name="localizedText"></param>
|
||||
/// <remarks>
|
||||
/// If this is a new entity and there is an approved field then we'll set it to true by default.
|
||||
/// </remarks>
|
||||
private static void MapGenericCustomProperties(IMemberService memberService, IUserService userService, IMember member, MemberDisplay display, ILocalizedTextService localizedText)
|
||||
{
|
||||
var membersProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
//map the tree node url
|
||||
if (HttpContext.Current != null)
|
||||
{
|
||||
var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
|
||||
var url = urlHelper.GetUmbracoApiService<MemberTreeController>(controller => controller.GetTreeNode(display.Key.ToString("N"), null));
|
||||
display.TreeNodeUrl = url;
|
||||
}
|
||||
|
||||
var genericProperties = new List<ContentPropertyDisplay>
|
||||
{
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = localizedText.Localize("content/membertype"),
|
||||
Value = localizedText.UmbracoDictionaryTranslate(display.ContentTypeName),
|
||||
View = PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View
|
||||
},
|
||||
GetLoginProperty(memberService, member, display, localizedText),
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = localizedText.Localize("general/email"),
|
||||
Value = display.Email,
|
||||
View = "email",
|
||||
Validation = {Mandatory = true}
|
||||
},
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = localizedText.Localize("password"),
|
||||
//NOTE: The value here is a json value - but the only property we care about is the generatedPassword one if it exists, the newPassword exists
|
||||
// only when creating a new member and we want to have a generated password pre-filled.
|
||||
Value = new Dictionary<string, object>
|
||||
{
|
||||
{"generatedPassword", member.GetAdditionalDataValueIgnoreCase("GeneratedPassword", null)},
|
||||
{"newPassword", member.GetAdditionalDataValueIgnoreCase("NewPassword", null)},
|
||||
},
|
||||
//TODO: Hard coding this because the changepassword doesn't necessarily need to be a resolvable (real) property editor
|
||||
View = "changepassword",
|
||||
//initialize the dictionary with the configuration from the default membership provider
|
||||
Config = new Dictionary<string, object>(membersProvider.GetConfiguration(userService))
|
||||
{
|
||||
//the password change toggle will only be displayed if there is already a password assigned.
|
||||
{"hasPassword", member.RawPasswordValue.IsNullOrWhiteSpace() == false}
|
||||
}
|
||||
},
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}membergroup", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = localizedText.Localize("content/membergroup"),
|
||||
Value = GetMemberGroupValue(display.Username),
|
||||
View = "membergroups",
|
||||
Config = new Dictionary<string, object> {{"IsRequired", true}}
|
||||
}
|
||||
};
|
||||
|
||||
TabsAndPropertiesResolver.MapGenericProperties(member, display, localizedText, genericProperties, properties =>
|
||||
{
|
||||
if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null
|
||||
&& UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings)))
|
||||
{
|
||||
var memberTypeLink = string.Format("#/member/memberTypes/edit/{0}", member.ContentTypeId);
|
||||
|
||||
//Replace the doctype property
|
||||
var docTypeProperty = properties.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
|
||||
docTypeProperty.Value = new List<object>
|
||||
{
|
||||
new
|
||||
{
|
||||
linkText = member.ContentType.Name,
|
||||
url = memberTypeLink,
|
||||
target = "_self",
|
||||
icon = "icon-item-arrangement"
|
||||
}
|
||||
};
|
||||
docTypeProperty.View = "urllist";
|
||||
}
|
||||
});
|
||||
|
||||
//check if there's an approval field
|
||||
var provider = membersProvider as global::umbraco.providers.members.UmbracoMembershipProvider;
|
||||
if (member.HasIdentity == false && provider != null)
|
||||
{
|
||||
var approvedField = provider.ApprovedPropertyTypeAlias;
|
||||
var prop = display.Properties.FirstOrDefault(x => x.Alias == approvedField);
|
||||
if (prop != null)
|
||||
{
|
||||
prop.Value = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the login property display field
|
||||
/// </summary>
|
||||
/// <param name="memberService"></param>
|
||||
/// <param name="member"></param>
|
||||
/// <param name="display"></param>
|
||||
/// <param name="localizedText"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
@@ -257,13 +142,13 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// the membership provider is a custom one, we cannot allow chaning the username because MembershipProvider's do not actually natively
|
||||
/// allow that.
|
||||
/// </remarks>
|
||||
internal static ContentPropertyDisplay GetLoginProperty(IMemberService memberService, IMember member, MemberDisplay display, ILocalizedTextService localizedText)
|
||||
internal static ContentPropertyDisplay GetLoginProperty(IMemberService memberService, IMember member, ILocalizedTextService localizedText)
|
||||
{
|
||||
var prop = new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = localizedText.Localize("login"),
|
||||
Value = display.Username
|
||||
Value = member.Username
|
||||
};
|
||||
|
||||
var scenario = memberService.GetMembershipScenario();
|
||||
@@ -329,33 +214,47 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// <remarks>
|
||||
/// This also ensures that the IsLocked out property is readonly when the member is not locked out - this is because
|
||||
/// an admin cannot actually set isLockedOut = true, they can only unlock.
|
||||
///
|
||||
/// This also ensures that the IsSensitive property display value is set based on the configured IMemberType property type
|
||||
/// </remarks>
|
||||
internal class MemberTabsAndPropertiesResolver : TabsAndPropertiesResolver
|
||||
internal class MemberTabsAndPropertiesResolver : TabsAndPropertiesResolver<IMember>
|
||||
{
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public MemberTabsAndPropertiesResolver(ILocalizedTextService localizedTextService)
|
||||
public MemberTabsAndPropertiesResolver(ILocalizedTextService localizedTextService, IMemberService memberService, IUserService userService)
|
||||
: base(localizedTextService)
|
||||
{
|
||||
_localizedTextService = localizedTextService;
|
||||
_memberService = memberService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public MemberTabsAndPropertiesResolver(ILocalizedTextService localizedTextService,
|
||||
IEnumerable<string> ignoreProperties) : base(localizedTextService, ignoreProperties)
|
||||
IEnumerable<string> ignoreProperties, IMemberService memberService, IUserService userService) : base(localizedTextService, ignoreProperties)
|
||||
{
|
||||
_localizedTextService = localizedTextService;
|
||||
_memberService = memberService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
protected override IEnumerable<Tab<ContentPropertyDisplay>> ResolveCore(IContentBase content)
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to deal with custom member properties and permissions
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <returns></returns>
|
||||
protected override List<Tab<ContentPropertyDisplay>> ResolveCore(UmbracoContext umbracoContext, IMember content)
|
||||
{
|
||||
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
|
||||
IgnoreProperties = content.PropertyTypes
|
||||
.Where(x => x.HasIdentity == false)
|
||||
.Select(x => x.Alias)
|
||||
.ToArray();
|
||||
|
||||
var result = base.ResolveCore(content).ToArray();
|
||||
var result = base.ResolveCore(umbracoContext, content);
|
||||
|
||||
if (provider.IsUmbracoMembershipProvider() == false)
|
||||
{
|
||||
@@ -366,12 +265,10 @@ namespace Umbraco.Web.Models.Mapping
|
||||
isLockedOutProperty.View = "readonlyvalue";
|
||||
isLockedOutProperty.Value = _localizedTextService.Localize("general/no");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
var umbracoProvider = (IUmbracoMemberTypeMembershipProvider) provider;
|
||||
var umbracoProvider = (IUmbracoMemberTypeMembershipProvider)provider;
|
||||
|
||||
//This is kind of a hack because a developer is supposed to be allowed to set their property editor - would have been much easier
|
||||
// if we just had all of the membeship provider fields on the member table :(
|
||||
@@ -382,17 +279,139 @@ namespace Umbraco.Web.Models.Mapping
|
||||
isLockedOutProperty.View = "readonlyvalue";
|
||||
isLockedOutProperty.Value = _localizedTextService.Localize("general/no");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (umbracoContext != null && umbracoContext.Security.CurrentUser != null
|
||||
&& umbracoContext.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings)))
|
||||
{
|
||||
var memberTypeLink = string.Format("#/member/memberTypes/edit/{0}", content.ContentTypeId);
|
||||
|
||||
//Replace the doctype property
|
||||
var docTypeProperty = result.SelectMany(x => x.Properties)
|
||||
.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
|
||||
docTypeProperty.Value = new List<object>
|
||||
{
|
||||
new
|
||||
{
|
||||
linkText = content.ContentType.Name,
|
||||
url = memberTypeLink,
|
||||
target = "_self",
|
||||
icon = "icon-item-arrangement"
|
||||
}
|
||||
};
|
||||
docTypeProperty.View = "urllist";
|
||||
}
|
||||
|
||||
//check if there's an approval field
|
||||
var legacyProvider = provider as global::umbraco.providers.members.UmbracoMembershipProvider;
|
||||
if (content.HasIdentity == false && legacyProvider != null)
|
||||
{
|
||||
var approvedField = legacyProvider.ApprovedPropertyTypeAlias;
|
||||
var prop = result.SelectMany(x => x.Properties).FirstOrDefault(x => x.Alias == approvedField);
|
||||
if (prop != null)
|
||||
{
|
||||
prop.Value = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override IEnumerable<ContentPropertyDisplay> GetCustomGenericProperties(IContentBase content)
|
||||
{
|
||||
var member = (IMember) content;
|
||||
var membersProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
var genericProperties = new List<ContentPropertyDisplay>
|
||||
{
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = _localizedTextService.Localize("content/membertype"),
|
||||
//Value = localizedText.UmbracoDictionaryTranslate(display.ContentTypeName),
|
||||
Value = _localizedTextService.UmbracoDictionaryTranslate(member.ContentType.Name),
|
||||
View = PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View
|
||||
},
|
||||
GetLoginProperty(_memberService, member, _localizedTextService),
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = _localizedTextService.Localize("general/email"),
|
||||
Value = member.Email,
|
||||
View = "email",
|
||||
Validation = {Mandatory = true}
|
||||
},
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = _localizedTextService.Localize("password"),
|
||||
//NOTE: The value here is a json value - but the only property we care about is the generatedPassword one if it exists, the newPassword exists
|
||||
// only when creating a new member and we want to have a generated password pre-filled.
|
||||
Value = new Dictionary<string, object>
|
||||
{
|
||||
{"generatedPassword", member.GetAdditionalDataValueIgnoreCase("GeneratedPassword", null)},
|
||||
{"newPassword", member.GetAdditionalDataValueIgnoreCase("NewPassword", null)},
|
||||
},
|
||||
//TODO: Hard coding this because the changepassword doesn't necessarily need to be a resolvable (real) property editor
|
||||
View = "changepassword",
|
||||
//initialize the dictionary with the configuration from the default membership provider
|
||||
Config = new Dictionary<string, object>(membersProvider.GetConfiguration(_userService))
|
||||
{
|
||||
//the password change toggle will only be displayed if there is already a password assigned.
|
||||
{"hasPassword", member.RawPasswordValue.IsNullOrWhiteSpace() == false}
|
||||
}
|
||||
},
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}membergroup", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = _localizedTextService.Localize("content/membergroup"),
|
||||
Value = GetMemberGroupValue(member.Username),
|
||||
View = "membergroups",
|
||||
Config = new Dictionary<string, object> {{"IsRequired", true}}
|
||||
}
|
||||
};
|
||||
|
||||
return genericProperties;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to assign the IsSensitive property values
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="properties"></param>
|
||||
/// <returns></returns>
|
||||
protected override List<ContentPropertyDisplay> MapProperties(UmbracoContext umbracoContext, IContentBase content, List<Property> properties)
|
||||
{
|
||||
var result = base.MapProperties(umbracoContext, content, properties);
|
||||
var member = (IMember)content;
|
||||
var memberType = member.ContentType;
|
||||
|
||||
//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)
|
||||
{
|
||||
//mark this property as readonly so that it does not post any data
|
||||
prop.Readonly = true;
|
||||
//replace this editor with a sensitivevalue
|
||||
prop.View = "sensitivevalue";
|
||||
//clear the value
|
||||
prop.Value = null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
internal class MembershipScenarioMappingResolver : ValueResolver<IMember, MembershipScenario>
|
||||
{
|
||||
private readonly Lazy<IMemberTypeService> _memberTypeService;
|
||||
private readonly IMemberTypeService _memberTypeService;
|
||||
|
||||
public MembershipScenarioMappingResolver(Lazy<IMemberTypeService> memberTypeService)
|
||||
public MembershipScenarioMappingResolver(IMemberTypeService memberTypeService)
|
||||
{
|
||||
_memberTypeService = memberTypeService;
|
||||
}
|
||||
@@ -405,7 +424,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
return MembershipScenario.NativeUmbraco;
|
||||
}
|
||||
var memberType = _memberTypeService.Value.Get(Constants.Conventions.MemberTypes.DefaultAlias);
|
||||
var memberType = _memberTypeService.Get(Constants.Conventions.MemberTypes.DefaultAlias);
|
||||
return memberType != null
|
||||
? MembershipScenario.CustomProviderWithUmbracoLink
|
||||
: MembershipScenario.StandaloneCustomProvider;
|
||||
@@ -432,7 +451,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
}
|
||||
else
|
||||
{
|
||||
var umbracoProvider = (IUmbracoMemberTypeMembershipProvider) provider;
|
||||
var umbracoProvider = (IUmbracoMemberTypeMembershipProvider)provider;
|
||||
|
||||
return new Dictionary<string, string>
|
||||
{
|
||||
@@ -443,5 +462,23 @@ namespace Umbraco.Web.Models.Mapping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A converter to go from a <see cref="MembershipUser"/> to a <see cref="MemberDisplay"/>
|
||||
/// </summary>
|
||||
internal class MembershipUserTypeConverter : ITypeConverter<MembershipUser, MemberDisplay>
|
||||
{
|
||||
public MemberDisplay Convert(ResolutionContext context)
|
||||
{
|
||||
var source = (MembershipUser)context.SourceValue;
|
||||
//first convert to IMember
|
||||
var member = Mapper.Map<MembershipUser, IMember>(source);
|
||||
//then convert to MemberDisplay
|
||||
return AutoMapperExtensions.MapWithUmbracoContext<IMember, MemberDisplay>(member, context.GetUmbracoContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Web.Mvc;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Trees;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the tree node url for the IMember
|
||||
/// </summary>
|
||||
internal class MemberTreeNodeUrlResolver : IValueResolver
|
||||
{
|
||||
|
||||
public ResolutionResult Resolve(ResolutionResult source)
|
||||
{
|
||||
return source.New(ResolveCore(source, (IMember)source.Value), typeof(string));
|
||||
}
|
||||
|
||||
private string ResolveCore(ResolutionResult res, IMember source)
|
||||
{
|
||||
var umbCtx = res.Context.GetUmbracoContext();
|
||||
//map the tree node url
|
||||
if (umbCtx != null)
|
||||
{
|
||||
var urlHelper = new UrlHelper(umbCtx.HttpContext.Request.RequestContext);
|
||||
var url = urlHelper.GetUmbracoApiService<MemberTreeController>(controller => controller.GetTreeNode(source.Key.ToString("N"), null));
|
||||
return url;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,9 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class PreValueDisplayResolver : ValueResolver<IDataTypeDefinition, IEnumerable<PreValueFieldDisplay>>
|
||||
{
|
||||
private readonly Lazy<IDataTypeService> _dataTypeService;
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
|
||||
public PreValueDisplayResolver(Lazy<IDataTypeService> dataTypeService)
|
||||
public PreValueDisplayResolver(IDataTypeService dataTypeService)
|
||||
{
|
||||
_dataTypeService = dataTypeService;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
}
|
||||
|
||||
//set up the defaults
|
||||
var dataTypeService = _dataTypeService.Value;
|
||||
var dataTypeService = _dataTypeService;
|
||||
var preVals = dataTypeService.GetPreValuesCollectionByDataTypeId(source.Id);
|
||||
IDictionary<string, object> dictionaryVals = preVals.FormatAsDictionary().ToDictionary(x => x.Key, x => (object)x.Value);
|
||||
var result = Enumerable.Empty<PreValueFieldDisplay>().ToArray();
|
||||
@@ -77,4 +77,4 @@ namespace Umbraco.Web.Models.Mapping
|
||||
return Convert(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// <summary>
|
||||
/// Creates the tabs collection with properties assigned for display models
|
||||
/// </summary>
|
||||
internal class TabsAndPropertiesResolver : ValueResolver<IContentBase, IEnumerable<Tab<ContentPropertyDisplay>>>
|
||||
internal class TabsAndPropertiesResolver<TSource> : IValueResolver
|
||||
where TSource : IContentBase
|
||||
{
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
protected IEnumerable<string> IgnoreProperties { get; set; }
|
||||
@@ -36,59 +37,23 @@ namespace Umbraco.Web.Models.Mapping
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps properties on to the generic properties tab
|
||||
/// Implements the <see cref="IValueResolver"/>
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="display"></param>
|
||||
/// <param name="localizedTextService"></param>
|
||||
/// <param name="customProperties">
|
||||
/// Any additional custom properties to assign to the generic properties tab.
|
||||
/// </param>
|
||||
/// <param name="onGenericPropertiesMapped"></param>
|
||||
/// <remarks>
|
||||
/// The generic properties tab is mapped during AfterMap and is responsible for
|
||||
/// setting up the properties such as Created date, updated date, template selected, etc...
|
||||
/// </remarks>
|
||||
public static void MapGenericProperties<TPersisted>(
|
||||
TPersisted content,
|
||||
ContentItemDisplayBase<ContentPropertyDisplay, TPersisted> display,
|
||||
ILocalizedTextService localizedTextService,
|
||||
IEnumerable<ContentPropertyDisplay> customProperties = null,
|
||||
Action<List<ContentPropertyDisplay>> onGenericPropertiesMapped = null)
|
||||
where TPersisted : IContentBase
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public ResolutionResult Resolve(ResolutionResult source)
|
||||
{
|
||||
var genericProps = display.Tabs.Single(x => x.Id == 0);
|
||||
|
||||
//store the current props to append to the newly inserted ones
|
||||
var currProps = genericProps.Properties.ToArray();
|
||||
|
||||
var contentProps = new List<ContentPropertyDisplay>();
|
||||
|
||||
if (customProperties != null)
|
||||
{
|
||||
//add the custom ones
|
||||
contentProps.AddRange(customProperties);
|
||||
}
|
||||
|
||||
//now add the user props
|
||||
contentProps.AddRange(currProps);
|
||||
|
||||
//callback
|
||||
if (onGenericPropertiesMapped != null)
|
||||
{
|
||||
onGenericPropertiesMapped(contentProps);
|
||||
}
|
||||
|
||||
//re-assign
|
||||
genericProps.Properties = contentProps;
|
||||
|
||||
//Show or hide properties tab based on wether it has or not any properties
|
||||
if (genericProps.Properties.Any() == false)
|
||||
{
|
||||
display.Tabs = display.Tabs.Where(x => x.Id != 0);
|
||||
}
|
||||
if (source.Value != null && (source.Value is TSource) == false)
|
||||
throw new AutoMapperMappingException(string.Format("Value supplied is of type {0} but expected {1}.\nChange the value resolver source type, or redirect the source value supplied to the value resolver using FromMember.", new object[]
|
||||
{
|
||||
source.Value.GetType(),
|
||||
typeof (TSource)
|
||||
}));
|
||||
return source.New(
|
||||
//perform the mapping with the current umbraco context
|
||||
ResolveCore(source.Context.GetUmbracoContext(), (TSource)source.Value), typeof(List<Tab<ContentPropertyDisplay>>));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the container (listview) tab to the document
|
||||
/// </summary>
|
||||
@@ -199,7 +164,13 @@ namespace Umbraco.Web.Models.Mapping
|
||||
display.Tabs = tabs;
|
||||
}
|
||||
|
||||
protected override IEnumerable<Tab<ContentPropertyDisplay>> ResolveCore(IContentBase content)
|
||||
/// <summary>
|
||||
/// Create the list of tabs for the <see cref="IContentBase"/>
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
/// <param name="content">Source value</param>
|
||||
/// <returns>Destination</returns>
|
||||
protected virtual List<Tab<ContentPropertyDisplay>> ResolveCore(UmbracoContext umbracoContext, TSource content)
|
||||
{
|
||||
var tabs = new List<Tab<ContentPropertyDisplay>>();
|
||||
|
||||
@@ -224,10 +195,8 @@ namespace Umbraco.Web.Models.Mapping
|
||||
if (properties.Count == 0)
|
||||
continue;
|
||||
|
||||
// Sort properties so items from different compositions appear in correct order (see U4-9298). Map sorted properties.
|
||||
var mappedProperties = Mapper.Map<IEnumerable<Property>, IEnumerable<ContentPropertyDisplay>>(properties.OrderBy(prop => prop.PropertyType.SortOrder));
|
||||
|
||||
TranslateProperties(mappedProperties);
|
||||
//map the properties
|
||||
var mappedProperties = MapProperties(umbracoContext, content, properties);
|
||||
|
||||
// add the tab
|
||||
// we need to pick an identifier... there is no "right" way...
|
||||
@@ -245,12 +214,41 @@ namespace Umbraco.Web.Models.Mapping
|
||||
});
|
||||
}
|
||||
|
||||
MapGenericProperties(umbracoContext, content, tabs);
|
||||
|
||||
// activate the first tab
|
||||
tabs[0].IsActive = true;
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a collection of custom generic properties that exist on the generic properties tab
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerable<ContentPropertyDisplay> GetCustomGenericProperties(IContentBase content)
|
||||
{
|
||||
return Enumerable.Empty<ContentPropertyDisplay>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps properties on to the generic properties tab
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="tabs"></param>
|
||||
/// <remarks>
|
||||
/// 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)
|
||||
{
|
||||
// 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
|
||||
var genericproperties = Mapper.Map<IEnumerable<Property>, IEnumerable<ContentPropertyDisplay>>(noGroupProperties).ToList();
|
||||
TranslateProperties(genericproperties);
|
||||
.Where(x => IgnoreProperties.Contains(x.Alias) == false) // skip ignored
|
||||
.ToList();
|
||||
var genericproperties = MapProperties(umbracoContext, content, noGroupProperties);
|
||||
|
||||
tabs.Add(new Tab<ContentPropertyDisplay>
|
||||
{
|
||||
@@ -260,20 +258,55 @@ namespace Umbraco.Web.Models.Mapping
|
||||
Properties = genericproperties
|
||||
});
|
||||
|
||||
// activate the first tab
|
||||
tabs.First().IsActive = true;
|
||||
var genericProps = tabs.Single(x => x.Id == 0);
|
||||
|
||||
return tabs;
|
||||
}
|
||||
//store the current props to append to the newly inserted ones
|
||||
var currProps = genericProps.Properties.ToArray();
|
||||
|
||||
private void TranslateProperties(IEnumerable<ContentPropertyDisplay> properties)
|
||||
{
|
||||
// Not sure whether it's a good idea to add this to the ContentPropertyDisplay mapper
|
||||
foreach (var prop in properties)
|
||||
var contentProps = new List<ContentPropertyDisplay>();
|
||||
|
||||
var customProperties = GetCustomGenericProperties(content);
|
||||
if (customProperties != null)
|
||||
{
|
||||
prop.Label = _localizedTextService.UmbracoDictionaryTranslate(prop.Label);
|
||||
prop.Description = _localizedTextService.UmbracoDictionaryTranslate(prop.Description);
|
||||
//add the custom ones
|
||||
contentProps.AddRange(customProperties);
|
||||
}
|
||||
|
||||
//now add the user props
|
||||
contentProps.AddRange(currProps);
|
||||
|
||||
//re-assign
|
||||
genericProps.Properties = contentProps;
|
||||
|
||||
//Show or hide properties tab based on wether it has or not any properties
|
||||
if (genericProps.Properties.Any() == false)
|
||||
{
|
||||
//loop throug the tabs, remove the one with the id of zero and exit the loop
|
||||
for (var i = 0; i < tabs.Count; i++)
|
||||
{
|
||||
if (tabs[i].Id != 0) continue;
|
||||
tabs.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <returns></returns>
|
||||
protected virtual List<ContentPropertyDisplay> MapProperties(UmbracoContext umbracoContext, IContentBase content, List<Property> properties)
|
||||
{
|
||||
var result = Mapper.Map<IEnumerable<Property>, IEnumerable<ContentPropertyDisplay>>(
|
||||
// Sort properties so items from different compositions appear in correct order (see U4-9298). Map sorted properties.
|
||||
properties.OrderBy(prop => prop.PropertyType.SortOrder))
|
||||
.ToList();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ using System.Security;
|
||||
[assembly: InternalsVisibleTo("Umbraco.Forms.Core.Providers")]
|
||||
[assembly: InternalsVisibleTo("Umbraco.Forms.Web")]
|
||||
|
||||
[assembly: InternalsVisibleTo("Umbraco.Headless")]
|
||||
|
||||
//allow custom unit-testing code to access internals through custom adapters
|
||||
[assembly: InternalsVisibleTo("Umbraco.VisualStudio")] // backwards compat.
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
using Microsoft.AspNet.Identity.Owin;
|
||||
using Microsoft.Owin;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.Security.Identity;
|
||||
|
||||
namespace Umbraco.Web.Security
|
||||
{
|
||||
internal static class OwinExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Nasty little hack to get httpcontextbase from an owin context
|
||||
/// </summary>
|
||||
/// <param name="owinContext"></param>
|
||||
/// <returns></returns>
|
||||
internal static Attempt<HttpContextBase> TryGetHttpContext(this IOwinContext owinContext)
|
||||
{
|
||||
var ctx = owinContext.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
|
||||
return ctx == null ? Attempt<HttpContextBase>.Fail() : Attempt.Succeed(ctx);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -330,6 +330,9 @@
|
||||
<Compile Include="Editors\CodeFileController.cs" />
|
||||
<Compile Include="Editors\TourController.cs" />
|
||||
<Compile Include="Models\BackOfficeTourFilter.cs" />
|
||||
<Compile Include="Models\Mapping\AutoMapperExtensions.cs" />
|
||||
<Compile Include="Models\Mapping\ContentTreeNodeUrlResolver.cs" />
|
||||
<Compile Include="Models\Mapping\MemberTreeNodeUrlResolver.cs" />
|
||||
<Compile Include="TourFilterResolver.cs" />
|
||||
<Compile Include="Editors\UserEditorAuthorizationHelper.cs" />
|
||||
<Compile Include="Editors\UserGroupAuthorizationAttribute.cs" />
|
||||
|
||||
@@ -14,6 +14,7 @@ using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Web;
|
||||
|
||||
@@ -235,6 +236,14 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
return base.ValidatePropertyData(postedItem, actionContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This ensures that the internal membership property types are removed from validation before processing the validation
|
||||
/// since those properties are actually mapped to real properties of the IMember.
|
||||
/// This also validates any posted data for fields that are sensitive.
|
||||
/// </summary>
|
||||
/// <param name="postedItem"></param>
|
||||
/// <param name="actionContext"></param>
|
||||
/// <returns></returns>
|
||||
protected override bool ValidateProperties(ContentItemBasic<ContentPropertyBasic, IMember> postedItem, HttpActionContext actionContext)
|
||||
{
|
||||
var propertiesToValidate = postedItem.Properties.ToList();
|
||||
@@ -245,9 +254,41 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
propertiesToValidate.RemoveAll(property => property.Alias == remove);
|
||||
}
|
||||
|
||||
return ValidateProperties(propertiesToValidate.ToArray(), postedItem.PersistedContent.Properties.ToArray(), actionContext);
|
||||
}
|
||||
var httpCtx = actionContext.Request.TryGetHttpContext();
|
||||
if (httpCtx.Success == false)
|
||||
{
|
||||
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "No http context");
|
||||
return false;
|
||||
}
|
||||
var umbCtx = httpCtx.Result.GetUmbracoContext();
|
||||
|
||||
//if the user doesn't have access to sensitive values, then we need to validate the incoming properties to check
|
||||
//if a sensitive value is being submitted.
|
||||
if (umbCtx.Security.CurrentUser.HasAccessToSensitiveData() == false)
|
||||
{
|
||||
var sensitiveProperties = postedItem.PersistedContent.ContentType
|
||||
.PropertyTypes.Where(x => postedItem.PersistedContent.ContentType.IsSensitiveProperty(x.Alias))
|
||||
.ToList();
|
||||
|
||||
foreach (var sensitiveProperty in sensitiveProperties)
|
||||
{
|
||||
var prop = propertiesToValidate.FirstOrDefault(x => x.Alias == sensitiveProperty.Alias);
|
||||
|
||||
if (prop != null)
|
||||
{
|
||||
//this should not happen, this means that there was data posted for a sensitive property that
|
||||
//the user doesn't have access to, which means that someone is trying to hack the values.
|
||||
|
||||
var message = string.Format("property with alias: {0} cannot be posted", prop.Alias);
|
||||
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, new InvalidOperationException(message));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ValidateProperties(propertiesToValidate, postedItem.PersistedContent.Properties.ToList(), actionContext);
|
||||
}
|
||||
|
||||
internal bool ValidateUniqueLogin(MemberSave contentItem, MembershipProvider membershipProvider, HttpActionContext actionContext)
|
||||
{
|
||||
if (contentItem == null) throw new ArgumentNullException("contentItem");
|
||||
@@ -333,4 +374,4 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
/// <returns></returns>
|
||||
protected virtual bool ValidateProperties(ContentItemBasic<ContentPropertyBasic, TPersisted> postedItem, HttpActionContext actionContext)
|
||||
{
|
||||
return ValidateProperties(postedItem.Properties.ToArray(), postedItem.PersistedContent.Properties.ToArray(), actionContext);
|
||||
return ValidateProperties(postedItem.Properties.ToList(), postedItem.PersistedContent.Properties.ToList(), actionContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -84,7 +84,7 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
/// <param name="persistedProperties"></param>
|
||||
/// <param name="actionContext"></param>
|
||||
/// <returns></returns>
|
||||
protected bool ValidateProperties(ContentPropertyBasic[] postedProperties , Property[] persistedProperties, HttpActionContext actionContext)
|
||||
protected bool ValidateProperties(List<ContentPropertyBasic> postedProperties , List<Property> persistedProperties, HttpActionContext actionContext)
|
||||
{
|
||||
foreach (var p in postedProperties)
|
||||
{
|
||||
@@ -125,8 +125,12 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
continue;
|
||||
}
|
||||
|
||||
//get the posted value for this property
|
||||
var postedValue = postedItem.Properties.Single(x => x.Alias == p.Alias).Value;
|
||||
//get the posted value for this property, this may be null in cases where the property was marked as readonly which means
|
||||
//the angular app will not post that value.
|
||||
var postedProp = postedItem.Properties.FirstOrDefault(x => x.Alias == p.Alias);
|
||||
if (postedProp == null) continue;
|
||||
|
||||
var postedValue = postedProp.Value;
|
||||
|
||||
//get the pre-values for this property
|
||||
var preValues = p.PreValues;
|
||||
@@ -180,4 +184,4 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2005
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Web.UI", "Umbraco.Web.UI\Umbraco.Web.UI.csproj", "{4C4C194C-B5E4-4991-8F87-4373E24CC19F}"
|
||||
EndProject
|
||||
|
||||
Reference in New Issue
Block a user