diff --git a/src/Umbraco.Core/EventArgs.cs b/src/Umbraco.Core/EventArgs.cs index 406cc780db..3e67239fa4 100644 --- a/src/Umbraco.Core/EventArgs.cs +++ b/src/Umbraco.Core/EventArgs.cs @@ -3,8 +3,7 @@ namespace Umbraco.Core { //Publishing Events - public class PublishEventArgs : System.ComponentModel.CancelEventArgs { } - public class UnPublishEventArgs : System.ComponentModel.CancelEventArgs { } + public class PublishingEventArgs : System.ComponentModel.CancelEventArgs { } public class SendToPublishEventArgs : System.ComponentModel.CancelEventArgs { } //Moving Content Events diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 4dbde74b63..ba72f6d099 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Reflection; using System.Runtime.Serialization; +using Umbraco.Core.Models.Membership; namespace Umbraco.Core.Models { @@ -18,6 +19,7 @@ namespace Umbraco.Core.Models private string _language; private DateTime? _releaseDate; private DateTime? _expireDate; + private IProfile _writer; /// /// Constructor for creating a Content object @@ -46,6 +48,7 @@ namespace Umbraco.Core.Models private static readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.Language); private static readonly PropertyInfo ReleaseDateSelector = ExpressionHelper.GetPropertyInfo(x => x.ReleaseDate); private static readonly PropertyInfo ExpireDateSelector = ExpressionHelper.GetPropertyInfo(x => x.ExpireDate); + private static readonly PropertyInfo WriterSelector = ExpressionHelper.GetPropertyInfo(x => x.Writer); /// /// Path to the template used by this Content @@ -132,10 +135,10 @@ namespace Umbraco.Core.Models set { if(value.HasValue && value.Value > DateTime.UtcNow && Published) - ChangePublishedState(false); + _published = false; if (value.HasValue && value.Value < DateTime.UtcNow && !Published) - ChangePublishedState(true); + _published = true; _releaseDate = value; OnPropertyChanged(ReleaseDateSelector); @@ -152,13 +155,27 @@ namespace Umbraco.Core.Models set { if(value.HasValue && DateTime.UtcNow > value.Value && Published) - ChangePublishedState(false); + _published = false; _expireDate = value; OnPropertyChanged(ExpireDateSelector); } } + /// + /// IProfile of the user who wrote/updated this Content + /// + [DataMember] + public virtual IProfile Writer + { + get { return _writer; } + set + { + _writer = value; + OnPropertyChanged(WriterSelector); + } + } + /// /// Gets the ContentType used by this content object /// diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 1f61bce6cd..6caa29d8a1 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using System.Runtime.Serialization; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Membership; namespace Umbraco.Core.Models { @@ -20,7 +21,7 @@ namespace Umbraco.Core.Models private int _sortOrder; private int _level; private string _path; - private int _userId; + private IProfile _user; private bool _trashed; private int _contentTypeId; private PropertyCollection _properties; @@ -44,7 +45,7 @@ namespace Umbraco.Core.Models private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder); private static readonly PropertyInfo LevelSelector = ExpressionHelper.GetPropertyInfo(x => x.Level); private static readonly PropertyInfo PathSelector = ExpressionHelper.GetPropertyInfo(x => x.Path); - private static readonly PropertyInfo UserIdSelector = ExpressionHelper.GetPropertyInfo(x => x.UserId); + private static readonly PropertyInfo UserSelector = ExpressionHelper.GetPropertyInfo(x => x.User); private static readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo(x => x.Trashed); private static readonly PropertyInfo DefaultContentTypeIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeId); private readonly static PropertyInfo PropertyCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.Properties); @@ -136,16 +137,16 @@ namespace Umbraco.Core.Models } /// - /// Id of the user who created this Content + /// IProfile of the user who created this Content /// [DataMember] - public virtual int UserId + public virtual IProfile User { - get { return _userId; } + get { return _user; } set { - _userId = value; - OnPropertyChanged(UserIdSelector); + _user = value; + OnPropertyChanged(UserSelector); } } diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index d66a8bbceb..0c661d6610 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -1,4 +1,5 @@ using System; +using Umbraco.Core.Models.Membership; namespace Umbraco.Core.Models { @@ -33,6 +34,11 @@ namespace Umbraco.Core.Models /// DateTime? ExpireDate { get; set; } + /// + /// Profile of the user who wrote the Content + /// + IProfile Writer { get; set; } + /// /// Gets the ContentType used by this content object /// diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs index db7d469adb..5af5684726 100644 --- a/src/Umbraco.Core/Models/IContentBase.cs +++ b/src/Umbraco.Core/Models/IContentBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Membership; namespace Umbraco.Core.Models { @@ -41,9 +42,9 @@ namespace Umbraco.Core.Models string Path { get; set; } /// - /// Id of the user who created the Content + /// Profile of the user who created the Content /// - int UserId { get; set; } + IProfile User { get; set; } /// /// Boolean indicating whether this Content is Trashed or not. diff --git a/src/Umbraco.Core/Models/Membership/IMembershipUser.cs b/src/Umbraco.Core/Models/Membership/IMembershipUser.cs new file mode 100644 index 0000000000..b54f8d371d --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/IMembershipUser.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Models.Membership +{ + internal interface IMembershipUser : IMembershipUserId + { + object Id { get; set; } + string Username { get; set; } + string Email { get; set; } + string Password { get; set; } + string PasswordQuestion { get; set; } + string PasswordAnswer { get; set; } + string Comments { get; set; } + bool IsApproved { get; set; } + bool IsOnline { get; set; } + bool IsLockedOut { get; set; } + DateTime CreationDate { get; set; } + DateTime LastLoginDate { get; set; } + DateTime LastActivityDate { get; set; } + DateTime LastPasswordChangeDate { get; set; } + DateTime LastLockoutDate { get; set; } + + object ProfileId { get; set; } + IEnumerable Groups { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/IMembershipUserId.cs b/src/Umbraco.Core/Models/Membership/IMembershipUserId.cs new file mode 100644 index 0000000000..03508e1ec8 --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/IMembershipUserId.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Models.Membership +{ + internal interface IMembershipUserId + { + object ProviderUserKey { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/IProfile.cs b/src/Umbraco.Core/Models/Membership/IProfile.cs new file mode 100644 index 0000000000..fbbff85e7d --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/IProfile.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Core.Models.Membership +{ + /// + /// Defines the the Profile interface + /// + public interface IProfile + { + object Id { get; set; } + string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/Profile.cs b/src/Umbraco.Core/Models/Membership/Profile.cs new file mode 100644 index 0000000000..eac7ea3274 --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/Profile.cs @@ -0,0 +1,52 @@ +using System; + +namespace Umbraco.Core.Models.Membership +{ + /// + /// Represents a Profile which is shared between Members and Users + /// + public class Profile : IProfile + { + /// + /// Initializes a new instance of the class. + /// + public Profile() + { + ProviderUserKeyType = typeof(int); + } + + public Profile(object id, string name) + { + ProviderUserKeyType = typeof(int); + Id = id; + Name = name; + } + + public object Id { get; set; } + + public string Name { get; set; } + + internal virtual object ProviderUserKey + { + get { throw new System.NotImplementedException(); } + set { throw new System.NotImplementedException(); } + } + + /// + /// Gets or sets the type of the provider user key. + /// + /// + /// The type of the provider user key. + /// + internal Type ProviderUserKeyType { get; private set; } + + /// + /// Sets the type of the provider user key. + /// + /// The type. + internal void SetProviderUserKeyType(Type type) + { + ProviderUserKeyType = type; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs new file mode 100644 index 0000000000..7aa16cca5d --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/User.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Models.Membership +{ + /// + /// Represents a backoffice user + /// + /// + /// Should be internal until a proper user/membership implementation + /// is part of the roadmap. + /// + internal class User : UserProfile, IMembershipUser + { + #region Implementation of IMembershipUser + + public string Username { get; set; } + public string Email { get; set; } + public string Password { get; set; } + public string PasswordQuestion { get; set; } + public string PasswordAnswer { get; set; } + public string Comments { get; set; } + public bool IsApproved { get; set; } + public bool IsOnline { get; set; } + public bool IsLockedOut { get; set; } + public DateTime CreationDate { get; set; } + public DateTime LastLoginDate { get; set; } + public DateTime LastActivityDate { get; set; } + public DateTime LastPasswordChangeDate { get; set; } + public DateTime LastLockoutDate { get; set; } + + public object ProfileId { get; set; } + public IEnumerable Groups { get; set; } + + #endregion + + #region Implementation of IMembershipUserId + + public new object ProviderUserKey { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/UserProfile.cs b/src/Umbraco.Core/Models/Membership/UserProfile.cs new file mode 100644 index 0000000000..dcb7eb5ddb --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/UserProfile.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Core.Models.Membership +{ + /// + /// Represents the Profile implementation for a backoffice User + /// + /// + /// Should be internal until a proper user/membership implementation + /// is part of the roadmap. + /// + internal class UserProfile : Profile + { + public UserProfile() + { + SessionTimeout = 60; + Applications = Enumerable.Empty(); + } + + /// + /// Gets or sets the session timeout. + /// + /// + /// The session timeout. + /// + public int SessionTimeout { get; set; } + + /// + /// Gets or sets the start content id. + /// + /// + /// The start content id. + /// + public int StartContentId { get; set; } + + /// + /// Gets or sets the start media id. + /// + /// + /// The start media id. + /// + public int StartMediaId { get; set; } + + /// + /// Gets or sets the applications. + /// + /// + /// The applications. + /// + public IEnumerable Applications { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs b/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs index 51b818437f..59391c6551 100644 --- a/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Models.Rdbms public bool Published { get; set; } [Column("documentUser")] - public int UserId { get; set; } + public int WriterUserId { get; set; } [Column("versionId")] [PrimaryKeyColumn(AutoIncrement = false)] diff --git a/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs index 31f5617cce..88bf204034 100644 --- a/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; namespace Umbraco.Core.Persistence.Factories @@ -11,12 +12,22 @@ namespace Umbraco.Core.Persistence.Factories private readonly Guid _nodeObjectTypeId; private readonly int _id; private int _primaryKey; + private readonly IProfile _user; + private readonly IProfile _writer; - public ContentFactory(IContentType contentType, Guid nodeObjectTypeId, int id) + public ContentFactory(IContentType contentType, Guid nodeObjectTypeId, int id, IProfile user, IProfile writer) { _contentType = contentType; _nodeObjectTypeId = nodeObjectTypeId; _id = id; + _user = user; + _writer = writer; + } + + public ContentFactory(Guid nodeObjectTypeId, int id) + { + _nodeObjectTypeId = nodeObjectTypeId; + _id = id; } #region Implementation of IEntityFactory @@ -32,10 +43,8 @@ namespace Umbraco.Core.Persistence.Factories : _id.ToGuid(), Name = dto.ContentVersionDto.ContentDto.NodeDto.Text, Path = dto.ContentVersionDto.ContentDto.NodeDto.Path, - UserId = - dto.ContentVersionDto.ContentDto.NodeDto.UserId.HasValue - ? dto.ContentVersionDto.ContentDto.NodeDto.UserId.Value - : dto.UserId, + User = _user, + Writer = _writer, Level = dto.ContentVersionDto.ContentDto.NodeDto.Level, ParentId = dto.ContentVersionDto.ContentDto.NodeDto.ParentId, SortOrder = dto.ContentVersionDto.ContentDto.NodeDto.SortOrder, @@ -61,7 +70,7 @@ namespace Umbraco.Core.Persistence.Factories ReleaseDate = entity.ReleaseDate, Text = entity.Name, UpdateDate = entity.UpdateDate, - UserId = entity.UserId, + WriterUserId = entity.Writer.Id.SafeCast(), VersionId = entity.Version, ContentVersionDto = BuildContentVersionDto(entity) }; @@ -118,7 +127,7 @@ namespace Umbraco.Core.Persistence.Factories Text = entity.Name, Trashed = entity.Trashed, UniqueId = entity.Key, - UserId = entity.UserId + UserId = entity.User.Id.SafeCast() }; return nodeDto; diff --git a/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs b/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs index b78013881c..56ee508f0b 100644 --- a/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; namespace Umbraco.Core.Persistence.Factories @@ -11,12 +12,20 @@ namespace Umbraco.Core.Persistence.Factories private readonly Guid _nodeObjectTypeId; private readonly int _id; private int _primaryKey; + private readonly IProfile _user; - public MediaFactory(IMediaType contentType, Guid nodeObjectTypeId, int id) + public MediaFactory(IMediaType contentType, Guid nodeObjectTypeId, int id, IProfile user) { _contentType = contentType; _nodeObjectTypeId = nodeObjectTypeId; _id = id; + _user = user; + } + + public MediaFactory(Guid nodeObjectTypeId, int id) + { + _nodeObjectTypeId = nodeObjectTypeId; + _id = id; } #region Implementation of IEntityFactory @@ -32,10 +41,7 @@ namespace Umbraco.Core.Persistence.Factories : _id.ToGuid(), Name = dto.ContentDto.NodeDto.Text, Path = dto.ContentDto.NodeDto.Path, - UserId = - dto.ContentDto.NodeDto.UserId.HasValue - ? dto.ContentDto.NodeDto.UserId.Value - : 0, + User = _user, Level = dto.ContentDto.NodeDto.Level, ParentId = dto.ContentDto.NodeDto.ParentId, SortOrder = dto.ContentDto.NodeDto.SortOrder, @@ -96,7 +102,7 @@ namespace Umbraco.Core.Persistence.Factories Text = entity.Name, Trashed = entity.Trashed, UniqueId = entity.Key, - UserId = entity.UserId + UserId = entity.User.Id.SafeCast() }; return nodeDto; diff --git a/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs index f6fe2f31e3..9dc82bac01 100644 --- a/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs @@ -36,7 +36,7 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.Name, dto => dto.Text); CacheMap(src => src.Trashed, dto => dto.Trashed); CacheMap(src => src.Key, dto => dto.UniqueId); - CacheMap(src => src.UserId, dto => dto.UserId); + CacheMap(src => src.User, dto => dto.UserId); CacheMap(src => src.ContentTypeId, dto => dto.ContentTypeId); CacheMap(src => src.UpdateDate, dto => dto.VersionDate); CacheMap(src => src.Version, dto => dto.VersionId); diff --git a/src/Umbraco.Core/Persistence/Mappers/MediaMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MediaMapper.cs index d2efd03034..cd54d8be4f 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MediaMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MediaMapper.cs @@ -35,7 +35,7 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.Name, dto => dto.Text); CacheMap(src => src.Trashed, dto => dto.Trashed); CacheMap(src => src.Key, dto => dto.UniqueId); - CacheMap(src => src.UserId, dto => dto.UserId); + CacheMap(src => src.User, dto => dto.UserId); CacheMap(src => src.ContentTypeId, dto => dto.ContentTypeId); CacheMap(src => src.UpdateDate, dto => dto.VersionDate); CacheMap(src => src.Version, dto => dto.VersionId); diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index b4fb977091..3f94b40663 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Factories; @@ -46,7 +47,13 @@ namespace Umbraco.Core.Persistence.Repositories var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId); - var factory = new ContentFactory(contentType, NodeObjectTypeId, id); + //NOTE: Should eventually be moved to a UserRepository like is the case with ContentType + var userDto = Database.FirstOrDefault("WHERE id = @Id", new { Id = dto.ContentVersionDto.ContentDto.NodeDto.UserId }); + var user = new Profile(userDto.Id, userDto.UserName); + var writerDto = Database.FirstOrDefault("WHERE id = @Id", new { Id = dto.WriterUserId }); + var writer = new Profile(writerDto.Id, writerDto.UserName); + + var factory = new ContentFactory(contentType, NodeObjectTypeId, id, user, writer); var content = factory.BuildEntity(dto); content.Properties = GetPropertyCollection(id, dto.ContentVersionDto.VersionId, contentType); @@ -141,7 +148,7 @@ namespace Umbraco.Core.Persistence.Repositories { ((Content)entity).AddingEntity(); - var factory = new ContentFactory(null, NodeObjectTypeId, entity.Id); + var factory = new ContentFactory(NodeObjectTypeId, entity.Id); var dto = factory.BuildDto(entity); //NOTE Should the logic below have some kind of fallback for empty parent ids ? @@ -202,7 +209,7 @@ namespace Umbraco.Core.Persistence.Repositories //Updates Modified date and Version Guid ((Content)entity).UpdatingEntity(); - var factory = new ContentFactory(null, NodeObjectTypeId, entity.Id); + var factory = new ContentFactory(NodeObjectTypeId, entity.Id); //Look up Content entry to get Primary for updating the DTO var contentDto = Database.SingleOrDefault("WHERE nodeId = @Id", new { Id = entity.Id }); factory.SetPrimaryKey(contentDto.PrimaryKey); @@ -281,12 +288,18 @@ namespace Umbraco.Core.Persistence.Repositories var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId); - var factory = new ContentFactory(contentType, NodeObjectTypeId, id); + //NOTE: Should eventually be moved to a UserRepository like is the case with ContentType + var userDto = Database.FirstOrDefault("WHERE id = @Id", new { Id = dto.ContentVersionDto.ContentDto.NodeDto.UserId }); + var user = new Profile(userDto.Id, userDto.UserName); + var writerDto = Database.FirstOrDefault("WHERE id = @Id", new { Id = dto.WriterUserId }); + var writer = new Profile(writerDto.Id, writerDto.UserName); + + var factory = new ContentFactory(contentType, NodeObjectTypeId, id, user, writer); var content = factory.BuildEntity(dto); content.Properties = GetPropertyCollection(id, versionId, contentType); - ((Content)content).ResetDirtyProperties(); + ((ICanBeDirty)content).ResetDirtyProperties(); return content; } diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index d7e1835493..b86a23cf34 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Factories; @@ -44,8 +45,12 @@ namespace Umbraco.Core.Persistence.Repositories return null; var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId); + + //NOTE: Should eventually be moved to a UserRepository like is the case with ContentType + var userDto = Database.FirstOrDefault("WHERE id = @Id", new {Id = dto.ContentDto.NodeDto.UserId}); + var user = new Profile(userDto.Id, userDto.UserName); - var factory = new MediaFactory(contentType, NodeObjectTypeId, id); + var factory = new MediaFactory(contentType, NodeObjectTypeId, id, user); var content = factory.BuildEntity(dto); content.Properties = GetPropertyCollection(id, dto.VersionId, contentType); @@ -136,7 +141,7 @@ namespace Umbraco.Core.Persistence.Repositories { ((Models.Media)entity).AddingEntity(); - var factory = new MediaFactory(null, NodeObjectTypeId, entity.Id); + var factory = new MediaFactory(NodeObjectTypeId, entity.Id); var dto = factory.BuildDto(entity); //NOTE Should the logic below have some kind of fallback for empty parent ids ? @@ -189,7 +194,7 @@ namespace Umbraco.Core.Persistence.Repositories //Updates Modified date and Version Guid ((Models.Media)entity).UpdatingEntity(); - var factory = new MediaFactory(null, NodeObjectTypeId, entity.Id); + var factory = new MediaFactory(NodeObjectTypeId, entity.Id); //Look up Content entry to get Primary for updating the DTO var contentDto = Database.SingleOrDefault("WHERE nodeId = @Id", new { Id = entity.Id }); factory.SetPrimaryKey(contentDto.PrimaryKey); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index acdd8b6765..be7079989b 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -98,6 +98,12 @@ + + + + + + diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index a7152cebb9..4d21a45e52 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -2,6 +2,7 @@ using System.Linq; using NUnit.Framework; using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Querying; @@ -173,7 +174,7 @@ namespace Umbraco.Tests.Persistence.Repositories var contentType = contentTypeRepository.Get(1045); var content = new Content(1048, contentType); content.Name = "Textpage 2 Child Node"; - content.UserId = 0; + content.User = new Profile(0, "Administrator"); // Act repository.AddOrUpdate(content); diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 6fc96317f4..03d96d3688 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -57,7 +57,6 @@ namespace Umbraco.Tests.TestHelpers Resolution.Freeze(); ApplicationContext = new ApplicationContext() { IsReady = true }; - ServiceContext = ServiceContext.Current; DatabaseContext = DatabaseContext.Current; //Configure the Database and Sql Syntax based on connection string set in config @@ -70,7 +69,6 @@ namespace Umbraco.Tests.TestHelpers public virtual void TearDown() { //reset the app context - ServiceContext = null; DatabaseContext = null; ApplicationContext.Current = null; Resolution.IsFrozen = false; @@ -87,7 +85,10 @@ namespace Umbraco.Tests.TestHelpers protected ApplicationContext ApplicationContext { get; private set; } - protected ServiceContext ServiceContext { get; private set; } + protected ServiceContext ServiceContext + { + get { return GetUmbracoContext("/test", 1234).ServiceContext; } + } protected DatabaseContext DatabaseContext { get; private set; } diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index c912afa18e..775acbb0fb 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -31,7 +31,6 @@ namespace Umbraco.Tests.TestHelpers TestHelper.InitializeDatabase(); Resolution.Freeze(); ApplicationContext = new ApplicationContext() { IsReady = true }; - ServiceContext = ServiceContext.Current; DatabaseContext = DatabaseContext.Current; //we need to clear out all currently created template files var masterPages = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Masterpages)); @@ -45,7 +44,6 @@ namespace Umbraco.Tests.TestHelpers { //reset the app context ApplicationContext.Current = null; - ServiceContext = null; DatabaseContext = null; Resolution.IsFrozen = false; if (RequiresDbSetup) @@ -79,8 +77,6 @@ namespace Umbraco.Tests.TestHelpers protected ApplicationContext ApplicationContext { get; private set; } - protected ServiceContext ServiceContext { get; private set; } - protected DatabaseContext DatabaseContext { get; private set; } internal virtual IRoutesCache GetRoutesCache() diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs index 3a96f12052..61f9ae4eb6 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs @@ -1,4 +1,5 @@ using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; namespace Umbraco.Tests.TestHelpers.Entities { @@ -6,7 +7,7 @@ namespace Umbraco.Tests.TestHelpers.Entities { public static Content CreateSimpleContent(IContentType contentType) { - var content = new Content(-1, contentType) {Name = "Home", Language = "en-US", Level = 1, ParentId = -1, SortOrder = 1, Template = "~/masterpages/umbTextPage.master", UserId = 0}; + var content = new Content(-1, contentType) { Name = "Home", Language = "en-US", Level = 1, ParentId = -1, SortOrder = 1, Template = "~/masterpages/umbTextPage.master", User = new Profile(0, "Administrator"), Writer = new Profile(0, "Administrator") }; object obj = new { @@ -22,7 +23,7 @@ namespace Umbraco.Tests.TestHelpers.Entities public static Content CreateSimpleContent(IContentType contentType, string name, int parentId) { - var content = new Content(parentId, contentType) { Name = name, Language = "en-US", ParentId = parentId, Template = "~/masterpages/umbTextPage.master", UserId = 0 }; + var content = new Content(parentId, contentType) { Name = name, Language = "en-US", ParentId = parentId, Template = "~/masterpages/umbTextPage.master", User = new Profile(0, "Administrator"), Writer = new Profile(0, "Administrator") }; object obj = new { @@ -38,7 +39,7 @@ namespace Umbraco.Tests.TestHelpers.Entities public static Content CreateTextpageContent(IContentType contentType, string name, int parentId) { - var content = new Content(parentId, contentType) { Name = name, Language = "en-US", ParentId = parentId, Template = "~/masterpages/umbTextPage.master", UserId = 0 }; + var content = new Content(parentId, contentType) { Name = name, Language = "en-US", ParentId = parentId, Template = "~/masterpages/umbTextPage.master", User = new Profile(0, "Administrator"), Writer = new Profile(0, "Administrator")}; object obj = new { diff --git a/src/Umbraco.Web/ApplicationContextExtensions.cs b/src/Umbraco.Web/ApplicationContextExtensions.cs index 703f9f0351..d9902147e6 100644 --- a/src/Umbraco.Web/ApplicationContextExtensions.cs +++ b/src/Umbraco.Web/ApplicationContextExtensions.cs @@ -2,7 +2,6 @@ using System.IO; using System.Web; using Umbraco.Core; -using Umbraco.Web.Services; namespace Umbraco.Web { @@ -28,16 +27,6 @@ namespace Umbraco.Web File.SetLastWriteTimeUtc(configPath, DateTime.UtcNow); } - /// - /// Adds the ServiceContext to the ApplicationContext - /// - /// - /// - public static ServiceContext ServiceContext(this ApplicationContext appContext) - { - return Services.ServiceContext.Current; - } - /// /// Adds the DatabaseContext to the ApplicationContext /// diff --git a/src/Umbraco.Web/Publishing/BasePublishingStrategy.cs b/src/Umbraco.Web/Publishing/BasePublishingStrategy.cs index d029b77064..87a62f6883 100644 --- a/src/Umbraco.Web/Publishing/BasePublishingStrategy.cs +++ b/src/Umbraco.Web/Publishing/BasePublishingStrategy.cs @@ -12,77 +12,72 @@ namespace Umbraco.Web.Publishing public abstract bool UnPublish(IEnumerable content, int userId); /// - /// The publish event handler + /// The publishing event handler used for publish and unpublish events /// - public delegate void PublishEventHandler(IContent sender, PublishEventArgs e); - - /// - /// The unpublish event handler - /// - public delegate void UnPublishEventHandler(IContent sender, UnPublishEventArgs e); + public delegate void PublishingEventHandler(IContent sender, PublishingEventArgs e); /// /// Occurs before publish /// - public static event PublishEventHandler BeforePublish; + public static event PublishingEventHandler Publishing; /// - /// Raises the event + /// Raises the event /// /// /// The instance containing the event data. - protected virtual void FireBeforePublish(IContent content, PublishEventArgs e) + protected virtual void OnPublish(IContent content, PublishingEventArgs e) { - if (BeforePublish != null) - BeforePublish(content, e); + if (Publishing != null) + Publishing(content, e); } /// /// Occurs after publish /// - public static event PublishEventHandler AfterPublish; + public static event PublishingEventHandler Published; /// - /// Raises the event + /// Raises the event /// /// /// The instance containing the event data. - protected virtual void FireAfterPublish(IContent content, PublishEventArgs e) + protected virtual void OnPublished(IContent content, PublishingEventArgs e) { - if (AfterPublish != null) - AfterPublish(content, e); + if (Published != null) + Published(content, e); } /// /// Occurs before unpublish /// - public static event UnPublishEventHandler BeforeUnPublish; + public static event PublishingEventHandler UnPublishing; /// - /// Raises the event + /// Raises the event /// /// /// The instance containing the event data. - protected virtual void FireBeforeUnPublish(IContent content, UnPublishEventArgs e) + protected virtual void OnUnPublish(IContent content, PublishingEventArgs e) { - if (BeforeUnPublish != null) - BeforeUnPublish(content, e); + if (UnPublishing != null) + UnPublishing(content, e); } /// /// Occurs after unpublish /// - public static event UnPublishEventHandler AfterUnPublish; + public static event PublishingEventHandler UnPublished; /// - /// Raises the event + /// Raises the event /// /// /// The instance containing the event data. - protected virtual void FireAfterUnPublish(IContent content, UnPublishEventArgs e) + protected virtual void OnUnPublished(IContent content, PublishingEventArgs e) { - if (AfterUnPublish != null) - AfterUnPublish(content, e); + if (UnPublished != null) + UnPublished(content, e); } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Publishing/PublishingStrategy.cs b/src/Umbraco.Web/Publishing/PublishingStrategy.cs index bba152335e..ef3b0b4d7d 100644 --- a/src/Umbraco.Web/Publishing/PublishingStrategy.cs +++ b/src/Umbraco.Web/Publishing/PublishingStrategy.cs @@ -24,9 +24,9 @@ namespace Umbraco.Web.Publishing /// True if the publish operation was successfull and not cancelled, otherwise false public override bool Publish(IContent content, int userId) { - var e = new PublishEventArgs(); - //Fire BeforePublish event - FireBeforePublish(content, e); + var e = new PublishingEventArgs(); + //Fire Publishing event + OnPublish(content, e); if (!e.Cancel) { @@ -63,8 +63,8 @@ namespace Umbraco.Web.Publishing string.Format("Content '{0}' with Id '{1}' has been published.", content.Name, content.Id)); - //Fire AfterPublish event - FireAfterPublish(content, e); + //Fire Published event + OnPublished(content, e); //NOTE: Ideally the xml cache should be refreshed here - as part of the publishing @@ -82,15 +82,15 @@ namespace Umbraco.Web.Publishing /// True if the publish operation was successfull and not cancelled, otherwise false public override bool PublishWithChildren(IEnumerable content, int userId) { - var e = new PublishEventArgs(); + var e = new PublishingEventArgs(); /* Only update content thats not already been published - we want to loop through * all unpublished content to write skipped content (expired and awaiting release) to log. */ foreach (var item in content.Where(x => x.Published == false)) { - //Fire BeforePublish event - FireBeforePublish(item, e); + //Fire Publishing event + OnPublish(item, e); if (e.Cancel) return false; @@ -127,8 +127,8 @@ namespace Umbraco.Web.Publishing string.Format("Content '{0}' with Id '{1}' has been published.", item.Name, item.Id)); - //Fire AfterPublish event - FireAfterPublish(item, e); + //Fire Published event + OnPublished(item, e); } //NOTE: Ideally the xml cache should be refreshed here - as part of the publishing @@ -144,9 +144,9 @@ namespace Umbraco.Web.Publishing /// True if the unpublish operation was successfull and not cancelled, otherwise false public override bool UnPublish(IContent content, int userId) { - var e = new UnPublishEventArgs(); + var e = new PublishingEventArgs(); //Fire BeforeUnPublish event - FireBeforeUnPublish(content, e); + OnUnPublish(content, e); if (!e.Cancel) { @@ -168,8 +168,8 @@ namespace Umbraco.Web.Publishing string.Format("Content '{0}' with Id '{1}' has been unpublished.", content.Name, content.Id)); - //Fire AfterUnPublish event - FireAfterUnPublish(content, e); + //Fire UnPublishing event + OnUnPublished(content, e); //NOTE: Ideally the xml cache should be refreshed here - as part of the unpublishing @@ -187,13 +187,13 @@ namespace Umbraco.Web.Publishing /// True if the unpublish operation was successfull and not cancelled, otherwise false public override bool UnPublish(IEnumerable content, int userId) { - var e = new UnPublishEventArgs(); + var e = new PublishingEventArgs(); //Only update content thats already been published foreach (var item in content.Where(x => x.Published == true)) { - //Fire BeforeUnPublish event - FireBeforeUnPublish(item, e); + //Fire UnPublished event + OnUnPublish(item, e); if (e.Cancel) return false; @@ -215,7 +215,7 @@ namespace Umbraco.Web.Publishing item.Name, item.Id)); //Fire AfterUnPublish event - FireAfterUnPublish(item, e); + OnUnPublished(item, e); } //NOTE: Ideally the xml cache should be refreshed here - as part of the publishing diff --git a/src/Umbraco.Web/Services/ContentService.cs b/src/Umbraco.Web/Services/ContentService.cs index b07fd2fa4e..97bf7a5e36 100644 --- a/src/Umbraco.Web/Services/ContentService.cs +++ b/src/Umbraco.Web/Services/ContentService.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Web; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; @@ -21,6 +23,7 @@ namespace Umbraco.Web.Services private readonly IUnitOfWorkProvider _provider; private readonly IPublishingStrategy _publishingStrategy; private readonly IUnitOfWork _unitOfWork; + private readonly IUserService _userService; public ContentService() : this(new PetaPocoUnitOfWorkProvider()) { @@ -31,11 +34,16 @@ namespace Umbraco.Web.Services } - public ContentService(IUnitOfWorkProvider provider, IPublishingStrategy publishingStrategy) + public ContentService(IUnitOfWorkProvider provider, IPublishingStrategy publishingStrategy) : this(provider, publishingStrategy, null) + { + } + + public ContentService(IUnitOfWorkProvider provider, IPublishingStrategy publishingStrategy, IUserService userService) { _provider = provider; _publishingStrategy = publishingStrategy; _unitOfWork = provider.GetUnitOfWork(); + _userService = userService; } /// @@ -44,8 +52,9 @@ namespace Umbraco.Web.Services /// /// Id of Parent for content /// Alias of the + /// Optional id of the user creating the content /// - public IContent CreateContent(int parentId, string contentTypeAlias) + public IContent CreateContent(int parentId, string contentTypeAlias, int userId = -1) { var repository = RepositoryResolver.ResolveByType(_unitOfWork); var query = Query.Builder.Where(x => x.Alias == contentTypeAlias); @@ -59,7 +68,10 @@ namespace Umbraco.Web.Services if (contentType == null) throw new Exception(string.Format("ContentType matching the passed in Alias: '{0}' was null", contentTypeAlias)); - return new Content(parentId, contentType); + var content = new Content(parentId, contentType); + SetUser(content, userId); + SetWriter(content, userId); + return content; } /// @@ -189,9 +201,9 @@ namespace Umbraco.Web.Services /// /// Re-Publishes all Content /// - /// Id of the User issueing the publishing + /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False - public bool RePublishAll(int userId) + public bool RePublishAll(int userId = -1) { var repository = RepositoryResolver.ResolveByType(_unitOfWork); @@ -217,6 +229,7 @@ namespace Umbraco.Web.Services //Only loop through content where the Published property has been updated foreach (var item in list.Where(x => ((ICanBeDirty)x).IsPropertyDirty("Published"))) { + SetWriter(item, userId); repository.AddOrUpdate(item); } @@ -233,9 +246,9 @@ namespace Umbraco.Web.Services /// Publishes a single object /// /// The to publish - /// Id of the User issueing the publishing + /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False - public bool Publish(IContent content, int userId) + public bool Publish(IContent content, int userId = -1) { return SaveAndPublish(content, userId); } @@ -244,9 +257,9 @@ namespace Umbraco.Web.Services /// Publishes a object and all its children /// /// The to publish along with its children - /// Id of the User issueing the publishing + /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False - public bool PublishWithChildren(IContent content, int userId) + public bool PublishWithChildren(IContent content, int userId = -1) { var repository = RepositoryResolver.ResolveByType(_unitOfWork); @@ -282,6 +295,7 @@ namespace Umbraco.Web.Services //Only loop through content where the Published property has been updated foreach (var item in list.Where(x => ((ICanBeDirty)x).IsPropertyDirty("Published"))) { + SetWriter(item, userId); repository.AddOrUpdate(item); } @@ -299,9 +313,9 @@ namespace Umbraco.Web.Services /// UnPublishes a single object /// /// The to publish - /// Id of the User issueing the publishing + /// Optional Id of the User issueing the publishing /// True if unpublishing succeeded, otherwise False - public bool UnPublish(IContent content, int userId) + public bool UnPublish(IContent content, int userId = -1) { var repository = RepositoryResolver.ResolveByType(_unitOfWork); @@ -324,6 +338,7 @@ namespace Umbraco.Web.Services { foreach (var child in children) { + SetWriter(child, userId); repository.AddOrUpdate(child); } } @@ -367,9 +382,9 @@ namespace Umbraco.Web.Services /// Saves and Publishes a single object /// /// The to save and publish - /// Id of the User issueing the publishing + /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False - public bool SaveAndPublish(IContent content, int userId) + public bool SaveAndPublish(IContent content, int userId = -1) { var repository = RepositoryResolver.ResolveByType(_unitOfWork); @@ -395,6 +410,7 @@ namespace Umbraco.Web.Services bool published = _publishingStrategy.Publish(content, userId); if (published) { + SetWriter(content, userId); repository.AddOrUpdate(content); _unitOfWork.Commit(); @@ -409,10 +425,13 @@ namespace Umbraco.Web.Services /// Saves a single object /// /// The to save - /// Id of the User saving the Content - public void Save(IContent content, int userId) + /// Optional Id of the User saving the Content + public void Save(IContent content, int userId = -1) { var repository = RepositoryResolver.ResolveByType(_unitOfWork); + + SetWriter(content, userId); + repository.AddOrUpdate(content); _unitOfWork.Commit(); } @@ -421,12 +440,13 @@ namespace Umbraco.Web.Services /// Saves a collection of objects /// /// Collection of to save - /// Id of the User saving the Content - public void Save(IEnumerable contents, int userId) + /// Optional Id of the User saving the Content + public void Save(IEnumerable contents, int userId = -1) { var repository = RepositoryResolver.ResolveByType(_unitOfWork); foreach (var content in contents) { + SetWriter(content, userId); repository.AddOrUpdate(content); } _unitOfWork.Commit(); @@ -459,13 +479,14 @@ namespace Umbraco.Web.Services /// /// Please note that this method will completely remove the Content from the database /// The to delete - /// Id of the User deleting the Content - public void Delete(IContent content, int userId) + /// Optional Id of the User deleting the Content + public void Delete(IContent content, int userId = -1) { //TODO Ensure that content is unpublished when deleted //TODO This method should handle/react to errors when there is a constraint issue with the content being deleted //TODO Children should either be deleted or moved to the recycle bin var repository = RepositoryResolver.ResolveByType(_unitOfWork); + SetWriter(content, userId); repository.Delete(content); _unitOfWork.Commit(); } @@ -475,12 +496,13 @@ namespace Umbraco.Web.Services /// /// Move an item to the Recycle Bin will result in the item being unpublished /// The to delete - /// Id of the User deleting the Content - public void MoveToRecycleBin(IContent content, int userId) + /// Optional Id of the User deleting the Content + public void MoveToRecycleBin(IContent content, int userId = -1) { //TODO If content item has children those should also be moved to the recycle bin //TODO Unpublish deleted content + children var repository = RepositoryResolver.ResolveByType(_unitOfWork); + SetWriter(content, userId); content.ChangeTrashedState(true); repository.AddOrUpdate(content); _unitOfWork.Commit(); @@ -496,9 +518,11 @@ namespace Umbraco.Web.Services /// /// The to move /// Id of the Content's new Parent - /// Id of the User moving the Content - public void Move(IContent content, int parentId, int userId) + /// Optional Id of the User moving the Content + public void Move(IContent content, int parentId, int userId = -1) { + SetWriter(content, userId); + //If Content is being moved away from Recycle Bin, its state should be un-trashed if(content.Trashed && parentId != -20) { @@ -543,9 +567,9 @@ namespace Umbraco.Web.Services /// /// The to copy /// Id of the Content's new Parent - /// Id of the User copying the Content + /// Optional Id of the User copying the Content /// The newly created object - public IContent Copy(IContent content, int parentId, int userId) + public IContent Copy(IContent content, int parentId, int userId = -1) { var copy = ((Content) content).Clone(); copy.ParentId = parentId; @@ -553,6 +577,8 @@ namespace Umbraco.Web.Services var repository = RepositoryResolver.ResolveByType(_unitOfWork); + SetWriter(content, userId); + repository.AddOrUpdate(copy); _unitOfWork.Commit(); @@ -563,9 +589,9 @@ namespace Umbraco.Web.Services /// Sends an to Publication, which executes handlers and events for the 'Send to Publication' action. /// /// The to send to publication - /// Id of the User issueing the send to publication + /// Optional Id of the User issueing the send to publication /// True if sending publication was succesfull otherwise false - public bool SendToPublication(IContent content, int userId) + public bool SendToPublication(IContent content, int userId = -1) { //TODO Implement something similar to this /*SendToPublishEventArgs e = new SendToPublishEventArgs(); @@ -592,17 +618,70 @@ namespace Umbraco.Web.Services /// /// Id of the being rolled back /// Id of the version to rollback to - /// Id of the User issueing the rollback of the Content + /// Optional Id of the User issueing the rollback of the Content /// The newly created object - public IContent Rollback(int id, Guid versionId, int userId) + public IContent Rollback(int id, Guid versionId, int userId = -1) { var repository = RepositoryResolver.ResolveByType(_unitOfWork); var content = repository.GetByVersion(id, versionId); + SetUser(content, userId); + SetWriter(content, userId); + repository.AddOrUpdate(content); _unitOfWork.Commit(); return content; } + + /// + /// Updates a content object with the User (id), who created the content. + /// + /// object to update + /// Optional Id of the User + private void SetUser(IContent content, int userId) + { + if(userId > -1) + { + //If a user id was passed in we use that + content.User = new Profile(userId, ""); + } + else if(_userService != null) + { + //If the UserService has been set for this instance of the ServiceContext + //we retrieve the current users id from there. + content.User = _userService.GetCurrentBackOfficeUser(); + } + else + { + //Otherwise we default to Admin user, which should always exist (almost always) + content.User = new Profile(0, "Administrator"); + } + } + + /// + /// Updates a content object with a Writer (user id), who updated the content. + /// + /// object to update + /// Optional Id of the Writer + private void SetWriter(IContent content, int userId) + { + if (userId > -1) + { + //If a user id was passed in we use that + content.Writer = new Profile(userId, ""); + } + else if (_userService != null) + { + //If the UserService has been set for this instance of the ServiceContext + //we retrieve the current users id from there. + content.Writer = _userService.GetCurrentBackOfficeUser(); + } + else + { + //Otherwise we default to Admin user, which should always exist (almost always) + content.Writer = new Profile(0, "Administrator"); + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Services/IContentService.cs b/src/Umbraco.Web/Services/IContentService.cs index 46f9b8b5bd..7b9359e2ab 100644 --- a/src/Umbraco.Web/Services/IContentService.cs +++ b/src/Umbraco.Web/Services/IContentService.cs @@ -15,8 +15,9 @@ namespace Umbraco.Web.Services /// /// Id of Parent for content /// Alias of the + /// Optional id of the user creating the content /// - IContent CreateContent(int parentId, string contentTypeAlias); + IContent CreateContent(int parentId, string contentTypeAlias, int userId = -1); //TODO Add CreateNewVersion method? Its currently used in the Document API when Publishing - latest version is published, //but then a new version is created so latest version is not published. @@ -84,55 +85,55 @@ namespace Umbraco.Web.Services /// /// Re-Publishes all Content /// - /// Id of the User issueing the publishing + /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False - bool RePublishAll(int userId); + bool RePublishAll(int userId = -1); /// /// Publishes a single object /// /// The to publish - /// Id of the User issueing the publishing + /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False - bool Publish(IContent content, int userId); + bool Publish(IContent content, int userId = -1); /// /// Publishes a object and all its children /// /// The to publish along with its children - /// Id of the User issueing the publishing + /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False - bool PublishWithChildren(IContent content, int userId); + bool PublishWithChildren(IContent content, int userId = -1); /// /// UnPublishes a single object /// /// The to publish - /// Id of the User issueing the publishing + /// Optional Id of the User issueing the publishing /// True if unpublishing succeeded, otherwise False - bool UnPublish(IContent content, int userId); + bool UnPublish(IContent content, int userId = -1); /// /// Saves and Publishes a single object /// /// The to save and publish - /// Id of the User issueing the publishing + /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False - bool SaveAndPublish(IContent content, int userId); + bool SaveAndPublish(IContent content, int userId = -1); /// /// Saves a single object /// /// The to save - /// Id of the User saving the Content - void Save(IContent content, int userId); + /// Optional Id of the User saving the Content + void Save(IContent content, int userId = -1); /// /// Saves a collection of objects /// /// Collection of to save - /// Id of the User saving the Content - void Save(IEnumerable contents, int userId); + /// Optional Id of the User saving the Content + void Save(IEnumerable contents, int userId = -1); /// /// Deletes all content of specified type. All children of deleted content is moved to Recycle Bin. @@ -146,24 +147,24 @@ namespace Umbraco.Web.Services /// /// Please note that this method will completely remove the Content from the database /// The to delete - /// Id of the User deleting the Content - void Delete(IContent content, int userId); + /// Optional Id of the User deleting the Content + void Delete(IContent content, int userId = -1); /// /// Deletes an object by moving it to the Recycle Bin /// /// Move an item to the Recycle Bin will result in the item being unpublished /// The to delete - /// Id of the User deleting the Content - void MoveToRecycleBin(IContent content, int userId); + /// Optional Id of the User deleting the Content + void MoveToRecycleBin(IContent content, int userId = -1); /// /// Moves an object to a new location /// /// The to move /// Id of the Content's new Parent - /// Id of the User moving the Content - void Move(IContent content, int parentId, int userId); + /// Optional Id of the User moving the Content + void Move(IContent content, int parentId, int userId = -1); /// /// Empties the Recycle Bin by deleting all that resides in the bin @@ -176,17 +177,17 @@ namespace Umbraco.Web.Services /// /// The to copy /// Id of the Content's new Parent - /// Id of the User copying the Content + /// Optional Id of the User copying the Content /// The newly created object - IContent Copy(IContent content, int parentId, int userId); + IContent Copy(IContent content, int parentId, int userId = -1); /// /// Sends an to Publication, which executes handlers and events for the 'Send to Publication' action. /// /// The to send to publication - /// Id of the User issueing the send to publication + /// Optional Id of the User issueing the send to publication /// True if sending publication was succesfull otherwise false - bool SendToPublication(IContent content, int userId); + bool SendToPublication(IContent content, int userId = -1); /// /// Rollback an object to a previous version. @@ -194,8 +195,8 @@ namespace Umbraco.Web.Services /// /// Id of the being rolled back /// Id of the version to rollback to - /// Id of the User issueing the rollback of the Content + /// Optional Id of the User issueing the rollback of the Content /// The newly created object - IContent Rollback(int id, Guid versionId, int userId); + IContent Rollback(int id, Guid versionId, int userId = -1); } } \ No newline at end of file diff --git a/src/Umbraco.Web/Services/IUserService.cs b/src/Umbraco.Web/Services/IUserService.cs new file mode 100644 index 0000000000..95c2c2a73d --- /dev/null +++ b/src/Umbraco.Web/Services/IUserService.cs @@ -0,0 +1,16 @@ +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Web.Services +{ + /// + /// Defines the UserService, which is an easy access to operations involving and eventually Users and Members. + /// + public interface IUserService : IService + { + /// + /// Gets an for the current BackOffice User + /// + /// containing the Name and Id of the logged in BackOffice User + IProfile GetCurrentBackOfficeUser(); + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Services/ServiceContext.cs b/src/Umbraco.Web/Services/ServiceContext.cs index 0ccade4292..bba2e2e87d 100644 --- a/src/Umbraco.Web/Services/ServiceContext.cs +++ b/src/Umbraco.Web/Services/ServiceContext.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Concurrent; +using System.Web; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Web.Publishing; @@ -12,19 +13,19 @@ namespace Umbraco.Web.Services /// public class ServiceContext { - #region Singleton - private static readonly Lazy lazy = new Lazy(() => new ServiceContext()); - - public static ServiceContext Current { get { return lazy.Value; } } - - private ServiceContext() + internal ServiceContext(HttpContextBase httpContext) { - if(_cache.IsEmpty) + _httpContext = httpContext; + + if (_cache.IsEmpty) { BuildServiceCache(); } } - #endregion + + public static ServiceContext Current { get; internal set; } + + private readonly HttpContextBase _httpContext; private readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); @@ -37,7 +38,9 @@ namespace Umbraco.Web.Services var fileProvider = new FileUnitOfWorkProvider(); var publishingStrategy = new PublishingStrategy(); - var contentService = new ContentService(provider, publishingStrategy); + var userService = new UserService(_httpContext); + + var contentService = new ContentService(provider, publishingStrategy, userService); _cache.AddOrUpdate(typeof (IContentService).Name, contentService, (x, y) => contentService); var mediaService = new MediaService(provider); diff --git a/src/Umbraco.Web/Services/ServiceFactory.cs b/src/Umbraco.Web/Services/ServiceFactory.cs index db4432d869..298df0365d 100644 --- a/src/Umbraco.Web/Services/ServiceFactory.cs +++ b/src/Umbraco.Web/Services/ServiceFactory.cs @@ -6,12 +6,14 @@ /// public static class ServiceFactory { + private static readonly ServiceContext ServiceContext = new ServiceContext(null); + /// /// Gets the /// public static IContentService ContentService { - get { return ServiceContext.Current.ContentService; } + get { return ServiceContext.ContentService; } } /// @@ -19,7 +21,7 @@ /// public static IContentTypeService ContentTypeService { - get { return ServiceContext.Current.ContentTypeService; } + get { return ServiceContext.ContentTypeService; } } /// @@ -27,7 +29,7 @@ /// public static IDataTypeService DataTypeService { - get { return ServiceContext.Current.DataTypeService; } + get { return ServiceContext.DataTypeService; } } /// @@ -35,7 +37,7 @@ /// public static IFileService FileService { - get { return ServiceContext.Current.FileService; } + get { return ServiceContext.FileService; } } /// @@ -43,7 +45,7 @@ /// public static ILocalizationService LocalizationService { - get { return ServiceContext.Current.LocalizationService; } + get { return ServiceContext.LocalizationService; } } /// @@ -51,7 +53,7 @@ /// public static IMediaService MediaService { - get { return ServiceContext.Current.MediaService; } + get { return ServiceContext.MediaService; } } /// @@ -59,7 +61,7 @@ /// public static IMacroService MacroService { - get { return ServiceContext.Current.MacroService; } + get { return ServiceContext.MacroService; } } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Services/UserService.cs b/src/Umbraco.Web/Services/UserService.cs new file mode 100644 index 0000000000..fd37ce92e6 --- /dev/null +++ b/src/Umbraco.Web/Services/UserService.cs @@ -0,0 +1,76 @@ +using System; +using System.Web; +using Umbraco.Core; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence; +using umbraco; + +namespace Umbraco.Web.Services +{ + /// + /// Represents the UserService, which is an easy access to operations involving and eventually Users and Members. + /// + public class UserService : IUserService + { + private readonly HttpContextBase _httpContext; + + public UserService(HttpContextBase httpContext) + { + _httpContext = httpContext; + } + + #region Implementation of IUserService + + /// + /// Gets an for the current BackOffice User + /// + /// containing the Name and Id of the logged in BackOffice User + public IProfile GetCurrentBackOfficeUser() + { + var cookie = _httpContext.Request.Cookies["UMB_UCONTEXT"]; + + Mandate.That(cookie != null, () => new ArgumentException("The Cookie containing the UserContext Guid Id was null", "Cookie")); + + string contextId = cookie.Value; + string cacheKey = string.Concat("UmbracoUserContext", contextId); + + int userId = 0; + + if(HttpRuntime.Cache[cacheKey] == null) + { + userId = + DatabaseFactory.Current.Database.ExecuteScalar( + "select userID from umbracoUserLogins where contextID = @ContextId", + new {ContextId = new Guid(contextId)}); + + HttpRuntime.Cache.Insert(cacheKey, userId, + null, + System.Web.Caching.Cache.NoAbsoluteExpiration, + new TimeSpan(0, (int) (GlobalSettings.TimeOutInMinutes/10), 0)); + } + else + { + userId = (int) HttpRuntime.Cache[cacheKey]; + } + + //Not too happy with this db lookup, but it'll improve once there is a UserRepository with caching + var userDto = GetUserById(userId); + + var profile = new Profile(userId, userDto.UserName); + return profile; + } + + /// + /// Gets a User dto from the database. + /// + /// Id of the User to retrieve + /// + private UserDto GetUserById(int id) + { + return DatabaseFactory.Current.Database.FirstOrDefault("WHERE id = @Id", new {Id = id}); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index b4d76c4dab..563de6f82f 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -327,11 +327,13 @@ + + ASPXCodeBehind diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index bfdcaf0622..dc391317b8 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -50,6 +50,7 @@ namespace Umbraco.Web HttpContext = httpContext; Application = applicationContext; + ServiceContext = new ServiceContext(httpContext); RoutesCache = routesCache; // set the urls... @@ -105,6 +106,11 @@ namespace Umbraco.Web /// public ApplicationContext Application { get; private set; } + /// + /// Gets the current ServiceContext + /// + public ServiceContext ServiceContext { get; private set; } + /// /// Gets the ///