Port v7@2aa0dfb2c5 - WIP
This commit is contained in:
@@ -3,19 +3,21 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Components;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services.Implement;
|
||||
|
||||
namespace Umbraco.Core.Auditing
|
||||
{
|
||||
public sealed class AuditEventHandler : ApplicationEventHandler
|
||||
public sealed class AuditEventsComponent : UmbracoComponentBase, IUmbracoCoreComponent
|
||||
{
|
||||
private IAuditService _auditServiceInstance;
|
||||
private IUserService _userServiceInstance;
|
||||
private IEntityService _entityServiceInstance;
|
||||
private IAuditService _auditService;
|
||||
private IUserService _userService;
|
||||
private IEntityService _entityService;
|
||||
|
||||
private IUser CurrentPerformingUser
|
||||
{
|
||||
@@ -24,13 +26,13 @@ namespace Umbraco.Core.Auditing
|
||||
var identity = Thread.CurrentPrincipal?.GetUmbracoIdentity();
|
||||
return identity == null
|
||||
? new User { Id = 0, Name = "SYSTEM", Email = "" }
|
||||
: _userServiceInstance.GetUserById(Convert.ToInt32(identity.Id));
|
||||
: _userService.GetUserById(Convert.ToInt32(identity.Id));
|
||||
}
|
||||
}
|
||||
|
||||
private IUser GetPerformingUser(int userId)
|
||||
{
|
||||
var found = userId >= 0 ? _userServiceInstance.GetUserById(userId) : null;
|
||||
var found = userId >= 0 ? _userService.GetUserById(userId) : null;
|
||||
return found ?? new User {Id = 0, Name = "SYSTEM", Email = ""};
|
||||
}
|
||||
|
||||
@@ -45,11 +47,11 @@ namespace Umbraco.Core.Auditing
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
|
||||
public void Initialize(IAuditService auditService, IUserService userService, IEntityService entityService)
|
||||
{
|
||||
_auditServiceInstance = applicationContext.Services.AuditService;
|
||||
_userServiceInstance = applicationContext.Services.UserService;
|
||||
_entityServiceInstance = applicationContext.Services.EntityService;
|
||||
_auditService = auditService;
|
||||
_userService = userService;
|
||||
_entityService = entityService;
|
||||
|
||||
//BackOfficeUserManager.AccountLocked += ;
|
||||
//BackOfficeUserManager.AccountUnlocked += ;
|
||||
@@ -63,7 +65,7 @@ namespace Umbraco.Core.Auditing
|
||||
BackOfficeUserManager.PasswordReset += OnPasswordReset;
|
||||
//BackOfficeUserManager.ResetAccessFailedCount += ;
|
||||
|
||||
UserService.SavedUserGroup2 += OnSavedUserGroupWithUsers;
|
||||
UserService.SavedUserGroup += OnSavedUserGroupWithUsers;
|
||||
|
||||
UserService.SavedUser += OnSavedUser;
|
||||
UserService.DeletedUser += OnDeletedUser;
|
||||
@@ -94,7 +96,7 @@ namespace Umbraco.Core.Auditing
|
||||
foreach (var id in args.MemberIds)
|
||||
{
|
||||
members.TryGetValue(id, out var member);
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {id} \"{member?.Name ?? "(unknown)"}\" {FormatEmail(member)}",
|
||||
"umbraco/member/roles/removed", $"roles modified, removed {roles}");
|
||||
@@ -109,7 +111,7 @@ namespace Umbraco.Core.Auditing
|
||||
foreach (var id in args.MemberIds)
|
||||
{
|
||||
members.TryGetValue(id, out var member);
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {id} \"{member?.Name ?? "(unknown)"}\" {FormatEmail(member)}",
|
||||
"umbraco/member/roles/assigned", $"roles modified, assigned {roles}");
|
||||
@@ -121,7 +123,7 @@ namespace Umbraco.Core.Auditing
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var member = exportedMemberEventArgs.Member;
|
||||
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
|
||||
"umbraco/member/exported", "exported member data");
|
||||
@@ -134,7 +136,7 @@ namespace Umbraco.Core.Auditing
|
||||
{
|
||||
var group = groupWithUser.UserGroup;
|
||||
|
||||
var dp = string.Join(", ", ((UserGroup)group).GetPreviouslyDirtyProperties());
|
||||
var dp = string.Join(", ", ((UserGroup)group).GetWereDirtyProperties());
|
||||
var sections = ((UserGroup)group).WasPropertyDirty("AllowedSections")
|
||||
? string.Join(", ", group.AllowedSections)
|
||||
: null;
|
||||
@@ -153,7 +155,7 @@ namespace Umbraco.Core.Auditing
|
||||
sb.Append($"default perms: {perms}");
|
||||
}
|
||||
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"User Group {group.Id} \"{group.Name}\" ({group.Alias})",
|
||||
"umbraco/user-group/save", $"{sb}");
|
||||
@@ -162,7 +164,7 @@ namespace Umbraco.Core.Auditing
|
||||
|
||||
foreach (var user in groupWithUser.RemovedUsers)
|
||||
{
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
user.Id, $"User \"{user.Name}\" {FormatEmail(user)}",
|
||||
"umbraco/user-group/save", $"Removed user \"{user.Name}\" {FormatEmail(user)} from group {group.Id} \"{group.Name}\" ({group.Alias})");
|
||||
@@ -170,7 +172,7 @@ namespace Umbraco.Core.Auditing
|
||||
|
||||
foreach (var user in groupWithUser.AddedUsers)
|
||||
{
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
user.Id, $"User \"{user.Name}\" {FormatEmail(user)}",
|
||||
"umbraco/user-group/save", $"Added user \"{user.Name}\" {FormatEmail(user)} to group {group.Id} \"{group.Name}\" ({group.Alias})");
|
||||
@@ -186,9 +188,9 @@ namespace Umbraco.Core.Auditing
|
||||
{
|
||||
var group = sender.GetUserGroupById(perm.UserGroupId);
|
||||
var assigned = string.Join(", ", perm.AssignedPermissions);
|
||||
var entity = _entityServiceInstance.Get(perm.EntityId);
|
||||
var entity = _entityService.Get(perm.EntityId);
|
||||
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"User Group {group.Id} \"{group.Name}\" ({group.Alias})",
|
||||
"umbraco/user-group/permissions-change", $"assigning {(string.IsNullOrWhiteSpace(assigned) ? "(nothing)" : assigned)} on id:{perm.EntityId} \"{entity.Name}\"");
|
||||
@@ -201,9 +203,9 @@ namespace Umbraco.Core.Auditing
|
||||
var members = saveEventArgs.SavedEntities;
|
||||
foreach (var member in members)
|
||||
{
|
||||
var dp = string.Join(", ", ((Member) member).GetPreviouslyDirtyProperties());
|
||||
var dp = string.Join(", ", ((Member) member).GetWereDirtyProperties());
|
||||
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
|
||||
"umbraco/member/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}");
|
||||
@@ -216,7 +218,7 @@ namespace Umbraco.Core.Auditing
|
||||
var members = deleteEventArgs.DeletedEntities;
|
||||
foreach (var member in members)
|
||||
{
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
|
||||
"umbraco/member/delete", $"delete member id:{member.Id} \"{member.Name}\" {FormatEmail(member)}");
|
||||
@@ -233,9 +235,9 @@ namespace Umbraco.Core.Auditing
|
||||
? string.Join(", ", affectedUser.Groups.Select(x => x.Alias))
|
||||
: null;
|
||||
|
||||
var dp = string.Join(", ", ((User)affectedUser).GetPreviouslyDirtyProperties());
|
||||
var dp = string.Join(", ", ((User)affectedUser).GetWereDirtyProperties());
|
||||
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}",
|
||||
"umbraco/user/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}{(groups == null ? "" : "; groups assigned: " + groups)}");
|
||||
@@ -247,7 +249,7 @@ namespace Umbraco.Core.Auditing
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var affectedUsers = deleteEventArgs.DeletedEntities;
|
||||
foreach (var affectedUser in affectedUsers)
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}",
|
||||
"umbraco/user/delete", "delete user");
|
||||
@@ -313,7 +315,7 @@ namespace Umbraco.Core.Auditing
|
||||
|
||||
private void WriteAudit(int performingId, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null)
|
||||
{
|
||||
var performingUser = _userServiceInstance.GetUserById(performingId);
|
||||
var performingUser = _userService.GetUserById(performingId);
|
||||
|
||||
var performingDetails = performingUser == null
|
||||
? $"User UNKNOWN:{performingId}"
|
||||
@@ -335,13 +337,13 @@ namespace Umbraco.Core.Auditing
|
||||
{
|
||||
if (affectedDetails == null)
|
||||
{
|
||||
var affectedUser = _userServiceInstance.GetUserById(affectedId);
|
||||
var affectedUser = _userService.GetUserById(affectedId);
|
||||
affectedDetails = affectedUser == null
|
||||
? $"User UNKNOWN:{affectedId}"
|
||||
: $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}";
|
||||
}
|
||||
|
||||
_auditServiceInstance.Write(performingId, performingDetails,
|
||||
_auditService.Write(performingId, performingDetails,
|
||||
ipAddress,
|
||||
DateTime.UtcNow,
|
||||
affectedId, affectedDetails,
|
||||
@@ -136,5 +136,11 @@ namespace Umbraco.Core.Collections
|
||||
dc.ResetDirtyProperties(rememberDirty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>Always return an empty enumerable, the list has no properties that can be dirty.</remarks>
|
||||
public IEnumerable<string> GetWereDirtyProperties()
|
||||
{
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,7 +483,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
get
|
||||
{
|
||||
var setting = ConfigurationManager.AppSettings.ContainsKey("umbracoLocalTempStorage");
|
||||
var setting = ConfigurationManager.AppSettings["umbracoLocalTempStorage"];
|
||||
if (!string.IsNullOrWhiteSpace(setting))
|
||||
return Enum<LocalTempStorage>.Parse(setting);
|
||||
|
||||
|
||||
@@ -126,8 +126,6 @@
|
||||
|
||||
public const string Languages = "languages";
|
||||
|
||||
public const string Macros = "macros";
|
||||
|
||||
/// <summary>
|
||||
/// alias for the user types tree.
|
||||
/// </summary>
|
||||
|
||||
@@ -215,6 +215,11 @@ namespace Umbraco.Core.Models.Entities
|
||||
throw new WontImplementException();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetWereDirtyProperties()
|
||||
{
|
||||
throw new WontImplementException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,6 +390,10 @@ namespace Umbraco.Core.Models.Identity
|
||||
_beingDirty.ResetDirtyProperties(rememberDirty);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<string> GetWereDirtyProperties()
|
||||
=> _beingDirty.GetWereDirtyProperties();
|
||||
|
||||
/// <summary>
|
||||
/// Disables change tracking.
|
||||
/// </summary>
|
||||
|
||||
11
src/Umbraco.Core/PropertyEditors/ColorPickerConfiguration.cs
Normal file
11
src/Umbraco.Core/PropertyEditors/ColorPickerConfiguration.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the configuration for the color picker value editor.
|
||||
/// </summary>
|
||||
public class ColorPickerConfiguration : ValueListConfiguration
|
||||
{
|
||||
[ConfigurationField("useLabel", "Include labels?", "boolean", Description = "Stores colors as a Json object containing both the color hex string and label, rather than just the hex string.")]
|
||||
public bool UseLabel { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -179,6 +180,9 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// <returns></returns>
|
||||
internal Attempt<object> TryConvertValueToCrlType(object value)
|
||||
{
|
||||
if (value is JValue)
|
||||
value = value.ToString();
|
||||
|
||||
//this is a custom check to avoid any errors, if it's a string and it's empty just make it null
|
||||
if (value is string s && string.IsNullOrWhiteSpace(s))
|
||||
value = null;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
@@ -10,15 +12,50 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
=> propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.ColorPicker);
|
||||
|
||||
public override Type GetPropertyValueType(PublishedPropertyType propertyType)
|
||||
=> typeof (string);
|
||||
=> UseLabel(propertyType) ? typeof(PickedColor) : typeof(string);
|
||||
|
||||
public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType)
|
||||
=> PropertyCacheLevel.Element;
|
||||
|
||||
public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
// make sure it's a string
|
||||
return source?.ToString() ?? string.Empty;
|
||||
var useLabel = UseLabel(propertyType);
|
||||
|
||||
if (source == null) return useLabel ? null : string.Empty;
|
||||
|
||||
var ssource = source.ToString();
|
||||
if (ssource.DetectIsJson())
|
||||
{
|
||||
try
|
||||
{
|
||||
var jo = JsonConvert.DeserializeObject<JObject>(ssource);
|
||||
if (useLabel) return new PickedColor(jo["value"].ToString(), jo["label"].ToString());
|
||||
return jo["value"].ToString();
|
||||
}
|
||||
catch { /* not json finally */ }
|
||||
}
|
||||
|
||||
if (useLabel) return new PickedColor(ssource, ssource);
|
||||
return ssource;
|
||||
}
|
||||
|
||||
private bool UseLabel(PublishedPropertyType propertyType)
|
||||
{
|
||||
return ConfigurationEditor.ConfigurationAs<ColorPickerConfiguration>(propertyType.DataType.Configuration).UseLabel;
|
||||
}
|
||||
|
||||
public class PickedColor
|
||||
{
|
||||
public PickedColor(string color, string label)
|
||||
{
|
||||
Color = color;
|
||||
Label = label;
|
||||
}
|
||||
|
||||
public string Color { get; }
|
||||
public string Label { get; }
|
||||
|
||||
public override string ToString() => Color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
var gridConfig = UmbracoConfig.For.GridConfig(
|
||||
Current.ProfilingLogger.Logger,
|
||||
Current.ApplicationCache.RuntimeCache,
|
||||
new DirectoryInfo(HttpContext.Current.Server.MapPath(SystemDirectories.AppPlugins)),
|
||||
new DirectoryInfo(HttpContext.Current.Server.MapPath(SystemDirectories.Config)),
|
||||
new DirectoryInfo(IOHelper.MapPath(SystemDirectories.AppPlugins)),
|
||||
new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Config)),
|
||||
HttpContext.Current.IsDebuggingEnabled);
|
||||
|
||||
var sections = GetArray(obj, "sections");
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType)
|
||||
=> PropertyCacheLevel.Element;
|
||||
|
||||
private static readonly string[] NewLineDelimiters = { "\r\n", "\r", "\n" };
|
||||
|
||||
public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
// data is (both in database and xml):
|
||||
@@ -52,7 +54,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
|
||||
// fall back on normal behaviour
|
||||
return values.Any() == false
|
||||
? sourceString.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)
|
||||
? sourceString.Split(NewLineDelimiters, StringSplitOptions.None)
|
||||
: values.ToArray();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the ValueList editor configuration.
|
||||
/// </summary>
|
||||
class ValueListConfiguration
|
||||
public class ValueListConfiguration
|
||||
{
|
||||
[JsonProperty("items")]
|
||||
public List<ValueListItem> Items { get; set; } = new List<ValueListItem>();
|
||||
@@ -324,7 +324,7 @@ namespace Umbraco.Core.Security
|
||||
Guid guidSession;
|
||||
if (sessionId.IsNullOrWhiteSpace() == false && Guid.TryParse(sessionId, out guidSession))
|
||||
{
|
||||
ApplicationContext.Current.Services.UserService.ClearLoginSession(guidSession);
|
||||
Current.Services.UserService.ClearLoginSession(guidSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,16 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
{
|
||||
public class BackOfficeClaimsIdentityFactory<T> : ClaimsIdentityFactory<T, int>
|
||||
where T: BackOfficeIdentityUser
|
||||
{
|
||||
private readonly ApplicationContext _appCtx;
|
||||
|
||||
[Obsolete("Use the overload specifying all dependencies instead")]
|
||||
public BackOfficeClaimsIdentityFactory()
|
||||
:this(ApplicationContext.Current)
|
||||
{
|
||||
}
|
||||
|
||||
public BackOfficeClaimsIdentityFactory(ApplicationContext appCtx)
|
||||
{
|
||||
if (appCtx == null) throw new ArgumentNullException("appCtx");
|
||||
_appCtx = appCtx;
|
||||
|
||||
SecurityStampClaimType = Constants.Security.SessionIdClaimType;
|
||||
UserNameClaimType = ClaimTypes.Name;
|
||||
}
|
||||
@@ -58,14 +44,5 @@ namespace Umbraco.Core.Security
|
||||
}
|
||||
|
||||
public class BackOfficeClaimsIdentityFactory : BackOfficeClaimsIdentityFactory<BackOfficeIdentityUser>
|
||||
{
|
||||
[Obsolete("Use the overload specifying all dependencies instead")]
|
||||
public BackOfficeClaimsIdentityFactory()
|
||||
{
|
||||
}
|
||||
|
||||
public BackOfficeClaimsIdentityFactory(ApplicationContext appCtx) : base(appCtx)
|
||||
{
|
||||
}
|
||||
}
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -1,52 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Microsoft.Owin;
|
||||
using Microsoft.Owin.Security.Cookies;
|
||||
using Semver;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
{
|
||||
public class BackOfficeCookieAuthenticationProvider : CookieAuthenticationProvider
|
||||
{
|
||||
private readonly ApplicationContext _appCtx;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
|
||||
[Obsolete("Use the ctor specifying all dependencies")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public BackOfficeCookieAuthenticationProvider()
|
||||
: this(ApplicationContext.Current)
|
||||
public BackOfficeCookieAuthenticationProvider(IUserService userService, IRuntimeState runtimeState)
|
||||
{
|
||||
_userService = userService;
|
||||
_runtimeState = runtimeState;
|
||||
}
|
||||
|
||||
public BackOfficeCookieAuthenticationProvider(ApplicationContext appCtx)
|
||||
{
|
||||
if (appCtx == null) throw new ArgumentNullException("appCtx");
|
||||
_appCtx = appCtx;
|
||||
}
|
||||
|
||||
private static readonly SemVersion MinUmbracoVersionSupportingLoginSessions = new SemVersion(7, 8);
|
||||
|
||||
public override void ResponseSignIn(CookieResponseSignInContext context)
|
||||
{
|
||||
var backOfficeIdentity = context.Identity as UmbracoBackOfficeIdentity;
|
||||
if (backOfficeIdentity != null)
|
||||
if (context.Identity is UmbracoBackOfficeIdentity backOfficeIdentity)
|
||||
{
|
||||
//generate a session id and assign it
|
||||
//create a session token - if we are configured and not in an upgrade state then use the db, otherwise just generate one
|
||||
|
||||
//NOTE - special check because when we are upgrading to 7.8 we cannot create a session since the db isn't ready and we'll get exceptions
|
||||
var canAcquireSession = _appCtx.IsUpgrading == false || _appCtx.CurrentVersion() >= MinUmbracoVersionSupportingLoginSessions;
|
||||
|
||||
var session = canAcquireSession
|
||||
? _appCtx.Services.UserService.CreateLoginSession((int)backOfficeIdentity.Id, context.OwinContext.GetCurrentRequestIpAddress())
|
||||
var session = _runtimeState.Level == RuntimeLevel.Run
|
||||
? _userService.CreateLoginSession((int)backOfficeIdentity.Id, context.OwinContext.GetCurrentRequestIpAddress())
|
||||
: Guid.NewGuid();
|
||||
|
||||
backOfficeIdentity.UserData.SessionId = session.ToString();
|
||||
@@ -58,15 +43,13 @@ namespace Umbraco.Core.Security
|
||||
public override void ResponseSignOut(CookieResponseSignOutContext context)
|
||||
{
|
||||
//Clear the user's session on sign out
|
||||
if (context != null && context.OwinContext != null && context.OwinContext.Authentication != null
|
||||
&& context.OwinContext.Authentication.User != null && context.OwinContext.Authentication.User.Identity != null)
|
||||
if (context?.OwinContext?.Authentication?.User?.Identity != null)
|
||||
{
|
||||
var claimsIdentity = context.OwinContext.Authentication.User.Identity as ClaimsIdentity;
|
||||
var sessionId = claimsIdentity.FindFirstValue(Constants.Security.SessionIdClaimType);
|
||||
Guid guidSession;
|
||||
if (sessionId.IsNullOrWhiteSpace() == false && Guid.TryParse(sessionId, out guidSession))
|
||||
if (sessionId.IsNullOrWhiteSpace() == false && Guid.TryParse(sessionId, out var guidSession))
|
||||
{
|
||||
_appCtx.Services.UserService.ClearLoginSession(guidSession);
|
||||
_userService.ClearLoginSession(guidSession);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +100,7 @@ namespace Umbraco.Core.Security
|
||||
/// </remarks>
|
||||
protected virtual async Task EnsureValidSessionId(CookieValidateIdentityContext context)
|
||||
{
|
||||
if (_appCtx.IsConfigured && _appCtx.IsUpgrading == false)
|
||||
if (_runtimeState.Level == RuntimeLevel.Run)
|
||||
await SessionIdValidator.ValidateSessionAsync(TimeSpan.FromMinutes(1), context);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ namespace Umbraco.Core.Services
|
||||
|
||||
xml.Add(new XAttribute("template", content.Template?.Id.ToString(CultureInfo.InvariantCulture) ?? "0"));
|
||||
|
||||
xml.Add(new XAttribute("isPublished", content.Published));
|
||||
|
||||
if (withDescendants)
|
||||
{
|
||||
var descendants = contentService.GetDescendants(content).ToArray();
|
||||
|
||||
@@ -1,15 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a service for handling audit.
|
||||
/// </summary>
|
||||
public interface IAuditService : IService
|
||||
{
|
||||
void Add(AuditType type, string comment, int userId, int objectId);
|
||||
IEnumerable<AuditItem> GetLogs(int objectId);
|
||||
IEnumerable<AuditItem> GetUserLogs(int userId, AuditType type, DateTime? sinceDate = null);
|
||||
IEnumerable<AuditItem> GetLogs(AuditType type, DateTime? sinceDate = null);
|
||||
|
||||
IEnumerable<IAuditItem> GetLogs(int objectId);
|
||||
IEnumerable<IAuditItem> GetUserLogs(int userId, AuditType type, DateTime? sinceDate = null);
|
||||
IEnumerable<IAuditItem> GetLogs(AuditType type, DateTime? sinceDate = null);
|
||||
void CleanLogs(int maximumAgeOfLogsInMinutes);
|
||||
|
||||
/// <summary>
|
||||
/// Returns paged items in the audit trail for a given entity
|
||||
/// </summary>
|
||||
/// <param name="entityId"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="orderDirection">
|
||||
/// By default this will always be ordered descending (newest first)
|
||||
/// </param>
|
||||
/// <param name="auditTypeFilter">
|
||||
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
|
||||
/// so we need to do that here
|
||||
/// </param>
|
||||
/// <param name="customFilter">
|
||||
/// Optional filter to be applied
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IAuditItem> GetPagedItemsByEntity(int entityId, long pageIndex, int pageSize, out long totalRecords,
|
||||
Direction orderDirection = Direction.Descending,
|
||||
AuditType[] auditTypeFilter = null,
|
||||
IQuery<IAuditItem> customFilter = null);
|
||||
|
||||
/// <summary>
|
||||
/// Returns paged items in the audit trail for a given user
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="orderDirection">
|
||||
/// By default this will always be ordered descending (newest first)
|
||||
/// </param>
|
||||
/// <param name="auditTypeFilter">
|
||||
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
|
||||
/// so we need to do that here
|
||||
/// </param>
|
||||
/// <param name="customFilter">
|
||||
/// Optional filter to be applied
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IAuditItem> GetPagedItemsByUser(int userId, long pageIndex, int pageSize, out long totalRecords,
|
||||
Direction orderDirection = Direction.Descending,
|
||||
AuditType[] auditTypeFilter = null,
|
||||
IQuery<IAuditItem> customFilter = null);
|
||||
|
||||
/// <summary>
|
||||
/// Writes an audit entry for an audited event.
|
||||
/// </summary>
|
||||
/// <param name="performingUserId">The identifier of the user triggering the audited event.</param>
|
||||
/// <param name="perfomingDetails">Free-form details about the user triggering the audited event.</param>
|
||||
/// <param name="performingIp">The IP address or the request triggering the audited event.</param>
|
||||
/// <param name="eventDateUtc">The date and time of the audited event.</param>
|
||||
/// <param name="affectedUserId">The identifier of the user affected by the audited event.</param>
|
||||
/// <param name="affectedDetails">Free-form details about the entity affected by the audited event.</param>
|
||||
/// <param name="eventType">
|
||||
/// The type of the audited event - must contain only alphanumeric chars and hyphens with forward slashes separating categories.
|
||||
/// <example>
|
||||
/// The eventType will generally be formatted like: {application}/{entity-type}/{category}/{sub-category}
|
||||
/// Example: umbraco/user/sign-in/failed
|
||||
/// </example>
|
||||
/// </param>
|
||||
/// <param name="eventDetails">Free-form details about the audited event.</param>
|
||||
IAuditEntry Write(int performingUserId, string perfomingDetails, string performingIp, DateTime eventDateUtc, int affectedUserId, string affectedDetails, string eventType, string eventDetails);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
45
src/Umbraco.Core/Services/IConsentService.cs
Normal file
45
src/Umbraco.Core/Services/IConsentService.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// A service for handling lawful data processing requirements
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Consent can be given or revoked or changed via the <see cref="RegisterConsent"/> method, which
|
||||
/// creates a new <see cref="IConsent"/> entity to track the consent. Revoking a consent is performed by
|
||||
/// registering a revoked consent.</para>
|
||||
/// <para>A consent can be revoked, by registering a revoked consent, but cannot be deleted.</para>
|
||||
/// <para>Getter methods return the current state of a consent, i.e. the latest <see cref="IConsent"/>
|
||||
/// entity that was created.</para>
|
||||
/// </remarks>
|
||||
public interface IConsentService : IService
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers consent.
|
||||
/// </summary>
|
||||
/// <param name="source">The source, i.e. whoever is consenting.</param>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="state">The state of the consent.</param>
|
||||
/// <param name="comment">Additional free text.</param>
|
||||
/// <returns>The corresponding consent entity.</returns>
|
||||
IConsent RegisterConsent(string source, string context, string action, ConsentState state, string comment = null);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves consents.
|
||||
/// </summary>
|
||||
/// <param name="source">The optional source.</param>
|
||||
/// <param name="context">The optional context.</param>
|
||||
/// <param name="action">The optional action.</param>
|
||||
/// <param name="sourceStartsWith">Determines whether <paramref name="source"/> is a start pattern.</param>
|
||||
/// <param name="contextStartsWith">Determines whether <paramref name="context"/> is a start pattern.</param>
|
||||
/// <param name="actionStartsWith">Determines whether <paramref name="action"/> is a start pattern.</param>
|
||||
/// <param name="includeHistory">Determines whether to include the history of consents.</param>
|
||||
/// <returns>Consents matching the paramters.</returns>
|
||||
IEnumerable<IConsent> LookupConsent(string source = null, string context = null, string action = null,
|
||||
bool sourceStartsWith = false, bool contextStartsWith = false, bool actionStartsWith = false,
|
||||
bool includeHistory = false);
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,16 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
IContent CreateContentFromBlueprint(IContent blueprint, string name, int userId = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes blueprints for a content type.
|
||||
/// </summary>
|
||||
void DeleteBlueprintsOfType(int contentTypeId, int userId = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes blueprints for content types.
|
||||
/// </summary>
|
||||
void DeleteBlueprintsOfTypes(IEnumerable<int> contentTypeIds, int userId = 0);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get, Count Documents
|
||||
@@ -326,6 +336,11 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
bool Sort(IEnumerable<IContent> items, int userId = 0, bool raiseEvents = true);
|
||||
|
||||
/// <summary>
|
||||
/// Sorts documents.
|
||||
/// </summary>
|
||||
bool Sort(IEnumerable<int> ids, int userId = 0, bool raiseEvents = true);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Publish Document
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Net.Http;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
@@ -12,6 +13,34 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public interface IUserService : IMembershipUserService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a database entry for starting a new login session for a user
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="requestingIpAddress"></param>
|
||||
/// <returns></returns>
|
||||
Guid CreateLoginSession(int userId, string requestingIpAddress);
|
||||
|
||||
/// <summary>
|
||||
/// Validates that a user login session is valid/current and hasn't been closed
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="sessionId"></param>
|
||||
/// <returns></returns>
|
||||
bool ValidateLoginSession(int userId, Guid sessionId);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the session's validity
|
||||
/// </summary>
|
||||
/// <param name="sessionId"></param>
|
||||
void ClearLoginSession(Guid sessionId);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all valid sessions for the user
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
int ClearLoginSessions(int userId);
|
||||
|
||||
/// <summary>
|
||||
/// This is basically facets of UserStates key = state, value = count
|
||||
/// </summary>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
@@ -37,8 +39,13 @@ namespace Umbraco.Core.Services
|
||||
int? val;
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
val = scope.Database.ExecuteScalar<int?>("SELECT id FROM umbracoNode WHERE uniqueId=@id AND nodeObjectType=@nodeObjectType",
|
||||
new { id = key, nodeObjectType = GetNodeObjectTypeGuid(umbracoObjectType) });
|
||||
var sql = scope.Database.SqlContext.Sql()
|
||||
.Select<NodeDto>(x => x.NodeId).From<NodeDto>().Where<NodeDto>(x => x.UniqueId == key);
|
||||
|
||||
if (umbracoObjectType != UmbracoObjectTypes.Unknown) // if unknow, don't include in query
|
||||
sql = sql.Where<NodeDto>(x => x.NodeObjectType == GetNodeObjectTypeGuid(umbracoObjectType) || x.NodeObjectType == Constants.ObjectTypes.IdReservation); // fixme TEST the OR here!
|
||||
|
||||
val = scope.Database.ExecuteScalar<int?>(sql);
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
@@ -89,8 +96,13 @@ namespace Umbraco.Core.Services
|
||||
Guid? val;
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
val = scope.Database.ExecuteScalar<Guid?>("SELECT uniqueId FROM umbracoNode WHERE id=@id AND nodeObjectType=@nodeObjectType",
|
||||
new { id, nodeObjectType = GetNodeObjectTypeGuid(umbracoObjectType) });
|
||||
var sql = scope.Database.SqlContext.Sql()
|
||||
.Select<NodeDto>(x => x.UniqueId).From<NodeDto>().Where<NodeDto>(x => x.NodeId == id);
|
||||
|
||||
if (umbracoObjectType != UmbracoObjectTypes.Unknown) // if unknow, don't include in query
|
||||
sql = sql.Where<NodeDto>(x => x.NodeObjectType == GetNodeObjectTypeGuid(umbracoObjectType) || x.NodeObjectType == Constants.ObjectTypes.IdReservation); // fixme TEST the OR here!
|
||||
|
||||
val = scope.Database.ExecuteScalar<Guid?>(sql);
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
@@ -104,7 +116,7 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
_locker.EnterWriteLock();
|
||||
_id2Key[id] = new TypedId<Guid>(val.Value, umbracoObjectType);
|
||||
_key2Id[val.Value] = new TypedId<int>();
|
||||
_key2Id[val.Value] = new TypedId<int>(id, umbracoObjectType);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
@@ -10,13 +14,17 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
public sealed class AuditService : ScopeRepositoryService, IAuditService
|
||||
{
|
||||
private readonly Lazy<bool> _isAvailable;
|
||||
private readonly IAuditRepository _auditRepository;
|
||||
private readonly IAuditEntryRepository _auditEntryRepository;
|
||||
|
||||
public AuditService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory,
|
||||
IAuditRepository auditRepository)
|
||||
IAuditRepository auditRepository, IAuditEntryRepository auditEntryRepository)
|
||||
: base(provider, logger, eventMessagesFactory)
|
||||
{
|
||||
_auditRepository = auditRepository;
|
||||
_auditEntryRepository = auditEntryRepository;
|
||||
_isAvailable = new Lazy<bool>(DetermineIsAvailable);
|
||||
}
|
||||
|
||||
public void Add(AuditType type, string comment, int userId, int objectId)
|
||||
@@ -28,35 +36,35 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<AuditItem> GetLogs(int objectId)
|
||||
public IEnumerable<IAuditItem> GetLogs(int objectId)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var result = _auditRepository.Get(Query<AuditItem>().Where(x => x.Id == objectId));
|
||||
var result = _auditRepository.Get(Query<IAuditItem>().Where(x => x.Id == objectId));
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<AuditItem> GetUserLogs(int userId, AuditType type, DateTime? sinceDate = null)
|
||||
public IEnumerable<IAuditItem> GetUserLogs(int userId, AuditType type, DateTime? sinceDate = null)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var result = sinceDate.HasValue == false
|
||||
? _auditRepository.Get(Query<AuditItem>().Where(x => x.UserId == userId && x.AuditType == type))
|
||||
: _auditRepository.Get(Query<AuditItem>().Where(x => x.UserId == userId && x.AuditType == type && x.CreateDate >= sinceDate.Value));
|
||||
? _auditRepository.Get(Query<IAuditItem>().Where(x => x.UserId == userId && x.AuditType == type))
|
||||
: _auditRepository.Get(Query<IAuditItem>().Where(x => x.UserId == userId && x.AuditType == type && x.CreateDate >= sinceDate.Value));
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<AuditItem> GetLogs(AuditType type, DateTime? sinceDate = null)
|
||||
public IEnumerable<IAuditItem> GetLogs(AuditType type, DateTime? sinceDate = null)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var result = sinceDate.HasValue == false
|
||||
? _auditRepository.Get(Query<AuditItem>().Where(x => x.AuditType == type))
|
||||
: _auditRepository.Get(Query<AuditItem>().Where(x => x.AuditType == type && x.CreateDate >= sinceDate.Value));
|
||||
? _auditRepository.Get(Query<IAuditItem>().Where(x => x.AuditType == type))
|
||||
: _auditRepository.Get(Query<IAuditItem>().Where(x => x.AuditType == type && x.CreateDate >= sinceDate.Value));
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
@@ -70,5 +78,163 @@ namespace Umbraco.Core.Services.Implement
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns paged items in the audit trail for a given entity
|
||||
/// </summary>
|
||||
/// <param name="entityId"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="orderDirection">
|
||||
/// By default this will always be ordered descending (newest first)
|
||||
/// </param>
|
||||
/// <param name="auditTypeFilter">
|
||||
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
|
||||
/// so we need to do that here
|
||||
/// </param>
|
||||
/// <param name="customFilter">
|
||||
/// Optional filter to be applied
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<IAuditItem> GetPagedItemsByEntity(int entityId, long pageIndex, int pageSize, out long totalRecords,
|
||||
Direction orderDirection = Direction.Descending,
|
||||
AuditType[] auditTypeFilter = null,
|
||||
IQuery<IAuditItem> customFilter = null)
|
||||
{
|
||||
if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex));
|
||||
if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize));
|
||||
|
||||
if (entityId == Constants.System.Root || entityId <= 0)
|
||||
{
|
||||
totalRecords = 0;
|
||||
return Enumerable.Empty<IAuditItem>();
|
||||
}
|
||||
|
||||
using (ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var query = Query<IAuditItem>().Where(x => x.Id == entityId);
|
||||
|
||||
return _auditRepository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, orderDirection, auditTypeFilter, customFilter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns paged items in the audit trail for a given user
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="orderDirection">
|
||||
/// By default this will always be ordered descending (newest first)
|
||||
/// </param>
|
||||
/// <param name="auditTypeFilter">
|
||||
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
|
||||
/// so we need to do that here
|
||||
/// </param>
|
||||
/// <param name="customFilter">
|
||||
/// Optional filter to be applied
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<IAuditItem> GetPagedItemsByUser(int userId, long pageIndex, int pageSize, out long totalRecords, Direction orderDirection = Direction.Descending, AuditType[] auditTypeFilter = null, IQuery<IAuditItem> customFilter = null)
|
||||
{
|
||||
if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex));
|
||||
if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize));
|
||||
|
||||
if (userId < 0)
|
||||
{
|
||||
totalRecords = 0;
|
||||
return Enumerable.Empty<IAuditItem>();
|
||||
}
|
||||
|
||||
using (ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var query = Query<IAuditItem>().Where(x => x.UserId == userId);
|
||||
|
||||
return _auditRepository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, orderDirection, auditTypeFilter, customFilter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAuditEntry Write(int performingUserId, string perfomingDetails, string performingIp, DateTime eventDateUtc, int affectedUserId, string affectedDetails, string eventType, string eventDetails)
|
||||
{
|
||||
if (performingUserId < 0) throw new ArgumentOutOfRangeException(nameof(performingUserId));
|
||||
if (string.IsNullOrWhiteSpace(perfomingDetails)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(perfomingDetails));
|
||||
if (string.IsNullOrWhiteSpace(eventType)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(eventType));
|
||||
if (string.IsNullOrWhiteSpace(eventDetails)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(eventDetails));
|
||||
|
||||
//we need to truncate the data else we'll get SQL errors
|
||||
affectedDetails = affectedDetails?.Substring(0, Math.Min(affectedDetails.Length, AuditEntryDto.DetailsLength));
|
||||
eventDetails = eventDetails.Substring(0, Math.Min(eventDetails.Length, AuditEntryDto.DetailsLength));
|
||||
|
||||
//validate the eventType - must contain a forward slash, no spaces, no special chars
|
||||
var eventTypeParts = eventType.ToCharArray();
|
||||
if (eventTypeParts.Contains('/') == false || eventTypeParts.All(c => char.IsLetterOrDigit(c) || c == '/' || c == '-') == false)
|
||||
throw new ArgumentException(nameof(eventType) + " must contain only alphanumeric characters, hyphens and at least one '/' defining a category");
|
||||
if (eventType.Length > AuditEntryDto.EventTypeLength)
|
||||
throw new ArgumentException($"Must be max {AuditEntryDto.EventTypeLength} chars.", nameof(eventType));
|
||||
if (performingIp != null && performingIp.Length > AuditEntryDto.IpLength)
|
||||
throw new ArgumentException($"Must be max {AuditEntryDto.EventTypeLength} chars.", nameof(performingIp));
|
||||
|
||||
var entry = new AuditEntry
|
||||
{
|
||||
PerformingUserId = performingUserId,
|
||||
PerformingDetails = perfomingDetails,
|
||||
PerformingIp = performingIp,
|
||||
EventDateUtc = eventDateUtc,
|
||||
AffectedUserId = affectedUserId,
|
||||
AffectedDetails = affectedDetails,
|
||||
EventType = eventType,
|
||||
EventDetails = eventDetails
|
||||
};
|
||||
|
||||
if (_isAvailable.Value == false) return entry;
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
_auditEntryRepository.Save(entry);
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
//TODO: Currently used in testing only, not part of the interface, need to add queryable methods to the interface instead
|
||||
internal IEnumerable<IAuditEntry> GetAll()
|
||||
{
|
||||
if (_isAvailable.Value == false) return Enumerable.Empty<IAuditEntry>();
|
||||
|
||||
using (ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
return _auditEntryRepository.GetMany();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Currently used in testing only, not part of the interface, need to add queryable methods to the interface instead
|
||||
internal IEnumerable<IAuditEntry> GetPage(long pageIndex, int pageCount, out long records)
|
||||
{
|
||||
if (_isAvailable.Value == false)
|
||||
{
|
||||
records = 0;
|
||||
return Enumerable.Empty<IAuditEntry>();
|
||||
}
|
||||
|
||||
using (ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
return _auditEntryRepository.GetPage(pageIndex, pageCount, out records);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the repository is available.
|
||||
/// </summary>
|
||||
private bool DetermineIsAvailable()
|
||||
{
|
||||
using (ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
return _auditEntryRepository.IsAvailable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
81
src/Umbraco.Core/Services/Implement/ConsentService.cs
Normal file
81
src/Umbraco.Core/Services/Implement/ConsentService.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IContentService"/>.
|
||||
/// </summary>
|
||||
internal class ConsentService : ScopeRepositoryService, IConsentService
|
||||
{
|
||||
private readonly IConsentRepository _consentRepository;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentService"/> class.
|
||||
/// </summary>
|
||||
public ConsentService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IConsentRepository consentRepository)
|
||||
: base(provider, logger, eventMessagesFactory)
|
||||
{
|
||||
_consentRepository = consentRepository;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IConsent RegisterConsent(string source, string context, string action, ConsentState state, string comment = null)
|
||||
{
|
||||
// prevent stupid states
|
||||
var v = 0;
|
||||
if ((state & ConsentState.Pending) > 0) v++;
|
||||
if ((state & ConsentState.Granted) > 0) v++;
|
||||
if ((state & ConsentState.Revoked) > 0) v++;
|
||||
if (v != 1)
|
||||
throw new ArgumentException("Invalid state.", nameof(state));
|
||||
|
||||
var consent = new Consent
|
||||
{
|
||||
Current = true,
|
||||
Source = source,
|
||||
Context = context,
|
||||
Action = action,
|
||||
CreateDate = DateTime.Now,
|
||||
State = state,
|
||||
Comment = comment
|
||||
};
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
_consentRepository.ClearCurrent(source, context, action);
|
||||
_consentRepository.Save(consent);
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
return consent;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IConsent> LookupConsent(string source = null, string context = null, string action = null,
|
||||
bool sourceStartsWith = false, bool contextStartsWith = false, bool actionStartsWith = false,
|
||||
bool includeHistory = false)
|
||||
{
|
||||
using (ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var query = Query<IConsent>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(source) == false)
|
||||
query = sourceStartsWith ? query.Where(x => x.Source.StartsWith(source)) : query.Where(x => x.Source == source);
|
||||
if (string.IsNullOrWhiteSpace(context) == false)
|
||||
query = contextStartsWith ? query.Where(x => x.Context.StartsWith(context)) : query.Where(x => x.Context == context);
|
||||
if (string.IsNullOrWhiteSpace(action) == false)
|
||||
query = actionStartsWith ? query.Where(x => x.Action.StartsWith(action)) : query.Where(x => x.Action == action);
|
||||
if (includeHistory == false)
|
||||
query = query.Where(x => x.Current);
|
||||
|
||||
return _consentRepository.Get(query);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
scope.ReadLock(Constants.Locks.ContentTree);
|
||||
return _documentRepository.CountPublished();
|
||||
return _documentRepository.CountPublished(contentTypeAlias);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,7 +651,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
totalChildren = 0;
|
||||
return Enumerable.Empty<IContent>();
|
||||
}
|
||||
query.Where(x => x.Path.SqlStartsWith($"{contentPath[0]},", TextColumnType.NVarchar));
|
||||
query.Where(x => x.Path.SqlStartsWith($"{contentPath[0].Path},", TextColumnType.NVarchar));
|
||||
}
|
||||
return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter);
|
||||
}
|
||||
@@ -800,8 +800,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
scope.ReadLock(Constants.Locks.ContentTree);
|
||||
var bin = $"{Constants.System.Root},{Constants.System.RecycleBinContent},";
|
||||
var query = Query<IContent>().Where(x => x.Path.StartsWith(bin));
|
||||
var query = Query<IContent>().Where(x => x.Path.StartsWith(Constants.System.RecycleBinContentPathPrefix));
|
||||
return _documentRepository.Get(query);
|
||||
}
|
||||
}
|
||||
@@ -1709,7 +1708,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
/// <summary>
|
||||
/// Sorts a collection of <see cref="IContent"/> objects by updating the SortOrder according
|
||||
/// to the ordering of items in the passed in <see cref="IEnumerable{T}"/>.
|
||||
/// to the ordering of items in the passed in <paramref name="items"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Using this method will ensure that the Published-state is maintained upon sorting
|
||||
@@ -1726,56 +1725,88 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IContent>(itemsA);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, "Saving"))
|
||||
return false;
|
||||
|
||||
var published = new List<IContent>();
|
||||
var saved = new List<IContent>();
|
||||
|
||||
scope.WriteLock(Constants.Locks.ContentTree);
|
||||
var sortOrder = 0;
|
||||
|
||||
foreach (var content in itemsA)
|
||||
{
|
||||
// if the current sort order equals that of the content we don't
|
||||
// need to update it, so just increment the sort order and continue.
|
||||
if (content.SortOrder == sortOrder)
|
||||
{
|
||||
sortOrder++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// else update
|
||||
content.SortOrder = sortOrder++;
|
||||
content.WriterId = userId;
|
||||
|
||||
// if it's published, register it, no point running StrategyPublish
|
||||
// since we're not really publishing it and it cannot be cancelled etc
|
||||
if (content.Published)
|
||||
published.Add(content);
|
||||
|
||||
// save
|
||||
saved.Add(content);
|
||||
_documentRepository.Save(content);
|
||||
}
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs, "Saved");
|
||||
}
|
||||
|
||||
scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange<IContent>(x, TreeChangeTypes.RefreshNode)).ToEventArgs());
|
||||
|
||||
if (raiseEvents && published.Any())
|
||||
scope.Events.Dispatch(Published, this, new PublishEventArgs<IContent>(published, false, false), "Published");
|
||||
|
||||
Audit(AuditType.Sort, "Sorting content performed by user", userId, 0);
|
||||
|
||||
var ret = Sort(scope, itemsA, userId, raiseEvents);
|
||||
scope.Complete();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sorts a collection of <see cref="IContent"/> objects by updating the SortOrder according
|
||||
/// to the ordering of items identified by the <paramref name="ids"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Using this method will ensure that the Published-state is maintained upon sorting
|
||||
/// so the cache is updated accordingly - as needed.
|
||||
/// </remarks>
|
||||
/// <param name="ids"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="raiseEvents"></param>
|
||||
/// <returns>True if sorting succeeded, otherwise False</returns>
|
||||
public bool Sort(IEnumerable<int> ids, int userId = 0, bool raiseEvents = true)
|
||||
{
|
||||
var idsA = ids.ToArray();
|
||||
if (idsA.Length == 0) return true;
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.ContentTree);
|
||||
var itemsA = GetByIds(idsA).ToArray();
|
||||
|
||||
var ret = Sort(scope, itemsA, userId, raiseEvents);
|
||||
scope.Complete();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
private bool Sort(IScope scope, IContent[] itemsA, int userId, bool raiseEvents)
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IContent>(itemsA);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, "Saving"))
|
||||
return false;
|
||||
|
||||
var published = new List<IContent>();
|
||||
var saved = new List<IContent>();
|
||||
var sortOrder = 0;
|
||||
|
||||
foreach (var content in itemsA)
|
||||
{
|
||||
// if the current sort order equals that of the content we don't
|
||||
// need to update it, so just increment the sort order and continue.
|
||||
if (content.SortOrder == sortOrder)
|
||||
{
|
||||
sortOrder++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// else update
|
||||
content.SortOrder = sortOrder++;
|
||||
content.WriterId = userId;
|
||||
|
||||
// if it's published, register it, no point running StrategyPublish
|
||||
// since we're not really publishing it and it cannot be cancelled etc
|
||||
if (content.Published)
|
||||
published.Add(content);
|
||||
|
||||
// save
|
||||
saved.Add(content);
|
||||
_documentRepository.Save(content);
|
||||
}
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs, "Saved");
|
||||
}
|
||||
|
||||
scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange<IContent>(x, TreeChangeTypes.RefreshNode)).ToEventArgs());
|
||||
|
||||
if (raiseEvents && published.Any())
|
||||
scope.Events.Dispatch(Published, this, new PublishEventArgs<IContent>(published, false, false), "Published");
|
||||
|
||||
Audit(AuditType.Sort, "Sorting content performed by user", userId, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2302,6 +2333,38 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteBlueprintsOfTypes(IEnumerable<int> contentTypeIds, int userId = 0)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.ContentTree);
|
||||
|
||||
var contentTypeIdsA = contentTypeIds.ToArray();
|
||||
var query = Query<IContent>();
|
||||
if (contentTypeIdsA.Length > 0)
|
||||
query.Where(x => contentTypeIdsA.Contains(x.ContentTypeId));
|
||||
|
||||
var blueprints = _documentBlueprintRepository.Get(query).Select(x =>
|
||||
{
|
||||
((Content) x).Blueprint = true;
|
||||
return x;
|
||||
}).ToArray();
|
||||
|
||||
foreach (var blueprint in blueprints)
|
||||
{
|
||||
_documentBlueprintRepository.Delete(blueprint);
|
||||
}
|
||||
|
||||
scope.Events.Dispatch(DeletedBlueprint, this, new DeleteEventArgs<IContent>(blueprints), "DeletedBlueprint");
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteBlueprintsOfType(int contentTypeId, int userId = 0)
|
||||
{
|
||||
DeleteBlueprintsOfTypes(new[] { contentTypeId }, userId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -32,8 +33,13 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
protected override void DeleteItemsOfTypes(IEnumerable<int> typeIds)
|
||||
{
|
||||
foreach (var typeId in typeIds)
|
||||
ContentService.DeleteOfType(typeId);
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var typeIdsA = typeIds.ToArray();
|
||||
ContentService.DeleteOfTypes(typeIdsA);
|
||||
ContentService.DeleteBlueprintsOfTypes(typeIdsA);
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -82,6 +88,5 @@ namespace Umbraco.Core.Services.Implement
|
||||
return Repository.GetAllContentTypeIds(aliases);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
protected void OnDeletedContainer(IScope scope, DeleteEventArgs<EntityContainer> args)
|
||||
{
|
||||
scope.Events.Dispatch(DeletedContainer, This, args);
|
||||
scope.Events.Dispatch(DeletedContainer, This, args, "DeletedContainer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,7 +445,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
//null check otherwise we get exceptions
|
||||
if (media.Path.IsNullOrWhiteSpace()) return Enumerable.Empty<IMedia>();
|
||||
|
||||
var rootId = Constants.System.Root.ToInvariantString();
|
||||
var rootId = Constants.System.RootString;
|
||||
var ids = media.Path.Split(',')
|
||||
.Where(x => x != rootId && x != media.Id.ToString(CultureInfo.InvariantCulture))
|
||||
.Select(int.Parse)
|
||||
@@ -616,7 +616,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
totalChildren = 0;
|
||||
return Enumerable.Empty<IMedia>();
|
||||
}
|
||||
query.Where(x => x.Path.SqlStartsWith(mediaPath[0] + ",", TextColumnType.NVarchar));
|
||||
query.Where(x => x.Path.SqlStartsWith(mediaPath[0].Path + ",", TextColumnType.NVarchar));
|
||||
}
|
||||
return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter);
|
||||
}
|
||||
@@ -706,8 +706,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
scope.ReadLock(Constants.Locks.MediaTree);
|
||||
var bin = $"{Constants.System.Root},{Constants.System.RecycleBinMedia},";
|
||||
var query = Query<IMedia>().Where(x => x.Path.StartsWith(bin));
|
||||
var query = Query<IMedia>().Where(x => x.Path.StartsWith(Constants.System.RecycleBinMediaPathPrefix));
|
||||
return _mediaRepository.Get(query);
|
||||
}
|
||||
}
|
||||
@@ -734,7 +733,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
/// <returns><see cref="IMedia"/></returns>
|
||||
public IMedia GetMediaByPath(string mediaPath)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
using (ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
return _mediaRepository.GetMediaByPath(mediaPath);
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
scope.ReadLock(Constants.Locks.MemberTree);
|
||||
var query1 = memberTypeAlias == null ? null : Query<IMember>().Where(x => x.ContentTypeAlias == memberTypeAlias);
|
||||
var query2 = filter == null ? null : Query<IMember>().Where(x => x.Name.Contains(filter) || x.Username.Contains(filter));
|
||||
var query2 = filter == null ? null : Query<IMember>().Where(x => x.Name.Contains(filter) || x.Username.Contains(filter) || x.Email.Contains(filter));
|
||||
return _memberRepository.GetPage(query1, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, orderBySystemField, query2);
|
||||
}
|
||||
}
|
||||
@@ -815,6 +815,10 @@ namespace Umbraco.Core.Services.Implement
|
||||
/// Default is <c>True</c> otherwise set to <c>False</c> to not raise events</param>
|
||||
public void Save(IMember member, bool raiseEvents = true)
|
||||
{
|
||||
//trimming username and email to make sure we have no trailing space
|
||||
member.Username = member.Username.Trim();
|
||||
member.Email = member.Email.Trim();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IMember>(member);
|
||||
@@ -866,7 +870,13 @@ namespace Umbraco.Core.Services.Implement
|
||||
scope.WriteLock(Constants.Locks.MemberTree);
|
||||
|
||||
foreach (var member in membersA)
|
||||
{
|
||||
//trimming username and email to make sure we have no trailing space
|
||||
member.Username = member.Username.Trim();
|
||||
member.Email = member.Email.Trim();
|
||||
|
||||
_memberRepository.Save(member);
|
||||
}
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
@@ -1018,7 +1028,9 @@ namespace Umbraco.Core.Services.Implement
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.MemberTree);
|
||||
_memberGroupRepository.AssignRoles(usernames, roleNames);
|
||||
var ids = _memberGroupRepository.GetMemberIds(usernames);
|
||||
_memberGroupRepository.AssignRoles(ids, roleNames);
|
||||
scope.Events.Dispatch(AssignedRoles, this, new RolesEventArgs(ids, roleNames));
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -1033,7 +1045,9 @@ namespace Umbraco.Core.Services.Implement
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.MemberTree);
|
||||
_memberGroupRepository.DissociateRoles(usernames, roleNames);
|
||||
var ids = _memberGroupRepository.GetMemberIds(usernames);
|
||||
_memberGroupRepository.DissociateRoles(ids, roleNames);
|
||||
scope.Events.Dispatch(RemovedRoles, this, new RolesEventArgs(ids, roleNames));
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -1049,6 +1063,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.MemberTree);
|
||||
_memberGroupRepository.AssignRoles(memberIds, roleNames);
|
||||
scope.Events.Dispatch(AssignedRoles, this, new RolesEventArgs(memberIds, roleNames));
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -1064,6 +1079,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.MemberTree);
|
||||
_memberGroupRepository.DissociateRoles(memberIds, roleNames);
|
||||
scope.Events.Dispatch(RemovedRoles, this, new RolesEventArgs(memberIds, roleNames));
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -1110,6 +1126,21 @@ namespace Umbraco.Core.Services.Implement
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMemberService, SaveEventArgs<IMember>> Saved;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after roles have been assigned.
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMemberService, RolesEventArgs> AssignedRoles;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after roles have been removed.
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMemberService, RolesEventArgs> RemovedRoles;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after members have been exported.
|
||||
/// </summary>
|
||||
internal static event TypedEventHandler<IMemberService, ExportedMemberEventArgs> Exported;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Membership
|
||||
@@ -1219,6 +1250,72 @@ namespace Umbraco.Core.Services.Implement
|
||||
return member;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports a member.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is internal for now and is used to export a member in the member editor,
|
||||
/// it will raise an event so that auditing logs can be created.
|
||||
/// </remarks>
|
||||
internal MemberExportModel ExportMember(Guid key)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var query = Query<IMember>().Where(x => x.Key == key);
|
||||
var member = _memberRepository.Get(query).FirstOrDefault();
|
||||
|
||||
if (member == null) return null;
|
||||
|
||||
var model = new MemberExportModel
|
||||
{
|
||||
Id = member.Id,
|
||||
Key = member.Key,
|
||||
Name = member.Name,
|
||||
Username = member.Username,
|
||||
Email = member.Email,
|
||||
Groups = GetAllRoles(member.Id).ToList(),
|
||||
ContentTypeAlias = member.ContentTypeAlias,
|
||||
CreateDate = member.CreateDate,
|
||||
UpdateDate = member.UpdateDate,
|
||||
Properties = new List<MemberExportProperty>(GetPropertyExportItems(member))
|
||||
};
|
||||
|
||||
scope.Events.Dispatch(Exported, this, new ExportedMemberEventArgs(member, model));
|
||||
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<MemberExportProperty> GetPropertyExportItems(IMember member)
|
||||
{
|
||||
if (member == null) throw new ArgumentNullException(nameof(member));
|
||||
|
||||
var exportProperties = new List<MemberExportProperty>();
|
||||
|
||||
foreach (var property in member.Properties)
|
||||
{
|
||||
//ignore list
|
||||
switch (property.Alias)
|
||||
{
|
||||
case Constants.Conventions.Member.PasswordQuestion:
|
||||
continue;
|
||||
}
|
||||
|
||||
var propertyExportModel = new MemberExportProperty
|
||||
{
|
||||
Id = property.Id,
|
||||
Alias = property.Alias,
|
||||
Name = property.PropertyType.Name,
|
||||
Value = property.GetValue(), // fixme ignoring variants
|
||||
CreateDate = property.CreateDate,
|
||||
UpdateDate = property.UpdateDate
|
||||
};
|
||||
exportProperties.Add(propertyExportModel);
|
||||
}
|
||||
|
||||
return exportProperties;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Content Types
|
||||
|
||||
@@ -768,21 +768,18 @@ namespace Umbraco.Core.Services.Implement
|
||||
foreach (var element in structureElement.Elements("DocumentType"))
|
||||
{
|
||||
var alias = element.Value;
|
||||
if (_importedContentTypes.ContainsKey(alias))
|
||||
{
|
||||
var allowedChild = _importedContentTypes[alias];
|
||||
if (allowedChild == null || allowedChildren.Any(x => x.Id.IsValueCreated && x.Id.Value == allowedChild.Id)) continue;
|
||||
|
||||
allowedChildren.Add(new ContentTypeSort(new Lazy<int>(() => allowedChild.Id), sortOrder, allowedChild.Alias));
|
||||
sortOrder++;
|
||||
}
|
||||
else
|
||||
var allowedChild = _importedContentTypes.ContainsKey(alias) ? _importedContentTypes[alias] : _contentTypeService.Get(alias);
|
||||
if (allowedChild == null)
|
||||
{
|
||||
_logger.Warn<PackagingService>(
|
||||
string.Format(
|
||||
"Packager: Error handling DocumentType structure. DocumentType with alias '{0}' could not be found and was not added to the structure for '{1}'.",
|
||||
alias, contentType.Alias));
|
||||
_logger.Warn<PackagingService>($"Packager: Error handling DocumentType structure. DocumentType with alias '{alias}' could not be found and was not added to the structure for '{contentType.Alias}'.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (allowedChildren.Any(x => x.Id.IsValueCreated && x.Id.Value == allowedChild.Id)) continue;
|
||||
|
||||
allowedChildren.Add(new ContentTypeSort(new Lazy<int>(() => allowedChild.Id), sortOrder, allowedChild.Alias));
|
||||
sortOrder++;
|
||||
}
|
||||
|
||||
contentType.AllowedContentTypes = allowedChildren;
|
||||
@@ -1679,9 +1676,10 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
internal InstallationSummary InstallPackage(string packageFilePath, int userId = 0, bool raiseEvents = false)
|
||||
{
|
||||
var metaData = GetPackageMetaData(packageFilePath);
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
var metaData = GetPackageMetaData(packageFilePath);
|
||||
if (ImportingPackage.IsRaisedEventCancelled(new ImportPackageEventArgs<string>(packageFilePath, metaData), this))
|
||||
{
|
||||
var initEmpty = new InstallationSummary().InitEmpty();
|
||||
@@ -1693,7 +1691,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
ImportedPackage.RaiseEvent(new ImportPackageEventArgs<InstallationSummary>(installationSummary, false), this);
|
||||
ImportedPackage.RaiseEvent(new ImportPackageEventArgs<InstallationSummary>(installationSummary, metaData, false), this);
|
||||
}
|
||||
|
||||
return installationSummary;
|
||||
|
||||
@@ -224,9 +224,9 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an <see cref="IUser"/>
|
||||
/// Disables an <see cref="IUser"/>
|
||||
/// </summary>
|
||||
/// <param name="membershipUser"><see cref="IUser"/> to Delete</param>
|
||||
/// <param name="membershipUser"><see cref="IUser"/> to disable</param>
|
||||
public void Delete(IUser membershipUser)
|
||||
{
|
||||
//disable
|
||||
@@ -542,6 +542,42 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
public Guid CreateLoginSession(int userId, string requestingIpAddress)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var session = _userRepository.CreateLoginSession(userId, requestingIpAddress);
|
||||
scope.Complete();
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
public int ClearLoginSessions(int userId)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var count = _userRepository.ClearLoginSessions(userId);
|
||||
scope.Complete();
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearLoginSession(Guid sessionId)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
_userRepository.ClearLoginSession(sessionId);
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ValidateLoginSession(int userId, Guid sessionId)
|
||||
{
|
||||
using (ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
return _userRepository.ValidateLoginSession(userId, sessionId);
|
||||
}
|
||||
}
|
||||
public IDictionary<UserState, int> GetUserStates()
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
@@ -747,8 +783,9 @@ namespace Umbraco.Core.Services.Implement
|
||||
_userGroupRepository.ReplaceGroupPermissions(groupId, permissions, entityIds);
|
||||
scope.Complete();
|
||||
|
||||
scope.Events.Dispatch(UserGroupPermissionsAssigned, this, new SaveEventArgs<EntityPermission>(entityIds.Select(x => new EntityPermission(groupId, x, permissions.Select(p => p.ToString(CultureInfo.InvariantCulture)).ToArray()))
|
||||
.ToArray(), false));
|
||||
var assigned = permissions.Select(p => p.ToString(CultureInfo.InvariantCulture)).ToArray();
|
||||
scope.Events.Dispatch(UserGroupPermissionsAssigned, this,
|
||||
new SaveEventArgs<EntityPermission>(entityIds.Select(x => new EntityPermission(groupId, x, assigned)).ToArray(), false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -768,8 +805,9 @@ namespace Umbraco.Core.Services.Implement
|
||||
_userGroupRepository.AssignGroupPermission(groupId, permission, entityIds);
|
||||
scope.Complete();
|
||||
|
||||
scope.Events.Dispatch(UserGroupPermissionsAssigned, this, new SaveEventArgs<EntityPermission>(entityIds.Select(x => new EntityPermission(groupId, x, new[] { permission.ToString(CultureInfo.InvariantCulture) }))
|
||||
.ToArray(), false));
|
||||
var assigned = new[] { permission.ToString(CultureInfo.InvariantCulture) };
|
||||
scope.Events.Dispatch(UserGroupPermissionsAssigned, this,
|
||||
new SaveEventArgs<EntityPermission>(entityIds.Select(x => new EntityPermission(groupId, x, assigned)).ToArray(), false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,7 +880,23 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IUserGroup>(userGroup);
|
||||
// we need to figure out which users have been added / removed, for audit purposes
|
||||
var empty = new IUser[0];
|
||||
var addedUsers = empty;
|
||||
var removedUsers = empty;
|
||||
|
||||
if (userIds != null)
|
||||
{
|
||||
var groupUsers = userGroup.HasIdentity ? _userRepository.GetAllInGroup(userGroup.Id).ToArray() : empty;
|
||||
var xGroupUsers = groupUsers.ToDictionary(x => x.Id, x => x);
|
||||
var groupIds = groupUsers.Select(x => x.Id).ToArray();
|
||||
|
||||
addedUsers = _userRepository.GetMany(userIds.Except(groupIds).ToArray()).Where(x => x.Id != 0).ToArray();
|
||||
removedUsers = groupIds.Except(userIds).Select(x => xGroupUsers[x]).Where(x => x.Id != 0).ToArray();
|
||||
}
|
||||
|
||||
var saveEventArgs = new SaveEventArgs<UserGroupWithUsers>(new UserGroupWithUsers(userGroup, addedUsers, removedUsers));
|
||||
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(SavingUserGroup, this, saveEventArgs))
|
||||
{
|
||||
scope.Complete();
|
||||
@@ -1183,12 +1237,12 @@ namespace Umbraco.Core.Services.Implement
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IUserService, SaveEventArgs<IUserGroup>> SavingUserGroup;
|
||||
internal static event TypedEventHandler<IUserService, SaveEventArgs<UserGroupWithUsers>> SavingUserGroup;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IUserService, SaveEventArgs<IUserGroup>> SavedUserGroup;
|
||||
internal static event TypedEventHandler<IUserService, SaveEventArgs<UserGroupWithUsers>> SavedUserGroup;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
|
||||
@@ -34,12 +34,13 @@ namespace Umbraco.Core.Services
|
||||
private readonly Lazy<INotificationService> _notificationService;
|
||||
private readonly Lazy<IExternalLoginService> _externalLoginService;
|
||||
private readonly Lazy<IRedirectUrlService> _redirectUrlService;
|
||||
private readonly Lazy<IConsentService> _consentService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServiceContext"/> class with lazy services.
|
||||
/// </summary>
|
||||
/// <remarks>Used by IoC. Note that LightInject will favor lazy args when picking a constructor.</remarks>
|
||||
public ServiceContext(Lazy<IPublicAccessService> publicAccessService, Lazy<ITaskService> taskService, Lazy<IDomainService> domainService, Lazy<IAuditService> auditService, Lazy<ILocalizedTextService> localizedTextService, Lazy<ITagService> tagService, Lazy<IContentService> contentService, Lazy<IUserService> userService, Lazy<IMemberService> memberService, Lazy<IMediaService> mediaService, Lazy<IContentTypeService> contentTypeService, Lazy<IMediaTypeService> mediaTypeService, Lazy<IDataTypeService> dataTypeService, Lazy<IFileService> fileService, Lazy<ILocalizationService> localizationService, Lazy<IPackagingService> packagingService, Lazy<IServerRegistrationService> serverRegistrationService, Lazy<IEntityService> entityService, Lazy<IRelationService> relationService, Lazy<IApplicationTreeService> treeService, Lazy<ISectionService> sectionService, Lazy<IMacroService> macroService, Lazy<IMemberTypeService> memberTypeService, Lazy<IMemberGroupService> memberGroupService, Lazy<INotificationService> notificationService, Lazy<IExternalLoginService> externalLoginService, Lazy<IRedirectUrlService> redirectUrlService)
|
||||
public ServiceContext(Lazy<IPublicAccessService> publicAccessService, Lazy<ITaskService> taskService, Lazy<IDomainService> domainService, Lazy<IAuditService> auditService, Lazy<ILocalizedTextService> localizedTextService, Lazy<ITagService> tagService, Lazy<IContentService> contentService, Lazy<IUserService> userService, Lazy<IMemberService> memberService, Lazy<IMediaService> mediaService, Lazy<IContentTypeService> contentTypeService, Lazy<IMediaTypeService> mediaTypeService, Lazy<IDataTypeService> dataTypeService, Lazy<IFileService> fileService, Lazy<ILocalizationService> localizationService, Lazy<IPackagingService> packagingService, Lazy<IServerRegistrationService> serverRegistrationService, Lazy<IEntityService> entityService, Lazy<IRelationService> relationService, Lazy<IApplicationTreeService> treeService, Lazy<ISectionService> sectionService, Lazy<IMacroService> macroService, Lazy<IMemberTypeService> memberTypeService, Lazy<IMemberGroupService> memberGroupService, Lazy<INotificationService> notificationService, Lazy<IExternalLoginService> externalLoginService, Lazy<IRedirectUrlService> redirectUrlService, Lazy<IConsentService> consentService)
|
||||
{
|
||||
_publicAccessService = publicAccessService;
|
||||
_taskService = taskService;
|
||||
@@ -68,14 +69,14 @@ namespace Umbraco.Core.Services
|
||||
_notificationService = notificationService;
|
||||
_externalLoginService = externalLoginService;
|
||||
_redirectUrlService = redirectUrlService;
|
||||
_consentService = consentService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServiceContext"/> class with services.
|
||||
/// </summary>
|
||||
/// <remarks>Used in tests. All items are optional and remain null if not specified.</remarks>
|
||||
public ServiceContext(
|
||||
IContentService contentService = null,
|
||||
public ServiceContext(IContentService contentService = null,
|
||||
IMediaService mediaService = null,
|
||||
IContentTypeService contentTypeService = null,
|
||||
IMediaTypeService mediaTypeService = null,
|
||||
@@ -101,7 +102,8 @@ namespace Umbraco.Core.Services
|
||||
IPublicAccessService publicAccessService = null,
|
||||
IExternalLoginService externalLoginService = null,
|
||||
IServerRegistrationService serverRegistrationService = null,
|
||||
IRedirectUrlService redirectUrlService = null)
|
||||
IRedirectUrlService redirectUrlService = null,
|
||||
IConsentService consentService = null)
|
||||
{
|
||||
if (serverRegistrationService != null) _serverRegistrationService = new Lazy<IServerRegistrationService>(() => serverRegistrationService);
|
||||
if (externalLoginService != null) _externalLoginService = new Lazy<IExternalLoginService>(() => externalLoginService);
|
||||
@@ -130,6 +132,7 @@ namespace Umbraco.Core.Services
|
||||
if (macroService != null) _macroService = new Lazy<IMacroService>(() => macroService);
|
||||
if (publicAccessService != null) _publicAccessService = new Lazy<IPublicAccessService>(() => publicAccessService);
|
||||
if (redirectUrlService != null) _redirectUrlService = new Lazy<IRedirectUrlService>(() => redirectUrlService);
|
||||
if (consentService != null) _consentService = new Lazy<IConsentService>(() => consentService);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -257,11 +260,19 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public IMemberGroupService MemberGroupService => _memberGroupService.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ExternalLoginService.
|
||||
/// </summary>
|
||||
public IExternalLoginService ExternalLoginService => _externalLoginService.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RedirectUrlService.
|
||||
/// </summary>
|
||||
public IRedirectUrlService RedirectUrlService => _redirectUrlService.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ConsentService.
|
||||
/// </summary>
|
||||
public IConsentService ConsentService => _consentService.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace Umbraco.Core
|
||||
public static GuidUdi GetUdi(this IContent entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new GuidUdi(entity.Blueprint ? Constants.UdiEntityType.DocumentBluePrint : Constants.UdiEntityType.Document, entity.Key).EnsureClosed();
|
||||
return new GuidUdi(entity.Blueprint ? Constants.UdiEntityType.DocumentBlueprint : Constants.UdiEntityType.Document, entity.Key).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
<Compile Include="Attempt.cs" />
|
||||
<Compile Include="AttemptOfTResult.cs" />
|
||||
<Compile Include="AttemptOfTResultTStatus.cs" />
|
||||
<Compile Include="Auditing\AuditEventHandler.cs" />
|
||||
<Compile Include="Auditing\AuditEventsComponent.cs" />
|
||||
<Compile Include="Auditing\IdentityAuditEventArgs.cs" />
|
||||
<Compile Include="BindingRedirects.cs" />
|
||||
<Compile Include="ByteArrayExtensions.cs" />
|
||||
@@ -377,6 +377,7 @@
|
||||
<Compile Include="Persistence\Repositories\IConsentRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\Implement\AuditEntryRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\Implement\ConsentRepository.cs" />
|
||||
<Compile Include="PropertyEditors\ColorPickerConfiguration.cs" />
|
||||
<Compile Include="PropertyEditors\ConfigurationEditorOfTConfiguration.cs" />
|
||||
<Compile Include="PropertyEditors\DataEditorAttribute.cs" />
|
||||
<Compile Include="PropertyEditors\ConfigurationEditor.cs" />
|
||||
@@ -395,6 +396,7 @@
|
||||
<Compile Include="PropertyEditors\TagConfiguration.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\ImageCropperValue.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\ImageCropperValueTypeConverter.cs" />
|
||||
<Compile Include="PropertyEditors\ValueListConfiguration.cs" />
|
||||
<Compile Include="PropertyEditors\VoidEditor.cs" />
|
||||
<Compile Include="ReflectionUtilities-Unused.cs" />
|
||||
<Compile Include="Runtime\CoreRuntime.cs" />
|
||||
@@ -1349,6 +1351,7 @@
|
||||
<Compile Include="Serialization\UdiJsonConverter.cs" />
|
||||
<Compile Include="Serialization\UdiRangeJsonConverter.cs" />
|
||||
<Compile Include="ServiceContextExtensions.cs" />
|
||||
<Compile Include="Services\IConsentService.cs" />
|
||||
<Compile Include="Services\Implement\AuditService.cs" />
|
||||
<Compile Include="Services\Changes\ContentTypeChange.cs" />
|
||||
<Compile Include="Services\Changes\ContentTypeChangeExtensions.cs" />
|
||||
@@ -1357,6 +1360,7 @@
|
||||
<Compile Include="Services\Changes\TreeChange.cs" />
|
||||
<Compile Include="Services\Changes\TreeChangeExtensions.cs" />
|
||||
<Compile Include="Services\Changes\TreeChangeTypes.cs" />
|
||||
<Compile Include="Services\Implement\ConsentService.cs" />
|
||||
<Compile Include="Services\Implement\ContentService.cs" />
|
||||
<Compile Include="Services\ContentServiceExtensions.cs" />
|
||||
<Compile Include="Services\Implement\ContentTypeService.cs" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -152,7 +153,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
LogHelper.Error(typeof(UriExtensions), "Failed to determine if request was client side", ex);
|
||||
Current.Logger.Error(typeof(UriExtensions), "Failed to determine if request was client side", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
@@ -9,12 +10,11 @@ using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
internal class ColorPickerConfigurationEditor : ValueListConfigurationEditor
|
||||
internal class ColorPickerConfigurationEditor : ConfigurationEditor<ColorPickerConfiguration>
|
||||
{
|
||||
public ColorPickerConfigurationEditor(ILocalizedTextService textService)
|
||||
: base(textService)
|
||||
public ColorPickerConfigurationEditor()
|
||||
{
|
||||
var field = Fields.First();
|
||||
var field = Fields.First(x => x.Key == "items");
|
||||
|
||||
//use a custom editor too
|
||||
field.View = "views/propertyeditors/colorpicker/colorpicker.prevalues.html";
|
||||
@@ -26,23 +26,84 @@ namespace Umbraco.Web.PropertyEditors
|
||||
field.Validators.Add(new ColorListValidator());
|
||||
}
|
||||
|
||||
public override Dictionary<string, object> ToConfigurationEditor(ValueListConfiguration configuration)
|
||||
public override Dictionary<string, object> ToConfigurationEditor(ColorPickerConfiguration configuration)
|
||||
{
|
||||
if (configuration == null)
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
{ "items", new object() }
|
||||
{ "items", new object() },
|
||||
{ "useLabel", false }
|
||||
};
|
||||
|
||||
// for now, we have to do this, because the color picker is weird, but it's fixed in 7.7 at some point
|
||||
// and then we probably don't need this whole override method anymore - base shouls be enough?
|
||||
var items = configuration.Items.ToDictionary(x => x.Id.ToString(), x => GetItemValue(x, configuration.UseLabel));
|
||||
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
{ "items", configuration.Items.ToDictionary(x => x.Id.ToString(), x => x.Value) }
|
||||
{ "items", items },
|
||||
{ "useLabel", configuration.UseLabel }
|
||||
};
|
||||
}
|
||||
|
||||
private object GetItemValue(ValueListConfiguration.ValueListItem item, bool useLabel)
|
||||
{
|
||||
if (useLabel)
|
||||
{
|
||||
return item.Value.DetectIsJson()
|
||||
? JsonConvert.DeserializeObject(item.Value)
|
||||
: new JObject { { "color", item.Value }, { "label", item.Value } };
|
||||
}
|
||||
|
||||
if (!item.Value.DetectIsJson())
|
||||
return item.Value;
|
||||
|
||||
var jobject = (JObject) JsonConvert.DeserializeObject(item.Value);
|
||||
return jobject.Property("color").Value.Value<string>();
|
||||
}
|
||||
|
||||
public override ColorPickerConfiguration FromConfigurationEditor(Dictionary<string, object> editorValues, ColorPickerConfiguration configuration)
|
||||
{
|
||||
var output = new ColorPickerConfiguration();
|
||||
|
||||
if (!editorValues.TryGetValue("items", out var jjj) || !(jjj is JArray jItems))
|
||||
return output; // oops
|
||||
|
||||
// handle useLabel
|
||||
if (editorValues.TryGetValue("useLabel", out var useLabelObj))
|
||||
output.UseLabel = useLabelObj.TryConvertTo<bool>();
|
||||
|
||||
// auto-assigning our ids, get next id from existing values
|
||||
var nextId = 1;
|
||||
if (configuration?.Items != null && configuration.Items.Count > 0)
|
||||
nextId = configuration.Items.Max(x => x.Id) + 1;
|
||||
|
||||
// create ValueListItem instances - sortOrder is ignored here
|
||||
foreach (var item in jItems.OfType<JObject>())
|
||||
{
|
||||
var value = item.Property("value")?.Value?.Value<string>();
|
||||
if (string.IsNullOrWhiteSpace(value)) continue;
|
||||
|
||||
var id = item.Property("id")?.Value?.Value<int>() ?? 0;
|
||||
if (id >= nextId) nextId = id + 1;
|
||||
|
||||
// if using a label, replace color by json blob
|
||||
// (a pity we have to serialize here!)
|
||||
if (output.UseLabel)
|
||||
{
|
||||
var label = item.Property("label")?.Value?.Value<string>();
|
||||
value = JsonConvert.SerializeObject(new { value, label });
|
||||
}
|
||||
|
||||
output.Items.Add(new ValueListConfiguration.ValueListItem { Id = id, Value = value });
|
||||
}
|
||||
|
||||
// ensure ids
|
||||
foreach (var item in output.Items)
|
||||
if (item.Id == 0)
|
||||
item.Id = nextId++;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
internal class ColorListValidator : IValueValidator
|
||||
{
|
||||
public IEnumerable<ValidationResult> Validate(object value, string valueType, object dataTypeConfiguration)
|
||||
|
||||
@@ -298,7 +298,6 @@
|
||||
<Compile Include="PropertyEditors\ValueConverters\MediaPickerValueConverter.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\MemberPickerValueConverter.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\MultiNodeTreePickerValueConverter.cs" />
|
||||
<Compile Include="PropertyEditors\ValueListConfiguration.cs" />
|
||||
<Compile Include="PropertyEditors\ValueListUniqueValueValidator.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\DataSource\PropertyData.cs" />
|
||||
<Compile Include="PublishedCache\NuCache\NuCacheComponent.cs" />
|
||||
|
||||
Reference in New Issue
Block a user