diff --git a/src/Umbraco.Core/Models/EntityBase/Entity.cs b/src/Umbraco.Core/Models/EntityBase/Entity.cs index fca289234a..8a9521c23c 100644 --- a/src/Umbraco.Core/Models/EntityBase/Entity.cs +++ b/src/Umbraco.Core/Models/EntityBase/Entity.cs @@ -171,19 +171,6 @@ namespace Umbraco.Core.Models.EntityBase } } - public static bool operator ==(Entity left, Entity right) - { - if (ReferenceEquals(null, left)) - return false; - - return left.Equals(right); - } - - public static bool operator !=(Entity left, Entity right) - { - return !(left == right); - } - public virtual bool SameIdentityAs(IEntity other) { if (ReferenceEquals(null, other)) diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index 512fc993ce..c723710e7a 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -14,7 +14,6 @@ namespace Umbraco.Core.Models [DataContract(IsReference = true)] public class PropertyType : Entity, IEquatable { - //private SerializationService _service; private string _name; private string _alias; private string _description; @@ -34,22 +33,12 @@ namespace Umbraco.Core.Models DataTypeId = dataTypeDefinition.ControlId; DataTypeDatabaseType = dataTypeDefinition.DatabaseType; - - EnsureSerializationService(); } internal PropertyType(Guid dataTypeControlId, DataTypeDatabaseType dataTypeDatabaseType) { DataTypeId = dataTypeControlId; DataTypeDatabaseType = dataTypeDatabaseType; - - EnsureSerializationService(); - } - - private void EnsureSerializationService() - { - /*var serviceStackSerializer = new ServiceStackXmlSerializer(); - _service = new SerializationService(serviceStackSerializer);*/ } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); @@ -206,6 +195,7 @@ namespace Umbraco.Core.Models /// Gets of Sets the Help text for the current PropertyType /// [DataMember] + [Obsolete("Not used anywhere in the UI")] public string HelpText { get { return _helpText; } @@ -240,7 +230,7 @@ namespace Umbraco.Core.Models /// Gets or Sets the RegEx for validation of legacy DataTypes /// [DataMember] - internal string ValidationRegExp + public string ValidationRegExp { get { return _validationRegExp; } set @@ -252,7 +242,6 @@ namespace Umbraco.Core.Models }, _validationRegExp, ValidationRegExpSelector); } } - //Note that Validation should eventually come from PropertyEditors like in v5 /// /// Create a new Property object from a "raw" database value. diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs index 24f5062150..59aed21755 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs @@ -11,6 +11,8 @@ namespace Umbraco.Core.Persistence.Factories private readonly IContentTypeComposition _contentType; private readonly Guid _version; private readonly int _id; + private readonly DateTime _createDate; + private readonly DateTime _updateDate; public PropertyFactory(IContentTypeComposition contentType, Guid version, int id) { @@ -19,6 +21,15 @@ namespace Umbraco.Core.Persistence.Factories _id = id; } + public PropertyFactory(IContentTypeComposition contentType, Guid version, int id, DateTime createDate, DateTime updateDate) + { + _contentType = contentType; + _version = version; + _id = id; + _createDate = createDate; + _updateDate = updateDate; + } + #region Implementation of IEntityFactory public IEnumerable BuildEntity(IEnumerable dtos) @@ -34,6 +45,8 @@ namespace Umbraco.Core.Persistence.Factories propertyDataDto.VersionId.Value, propertyDataDto.Id); //on initial construction we don't want to have dirty properties tracked + property.CreateDate = _createDate; + property.UpdateDate = _updateDate; // http://issues.umbraco.org/issue/U4-1946 property.ResetDirtyProperties(false); properties.Add(property); diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs index 68d3a0c299..57cc79bc32 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs @@ -9,12 +9,21 @@ namespace Umbraco.Core.Persistence.Factories internal class PropertyGroupFactory : IEntityFactory, IEnumerable> { private readonly int _id; + private readonly DateTime _createDate; + private readonly DateTime _updateDate; public PropertyGroupFactory(int id) { _id = id; } + public PropertyGroupFactory(int id, DateTime createDate, DateTime updateDate) + { + _id = id; + _createDate = createDate; + _updateDate = updateDate; + } + #region Implementation of IEntityFactory,IEnumerable> public IEnumerable BuildEntity(IEnumerable dto) @@ -58,7 +67,9 @@ namespace Umbraco.Core.Persistence.Factories Mandatory = typeDto.Mandatory, SortOrder = typeDto.SortOrder, ValidationRegExp = typeDto.ValidationRegExp, - PropertyGroupId = new Lazy(() => tempGroupDto.Id) + PropertyGroupId = new Lazy(() => tempGroupDto.Id), + CreateDate = _createDate, + UpdateDate = _updateDate }; //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index dd7de589c8..272dd6d4d4 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -401,6 +401,24 @@ namespace Umbraco.Core.Persistence.Repositories #region Implementation of IContentRepository + public IEnumerable GetByPublishedVersion(IQuery query) + { + var sqlClause = GetBaseQuery(false); + var translator = new SqlTranslator(sqlClause, query); + var sql = translator.Translate() + .Where(x => x.Published) + .OrderByDescending(x => x.VersionDate) + .OrderBy(x => x.SortOrder); + + //NOTE: This doesn't allow properties to be part of the query + var dtos = Database.Fetch(sql); + + foreach (var dto in dtos) + { + yield return CreateContentFromDto(dto, dto.VersionId); + } + } + public IContent GetByLanguage(int id, string language) { var sql = GetBaseQuery(false); @@ -437,7 +455,7 @@ namespace Umbraco.Core.Persistence.Repositories content.Template = _templateRepository.Get(dto.TemplateId.Value); } - content.Properties = GetPropertyCollection(dto.NodeId, versionId, contentType); + content.Properties = GetPropertyCollection(dto.NodeId, versionId, contentType, content.CreateDate, content.UpdateDate); //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 @@ -445,7 +463,7 @@ namespace Umbraco.Core.Persistence.Repositories return content; } - private PropertyCollection GetPropertyCollection(int id, Guid versionId, IContentType contentType) + private PropertyCollection GetPropertyCollection(int id, Guid versionId, IContentType contentType, DateTime createDate, DateTime updateDate) { var sql = new Sql(); sql.Select("*") @@ -456,7 +474,7 @@ namespace Umbraco.Core.Persistence.Repositories .Where(x => x.VersionId == versionId); var propertyDataDtos = Database.Fetch(sql); - var propertyFactory = new PropertyFactory(contentType, versionId, id); + var propertyFactory = new PropertyFactory(contentType, versionId, id, createDate, updateDate); var properties = propertyFactory.BuildEntity(propertyDataDtos); var newProperties = properties.Where(x => x.HasIdentity == false); diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 19ec4970e3..fdac85950b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -326,7 +326,7 @@ namespace Umbraco.Core.Persistence.Repositories return allowedContentTypeDtos.Select(x => new ContentTypeSort { Id = new Lazy(() => x.AllowedId), SortOrder = x.SortOrder }).ToList(); } - protected PropertyGroupCollection GetPropertyGroupCollection(int id) + protected PropertyGroupCollection GetPropertyGroupCollection(int id, DateTime createDate, DateTime updateDate) { var sql = new Sql(); sql.Select("*") @@ -340,12 +340,12 @@ namespace Umbraco.Core.Persistence.Repositories var dtos = Database.Fetch(new GroupPropertyTypeRelator().Map, sql); - var propertyGroupFactory = new PropertyGroupFactory(id); + var propertyGroupFactory = new PropertyGroupFactory(id, createDate, updateDate); var propertyGroups = propertyGroupFactory.BuildEntity(dtos); return new PropertyGroupCollection(propertyGroups); } - protected PropertyTypeCollection GetPropertyTypeCollection(int id) + protected PropertyTypeCollection GetPropertyTypeCollection(int id, DateTime createDate, DateTime updateDate) { var sql = new Sql(); sql.Select("*") @@ -371,7 +371,9 @@ namespace Umbraco.Core.Persistence.Repositories HelpText = dto.HelpText, Mandatory = dto.Mandatory, SortOrder = dto.SortOrder, - ValidationRegExp = dto.ValidationRegExp + ValidationRegExp = dto.ValidationRegExp, + CreateDate = createDate, + UpdateDate = updateDate }).ToList(); //Reset dirty properties diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs index 5d9122f6ba..ba92af9964 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs @@ -51,8 +51,8 @@ namespace Umbraco.Core.Persistence.Repositories var contentType = factory.BuildEntity(dto); contentType.AllowedContentTypes = GetAllowedContentTypeIds(id); - contentType.PropertyGroups = GetPropertyGroupCollection(id); - ((ContentType)contentType).PropertyTypes = GetPropertyTypeCollection(id); + contentType.PropertyGroups = GetPropertyGroupCollection(id, contentType.CreateDate, contentType.UpdateDate); + ((ContentType)contentType).PropertyTypes = GetPropertyTypeCollection(id, contentType.CreateDate, contentType.UpdateDate); var templates = Database.Fetch("WHERE contentTypeNodeId = @Id", new { Id = id }); if(templates.Any()) diff --git a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs index 7cf63b33ba..d70aa9510c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs @@ -111,6 +111,20 @@ namespace Umbraco.Core.Persistence.Repositories #endregion + protected IEnumerable FindAllFiles(string path) + { + var list = new List(); + list.AddRange(FileSystem.GetFiles(path, "*")); + + var directories = FileSystem.GetDirectories(path); + foreach (var directory in directories) + { + list.AddRange(FindAllFiles(directory)); + } + + return list; + } + /// /// Dispose any disposable properties /// diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs index 55c2c7b682..8453bcd6da 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs @@ -1,4 +1,6 @@ -using Umbraco.Core.Models; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Persistence.Repositories { @@ -11,5 +13,12 @@ namespace Umbraco.Core.Persistence.Repositories /// Culture code for the language to retrieve /// An item IContent GetByLanguage(int id, string language); + + /// + /// Gets all published Content byh the specified query + /// + /// Query to execute against published versions + /// An enumerable list of + IEnumerable GetByPublishedVersion(IQuery query); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 9c2fe80601..81ec056f0a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -51,7 +51,7 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new MediaFactory(mediaType, NodeObjectTypeId, id); var media = factory.BuildEntity(dto); - media.Properties = GetPropertyCollection(id, dto.VersionId, mediaType); + media.Properties = GetPropertyCollection(id, dto.VersionId, mediaType, media.CreateDate, media.UpdateDate); //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 @@ -120,6 +120,8 @@ namespace Umbraco.Core.Persistence.Repositories { "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", "DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id", + "DELETE FROM umbracoRelation WHERE parentId = @Id", + "DELETE FROM umbracoRelation WHERE childId = @Id", "DELETE FROM cmsTagRelationship WHERE nodeId = @Id", "DELETE FROM cmsDocument WHERE NodeId = @Id", "DELETE FROM cmsPropertyData WHERE contentNodeId = @Id", @@ -157,7 +159,7 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new MediaFactory(mediaType, NodeObjectTypeId, dto.NodeId); var media = factory.BuildEntity(dto); - media.Properties = GetPropertyCollection(dto.NodeId, dto.VersionId, mediaType); + media.Properties = GetPropertyCollection(dto.NodeId, dto.VersionId, mediaType, media.CreateDate, media.UpdateDate); //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 @@ -342,7 +344,7 @@ namespace Umbraco.Core.Persistence.Repositories #endregion - private PropertyCollection GetPropertyCollection(int id, Guid versionId, IMediaType contentType) + private PropertyCollection GetPropertyCollection(int id, Guid versionId, IMediaType contentType, DateTime createDate, DateTime updateDate) { var sql = new Sql(); sql.Select("*") @@ -353,7 +355,7 @@ namespace Umbraco.Core.Persistence.Repositories .Where(x => x.VersionId == versionId); var propertyDataDtos = Database.Fetch(sql); - var propertyFactory = new PropertyFactory(contentType, versionId, id); + var propertyFactory = new PropertyFactory(contentType, versionId, id, createDate, updateDate); var properties = propertyFactory.BuildEntity(propertyDataDtos); var newProperties = properties.Where(x => x.HasIdentity == false); diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs index d3d4562922..515557c4ef 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs @@ -42,8 +42,8 @@ namespace Umbraco.Core.Persistence.Repositories var contentType = factory.BuildEntity(dto); contentType.AllowedContentTypes = GetAllowedContentTypeIds(id); - contentType.PropertyGroups = GetPropertyGroupCollection(id); - ((MediaType)contentType).PropertyTypes = GetPropertyTypeCollection(id); + contentType.PropertyGroups = GetPropertyGroupCollection(id, contentType.CreateDate, contentType.UpdateDate); + ((MediaType)contentType).PropertyTypes = GetPropertyTypeCollection(id, contentType.CreateDate, contentType.UpdateDate); var list = Database.Fetch("WHERE childContentTypeId = @Id", new{ Id = id}); foreach (var contentTypeDto in list) diff --git a/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs index 9feee21569..de7dd50223 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs @@ -45,12 +45,11 @@ namespace Umbraco.Core.Persistence.Repositories var path = FileSystem.GetRelativePath(id); var created = FileSystem.GetCreated(path).UtcDateTime; var updated = FileSystem.GetLastModified(path).UtcDateTime; - var name = new FileInfo(path).Name; var script = new Script(path) { Content = content, - Key = name.EncodeAsGuid(), + Key = path.EncodeAsGuid(), CreateDate = created, UpdateDate = updated }; @@ -73,7 +72,7 @@ namespace Umbraco.Core.Persistence.Repositories } else { - var files = FileSystem.GetFiles("", "*"); + var files = FindAllFiles(""); foreach (var file in files) { yield return Get(file); diff --git a/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs index ab2b602230..9acfff4e8b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs @@ -47,12 +47,11 @@ namespace Umbraco.Core.Persistence.Repositories var path = FileSystem.GetRelativePath(id); var created = FileSystem.GetCreated(path).UtcDateTime; var updated = FileSystem.GetLastModified(path).UtcDateTime; - var name = new FileInfo(path).Name; var stylesheet = new Stylesheet(path) { Content = content, - Key = name.EncodeAsGuid(), + Key = path.EncodeAsGuid(), CreateDate = created, UpdateDate = updated }; @@ -75,7 +74,7 @@ namespace Umbraco.Core.Persistence.Repositories } else { - var files = FileSystem.GetFiles("", "*"); + var files = FindAllFiles(""); foreach (var file in files) { yield return Get(file); diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index f971392078..eabbfbe990 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -21,24 +21,24 @@ using Umbraco.Core.Publishing; namespace Umbraco.Core.Services { /// - /// Represents the Content Service, which is an easy access to operations involving - /// - public class ContentService : IContentService - { - private readonly IDatabaseUnitOfWorkProvider _uowProvider; - private readonly IPublishingStrategy _publishingStrategy; + /// Represents the Content Service, which is an easy access to operations involving + /// + public class ContentService : IContentService + { + private readonly IDatabaseUnitOfWorkProvider _uowProvider; + private readonly IPublishingStrategy _publishingStrategy; private readonly RepositoryFactory _repositoryFactory; //Support recursive locks because some of the methods that require locking call other methods that require locking. //for example, the Move method needs to be locked but this calls the Save method which also needs to be locked. - private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); public ContentService() : this(new RepositoryFactory()) - {} + { } public ContentService(RepositoryFactory repositoryFactory) : this(new PetaPocoUnitOfWorkProvider(), repositoryFactory, new PublishingStrategy()) - {} + { } public ContentService(IDatabaseUnitOfWorkProvider provider) : this(provider, new RepositoryFactory(), new PublishingStrategy()) @@ -48,45 +48,45 @@ namespace Umbraco.Core.Services : this(provider, repositoryFactory, new PublishingStrategy()) { } - public ContentService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, IPublishingStrategy publishingStrategy) - { + public ContentService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, IPublishingStrategy publishingStrategy) + { if (provider == null) throw new ArgumentNullException("provider"); if (repositoryFactory == null) throw new ArgumentNullException("repositoryFactory"); if (publishingStrategy == null) throw new ArgumentNullException("publishingStrategy"); _uowProvider = provider; _publishingStrategy = publishingStrategy; _repositoryFactory = repositoryFactory; - } + } - /// - /// Creates an object using the alias of the - /// that this Content is based on. - /// + /// + /// Creates an object using the alias of the + /// that this Content is based on. + /// /// Name of the Content object - /// Id of Parent for the new Content - /// Alias of the - /// Optional id of the user creating the content - /// - public IContent CreateContent(string name, int parentId, string contentTypeAlias, int userId = 0) - { - var contentType = FindContentTypeByAlias(contentTypeAlias); + /// Id of Parent for the new Content + /// Alias of the + /// Optional id of the user creating the content + /// + public IContent CreateContent(string name, int parentId, string contentTypeAlias, int userId = 0) + { + var contentType = FindContentTypeByAlias(contentTypeAlias); var content = new Content(name, parentId, contentType); ; - if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parentId), this)) - { - content.WasCancelled = true; - return content; - } + if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parentId), this)) + { + content.WasCancelled = true; + return content; + } - content.CreatorId = userId; - content.WriterId = userId; + content.CreatorId = userId; + content.WriterId = userId; - Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parentId), this); + Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parentId), this); - Audit.Add(AuditTypes.New, "", content.CreatorId, content.Id); + Audit.Add(AuditTypes.New, "", content.CreatorId, content.Id); - return content; - } + return content; + } /// /// Creates an object using the alias of the @@ -118,65 +118,65 @@ namespace Umbraco.Core.Services return content; } - /// - /// Gets an object by Id - /// - /// Id of the Content to retrieve - /// - public IContent GetById(int id) - { - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) - { - return repository.Get(id); - } - } + /// + /// Gets an object by Id + /// + /// Id of the Content to retrieve + /// + public IContent GetById(int id) + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + return repository.Get(id); + } + } - /// - /// Gets an object by its 'UniqueId' - /// - /// Guid key of the Content to retrieve - /// - public IContent GetById(Guid key) - { - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) - { - var query = Query.Builder.Where(x => x.Key == key); - var contents = repository.GetByQuery(query); - return contents.SingleOrDefault(); - } - } + /// + /// Gets an object by its 'UniqueId' + /// + /// Guid key of the Content to retrieve + /// + public IContent GetById(Guid key) + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.Key == key); + var contents = repository.GetByQuery(query); + return contents.SingleOrDefault(); + } + } - /// - /// Gets a collection of objects by the Id of the - /// - /// Id of the - /// An Enumerable list of objects - public IEnumerable GetContentOfContentType(int id) - { - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) - { - var query = Query.Builder.Where(x => x.ContentTypeId == id); - var contents = repository.GetByQuery(query); + /// + /// Gets a collection of objects by the Id of the + /// + /// Id of the + /// An Enumerable list of objects + public IEnumerable GetContentOfContentType(int id) + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.ContentTypeId == id); + var contents = repository.GetByQuery(query); - return contents; - } - } + return contents; + } + } - /// - /// Gets a collection of objects by Level - /// - /// The level to retrieve Content from - /// An Enumerable list of objects - public IEnumerable GetByLevel(int level) - { - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) - { + /// + /// Gets a collection of objects by Level + /// + /// The level to retrieve Content from + /// An Enumerable list of objects + public IEnumerable GetByLevel(int level) + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { var query = Query.Builder.Where(x => x.Level == level && !x.Path.StartsWith("-20")); - var contents = repository.GetByQuery(query); + var contents = repository.GetByQuery(query); - return contents; - } - } + return contents; + } + } /// /// Gets a specific version of an item. @@ -234,20 +234,20 @@ namespace Umbraco.Core.Services } /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// An Enumerable list of objects - public IEnumerable GetChildren(int id) - { - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) - { - var query = Query.Builder.Where(x => x.ParentId == id); - var contents = repository.GetByQuery(query); + /// Gets a collection of objects by Parent Id + /// + /// Id of the Parent to retrieve Children from + /// An Enumerable list of objects + public IEnumerable GetChildren(int id) + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.ParentId == id); + var contents = repository.GetByQuery(query); - return contents; - } - } + return contents; + } + } /// /// Gets a collection of objects by its name or partial name @@ -328,65 +328,65 @@ namespace Umbraco.Core.Services return version.FirstOrDefault(x => x.Published == true); } - /// - /// Gets a collection of objects, which reside at the first level / root - /// - /// An Enumerable list of objects - public IEnumerable GetRootContent() - { - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) - { - var query = Query.Builder.Where(x => x.ParentId == -1); - var contents = repository.GetByQuery(query); + /// + /// Gets a collection of objects, which reside at the first level / root + /// + /// An Enumerable list of objects + public IEnumerable GetRootContent() + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.ParentId == -1); + var contents = repository.GetByQuery(query); - return contents; - } - } + return contents; + } + } - /// - /// Gets a collection of objects, which has an expiration date less than or equal to today. - /// - /// An Enumerable list of objects - public IEnumerable GetContentForExpiration() - { - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) - { - var query = Query.Builder.Where(x => x.Published == true && x.ExpireDate <= DateTime.Now); - var contents = repository.GetByQuery(query); + /// + /// Gets a collection of objects, which has an expiration date less than or equal to today. + /// + /// An Enumerable list of objects + public IEnumerable GetContentForExpiration() + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.Published == true && x.ExpireDate <= DateTime.Now); + var contents = repository.GetByQuery(query); - return contents; - } - } + return contents; + } + } - /// - /// Gets a collection of objects, which has a release date less than or equal to today. - /// - /// An Enumerable list of objects - public IEnumerable GetContentForRelease() - { - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) - { - var query = Query.Builder.Where(x => x.Published == false && x.ReleaseDate <= DateTime.Now); - var contents = repository.GetByQuery(query); + /// + /// Gets a collection of objects, which has a release date less than or equal to today. + /// + /// An Enumerable list of objects + public IEnumerable GetContentForRelease() + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.Published == false && x.ReleaseDate <= DateTime.Now); + var contents = repository.GetByQuery(query); - return contents; - } - } + return contents; + } + } - /// - /// Gets a collection of an objects, which resides in the Recycle Bin - /// - /// An Enumerable list of objects - public IEnumerable GetContentInRecycleBin() - { - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) - { - var query = Query.Builder.Where(x => x.Path.Contains("-20")); - var contents = repository.GetByQuery(query); + /// + /// Gets a collection of an objects, which resides in the Recycle Bin + /// + /// An Enumerable list of objects + public IEnumerable GetContentInRecycleBin() + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.Path.Contains("-20")); + var contents = repository.GetByQuery(query); - return contents; - } - } + return contents; + } + } /// /// Checks whether an item has any children @@ -490,14 +490,14 @@ namespace Umbraco.Core.Services return result.Success; } - /// - /// Publishes a object and all its children - /// - /// The to publish along with its children - /// Optional Id of the User issueing the publishing - /// True if publishing succeeded, otherwise False - public bool PublishWithChildren(IContent content, int userId = 0) - { + /// + /// Publishes a object and all its children + /// + /// The to publish along with its children + /// Optional Id of the User issueing the publishing + /// True if publishing succeeded, otherwise False + public bool PublishWithChildren(IContent content, int userId = 0) + { var result = PublishWithChildrenDo(content, userId, true); //This used to just return false only when the parent content failed, otherwise would always return true so we'll @@ -506,145 +506,145 @@ namespace Umbraco.Core.Services return false; return result.Single(x => x.Result.ContentItem.Id == content.Id).Success; - } + } - /// - /// UnPublishes a single object - /// - /// The to publish - /// Optional Id of the User issueing the publishing - /// True if unpublishing succeeded, otherwise False - public bool UnPublish(IContent content, int userId = 0) - { - return UnPublishDo(content, false, userId); - } + /// + /// UnPublishes a single object + /// + /// The to publish + /// Optional Id of the User issueing the publishing + /// True if unpublishing succeeded, otherwise False + public bool UnPublish(IContent content, int userId = 0) + { + return UnPublishDo(content, false, userId); + } - /// - /// Saves and Publishes a single object - /// - /// The to save and publish - /// Optional Id of the User issueing the publishing + /// + /// Saves and Publishes a single object + /// + /// The to save and publish + /// Optional Id of the User issueing the publishing /// Optional boolean indicating whether or not to raise save events. - /// True if publishing succeeded, otherwise False + /// True if publishing succeeded, otherwise False public bool SaveAndPublish(IContent content, int userId = 0, bool raiseEvents = true) - { + { var result = SaveAndPublishDo(content, userId, raiseEvents); return result.Success; - } + } - /// - /// Saves a single object - /// - /// The to save - /// Optional Id of the User saving the Content + /// + /// Saves a single object + /// + /// The to save + /// Optional Id of the User saving the Content /// Optional boolean indicating whether or not to raise events. public void Save(IContent content, int userId = 0, bool raiseEvents = true) - { - Save(content, true, userId, raiseEvents); - } + { + Save(content, true, userId, raiseEvents); + } - /// - /// Saves a collection of objects. - /// - /// - /// If the collection of content contains new objects that references eachother by Id or ParentId, - /// then use the overload Save method with a collection of Lazy . - /// - /// Collection of to save - /// Optional Id of the User saving the Content + /// + /// Saves a collection of objects. + /// + /// + /// If the collection of content contains new objects that references eachother by Id or ParentId, + /// then use the overload Save method with a collection of Lazy . + /// + /// Collection of to save + /// Optional Id of the User saving the Content /// Optional boolean indicating whether or not to raise events. public void Save(IEnumerable contents, int userId = 0, bool raiseEvents = true) - { - if(raiseEvents) - { + { + if (raiseEvents) + { if (Saving.IsRaisedEventCancelled(new SaveEventArgs(contents), this)) - return; + return; } - using (new WriteLock(Locker)) - { - var containsNew = contents.Any(x => x.HasIdentity == false); + using (new WriteLock(Locker)) + { + var containsNew = contents.Any(x => x.HasIdentity == false); - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentRepository(uow)) - { - if (containsNew) - { - foreach (var content in contents) - { - content.WriterId = userId; + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + if (containsNew) + { + foreach (var content in contents) + { + content.WriterId = userId; - //Only change the publish state if the "previous" version was actually published - if (content.Published) - content.ChangePublishedState(PublishedState.Saved); + //Only change the publish state if the "previous" version was actually published + if (content.Published) + content.ChangePublishedState(PublishedState.Saved); - repository.AddOrUpdate(content); - uow.Commit(); - } - } - else - { - foreach (var content in contents) - { - content.WriterId = userId; - repository.AddOrUpdate(content); - } - uow.Commit(); - } - } + repository.AddOrUpdate(content); + uow.Commit(); + } + } + else + { + foreach (var content in contents) + { + content.WriterId = userId; + repository.AddOrUpdate(content); + } + uow.Commit(); + } + } - if (raiseEvents) - Saved.RaiseEvent(new SaveEventArgs(contents, false), this); + if (raiseEvents) + Saved.RaiseEvent(new SaveEventArgs(contents, false), this); - Audit.Add(AuditTypes.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, -1); - } - } - - /// - /// Deletes all content of specified type. All children of deleted content is moved to Recycle Bin. - /// - /// This needs extra care and attention as its potentially a dangerous and extensive operation - /// Id of the - /// Optional Id of the user issueing the delete operation - public void DeleteContentOfType(int contentTypeId, int userId = 0) - { - using (new WriteLock(Locker)) - { - using (var uow = _uowProvider.GetUnitOfWork()) - { - var repository = _repositoryFactory.CreateContentRepository(uow); - //NOTE What about content that has the contenttype as part of its composition? - var query = Query.Builder.Where(x => x.ContentTypeId == contentTypeId); - var contents = repository.GetByQuery(query); + Audit.Add(AuditTypes.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, -1); + } + } - if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(contents), this)) - return; + /// + /// Deletes all content of specified type. All children of deleted content is moved to Recycle Bin. + /// + /// This needs extra care and attention as its potentially a dangerous and extensive operation + /// Id of the + /// Optional Id of the user issueing the delete operation + public void DeleteContentOfType(int contentTypeId, int userId = 0) + { + using (new WriteLock(Locker)) + { + using (var uow = _uowProvider.GetUnitOfWork()) + { + var repository = _repositoryFactory.CreateContentRepository(uow); + //NOTE What about content that has the contenttype as part of its composition? + var query = Query.Builder.Where(x => x.ContentTypeId == contentTypeId); + var contents = repository.GetByQuery(query); - foreach (var content in contents.OrderByDescending(x => x.ParentId)) - { - //Look for children of current content and move that to trash before the current content is deleted - var c = content; - var childQuery = Query.Builder.Where(x => x.Path.StartsWith(c.Path)); - var children = repository.GetByQuery(childQuery); + if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(contents), this)) + return; - foreach (var child in children) - { - if (child.ContentType.Id != contentTypeId) - MoveToRecycleBin(child, userId); - } + foreach (var content in contents.OrderByDescending(x => x.ParentId)) + { + //Look for children of current content and move that to trash before the current content is deleted + var c = content; + var childQuery = Query.Builder.Where(x => x.Path.StartsWith(c.Path)); + var children = repository.GetByQuery(childQuery); - //Permantly delete the content - Delete(content, userId); - } - } + foreach (var child in children) + { + if (child.ContentType.Id != contentTypeId) + MoveToRecycleBin(child, userId); + } - Audit.Add(AuditTypes.Delete, - string.Format("Delete Content of Type {0} performed by user", contentTypeId), - userId, -1); - } - } + //Permantly delete the content + Delete(content, userId); + } + } - /// - /// Permanently deletes an object. + Audit.Add(AuditTypes.Delete, + string.Format("Delete Content of Type {0} performed by user", contentTypeId), + userId, -1); + } + } + + /// + /// Permanently deletes an object as well as all of its Children. /// /// /// This method will also delete associated media files, child content and possibly associated domains. @@ -652,47 +652,47 @@ namespace Umbraco.Core.Services /// Please note that this method will completely remove the Content from the database /// The to delete /// Optional Id of the User deleting the Content - public void Delete(IContent content, int userId = 0) - { - using (new WriteLock(Locker)) - { - if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(content), this)) - return; + public void Delete(IContent content, int userId = 0) + { + using (new WriteLock(Locker)) + { + if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(content), this)) + return; - //Make sure that published content is unpublished before being deleted - if (HasPublishedVersion(content.Id)) - { - UnPublish(content, userId); - } + //Make sure that published content is unpublished before being deleted + if (HasPublishedVersion(content.Id)) + { + UnPublish(content, userId); + } - //Delete children before deleting the 'possible parent' - var children = GetChildren(content.Id); - foreach (var child in children) - { - Delete(child, userId); - } + //Delete children before deleting the 'possible parent' + var children = GetChildren(content.Id); + foreach (var child in children) + { + Delete(child, userId); + } - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentRepository(uow)) - { - repository.Delete(content); - uow.Commit(); - } + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + repository.Delete(content); + uow.Commit(); + } - Deleted.RaiseEvent(new DeleteEventArgs(content, false), this); + Deleted.RaiseEvent(new DeleteEventArgs(content, false), this); - Audit.Add(AuditTypes.Delete, "Delete Content performed by user", userId, content.Id); - } - } + Audit.Add(AuditTypes.Delete, "Delete Content performed by user", userId, content.Id); + } + } - /// - /// Permanently deletes versions from an object prior to a specific date. - /// - /// Id of the object to delete versions from - /// Latest version date - /// Optional Id of the User deleting versions of a Content object - public void DeleteVersions(int id, DateTime versionDate, int userId = 0) - { + /// + /// Permanently deletes versions from an object prior to a specific date. + /// + /// Id of the object to delete versions from + /// Latest version date + /// Optional Id of the User deleting versions of a Content object + public void DeleteVersions(int id, DateTime versionDate, int userId = 0) + { //TODO: We should check if we are going to delete the most recent version because if that happens it means the // entity is completely deleted and we should raise the normal Deleting/Deleted event @@ -709,19 +709,19 @@ namespace Umbraco.Core.Services DeletedVersions.RaiseEvent(new DeleteRevisionsEventArgs(id, false, dateToRetain: versionDate), this); Audit.Add(AuditTypes.Delete, "Delete Content by version date performed by user", userId, -1); - } + } - /// - /// Permanently deletes specific version(s) from an object. - /// - /// Id of the object to delete a version from - /// Id of the version to delete - /// Boolean indicating whether to delete versions prior to the versionId - /// Optional Id of the User deleting versions of a Content object - public void DeleteVersion(int id, Guid versionId, bool deletePriorVersions, int userId = 0) - { - using (new WriteLock(Locker)) - { + /// + /// Permanently deletes specific version(s) from an object. + /// + /// Id of the object to delete a version from + /// Id of the version to delete + /// Boolean indicating whether to delete versions prior to the versionId + /// Optional Id of the User deleting versions of a Content object + public void DeleteVersion(int id, Guid versionId, bool deletePriorVersions, int userId = 0) + { + using (new WriteLock(Locker)) + { //TODO: We should check if we are going to delete the most recent version because if that happens it means the // entity is completely deleted and we should raise the normal Deleting/Deleted event @@ -744,19 +744,19 @@ namespace Umbraco.Core.Services DeletedVersions.RaiseEvent(new DeleteRevisionsEventArgs(id, false, specificVersion: versionId), this); Audit.Add(AuditTypes.Delete, "Delete Content by version performed by user", 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 - /// Optional Id of the User deleting the Content - public void MoveToRecycleBin(IContent content, int userId = 0) - { - using (new WriteLock(Locker)) - { + /// + /// 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 + /// Optional Id of the User deleting the Content + public void MoveToRecycleBin(IContent content, int userId = 0) + { + using (new WriteLock(Locker)) + { if (Trashing.IsRaisedEventCancelled(new MoveEventArgs(content, -20), this)) return; @@ -794,24 +794,24 @@ namespace Umbraco.Core.Services Trashed.RaiseEvent(new MoveEventArgs(content, false, -20), this); Audit.Add(AuditTypes.Move, "Move Content to Recycle Bin performed by user", userId, content.Id); - } - } + } + } - /// - /// Moves an object to a new location by changing its parent id. - /// - /// - /// If the object is already published it will be - /// published after being moved to its new location. Otherwise it'll just - /// be saved with a new parent id. - /// - /// The to move - /// Id of the Content's new Parent - /// Optional Id of the User moving the Content - public void Move(IContent content, int parentId, int userId = 0) - { - using (new WriteLock(Locker)) - { + /// + /// Moves an object to a new location by changing its parent id. + /// + /// + /// If the object is already published it will be + /// published after being moved to its new location. Otherwise it'll just + /// be saved with a new parent id. + /// + /// The to move + /// Id of the Content's new Parent + /// Optional Id of the User moving the Content + public void Move(IContent content, int parentId, int userId = 0) + { + using (new WriteLock(Locker)) + { //This ensures that the correct method is called if this method is used to Move to recycle bin. if (parentId == -20) { @@ -821,9 +821,9 @@ namespace Umbraco.Core.Services if (Moving.IsRaisedEventCancelled(new MoveEventArgs(content, parentId), this)) return; - + content.WriterId = userId; - if (parentId == -1) + if (parentId == -1) { content.Path = string.Concat("-1,", content.Id); content.Level = 1; @@ -890,36 +890,36 @@ namespace Umbraco.Core.Services Moved.RaiseEvent(new MoveEventArgs(content, false, parentId), this); Audit.Add(AuditTypes.Move, "Move Content performed by user", userId, content.Id); - } - } + } + } /// - /// Empties the Recycle Bin by deleting all that resides in the bin - /// - public void EmptyRecycleBin() - { - //TODO: Why don't we have a base class to share between MediaService/ContentService as some of this is exacty the same? + /// Empties the Recycle Bin by deleting all that resides in the bin + /// + public void EmptyRecycleBin() + { + //TODO: Why don't we have a base class to share between MediaService/ContentService as some of this is exacty the same? - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentRepository(uow)) - { - var query = Query.Builder.Where(x => x.Trashed == true); - var contents = repository.GetByQuery(query).OrderByDescending(x => x.Level); + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + var query = Query.Builder.Where(x => x.Trashed == true); + var contents = repository.GetByQuery(query).OrderByDescending(x => x.Level); - foreach (var content in contents) - { - if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(content), this)) - continue; + foreach (var content in contents) + { + if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(content), this)) + continue; - repository.Delete(content); + repository.Delete(content); - Deleted.RaiseEvent(new DeleteEventArgs(content, false), this); - } - uow.Commit(); - } + Deleted.RaiseEvent(new DeleteEventArgs(content, false), this); + } + uow.Commit(); + } Audit.Add(AuditTypes.Delete, "Empty Recycle Bin performed by user", 0, -20); - } + } /// /// Copies an object by creating a new Content object of the same type and copies all data from the current @@ -934,7 +934,7 @@ namespace Umbraco.Core.Services { using (new WriteLock(Locker)) { - var copy = ((Content) content).Clone(); + var copy = ((Content)content).Clone(); copy.ParentId = parentId; // A copy should never be set to published automatically even if the original was. @@ -991,10 +991,10 @@ namespace Umbraco.Core.Services var tagsDataTypeId = new Guid(Constants.PropertyEditors.Tags); if (content.Properties.Any(x => x.PropertyType.DataTypeId == tagsDataTypeId)) { - var tags = uow.Database.Fetch("WHERE nodeId = @Id", new {Id = content.Id}); + var tags = uow.Database.Fetch("WHERE nodeId = @Id", new { Id = content.Id }); foreach (var tag in tags) { - uow.Database.Insert(new TagRelationshipDto {NodeId = copy.Id, TagId = tag.TagId}); + uow.Database.Insert(new TagRelationshipDto { NodeId = copy.Id, TagId = tag.TagId }); } } } @@ -1019,9 +1019,9 @@ namespace Umbraco.Core.Services string.Format("Copied content with Id: '{0}' related to original content with Id: '{1}'", copy.Id, content.Id), copy.WriterId, copy.Id); } - + //Look for children and copy those as well - var children = GetChildren(content.Id); + var children = GetChildren(content.Id).OrderBy(x => x.SortOrder); foreach (var child in children) { Copy(child, copy.Id, relateToOriginal, userId); @@ -1030,48 +1030,48 @@ namespace Umbraco.Core.Services Copied.RaiseEvent(new CopyEventArgs(content, copy, false, parentId), this); Audit.Add(AuditTypes.Copy, "Copy Content performed by user", content.WriterId, content.Id); - + RuntimeCacheProvider.Current.Clear(); return copy; } } - /// - /// Sends an to Publication, which executes handlers and events for the 'Send to Publication' action. - /// - /// The to send to publication - /// Optional Id of the User issueing the send to publication - /// True if sending publication was succesfull otherwise false - internal bool SendToPublication(IContent content, int userId = 0) - { + /// + /// Sends an to Publication, which executes handlers and events for the 'Send to Publication' action. + /// + /// The to send to publication + /// Optional Id of the User issueing the send to publication + /// True if sending publication was succesfull otherwise false + internal bool SendToPublication(IContent content, int userId = 0) + { - if (SendingToPublish.IsRaisedEventCancelled(new SendToPublishEventArgs(content), this)) - return false; + if (SendingToPublish.IsRaisedEventCancelled(new SendToPublishEventArgs(content), this)) + return false; - //TODO: Do some stuff here.. RunActionHandlers + //TODO: Do some stuff here.. RunActionHandlers - SentToPublish.RaiseEvent(new SendToPublishEventArgs(content, false), this); + SentToPublish.RaiseEvent(new SendToPublishEventArgs(content, false), this); - Audit.Add(AuditTypes.SendToPublish, "Send to Publish performed by user", content.WriterId, content.Id); + Audit.Add(AuditTypes.SendToPublish, "Send to Publish performed by user", content.WriterId, content.Id); - return true; - } + return true; + } - /// - /// Rollback an object to a previous version. - /// This will create a new version, which is a copy of all the old data. - /// - /// - /// The way data is stored actually only allows us to rollback on properties - /// and not data like Name and Alias of the Content. - /// - /// Id of the being rolled back - /// Id of the version to rollback to - /// Optional Id of the User issueing the rollback of the Content - /// The newly created object - public IContent Rollback(int id, Guid versionId, int userId = 0) - { + /// + /// Rollback an object to a previous version. + /// This will create a new version, which is a copy of all the old data. + /// + /// + /// The way data is stored actually only allows us to rollback on properties + /// and not data like Name and Alias of the Content. + /// + /// Id of the being rolled back + /// Id of the version to rollback to + /// Optional Id of the User issueing the rollback of the Content + /// The newly created object + public IContent Rollback(int id, Guid versionId, int userId = 0) + { var content = GetByVersion(versionId); if (RollingBack.IsRaisedEventCancelled(new RollbackEventArgs(content), this)) @@ -1092,7 +1092,7 @@ namespace Umbraco.Core.Services Audit.Add(AuditTypes.RollBack, "Content rollback performed by user", content.WriterId, content.Id); return content; - } + } #region Internal Methods @@ -1120,13 +1120,13 @@ namespace Umbraco.Core.Services return PublishWithChildrenDo(content, userId, includeUnpublished); } - /// - /// Saves and Publishes a single object - /// - /// The to save and publish - /// Optional Id of the User issueing the publishing + /// + /// Saves and Publishes a single object + /// + /// The to save and publish + /// Optional Id of the User issueing the publishing /// Optional boolean indicating whether or not to raise save events. - /// True if publishing succeeded, otherwise False + /// True if publishing succeeded, otherwise False internal Attempt SaveAndPublishInternal(IContent content, int userId = 0, bool raiseEvents = true) { return SaveAndPublishDo(content, userId, raiseEvents); @@ -1141,8 +1141,8 @@ namespace Umbraco.Core.Services { using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) { - var query = Query.Builder.Where(x => x.Id != content.Id && x.Path.StartsWith(content.Path) && x.Published == true && x.Trashed == false); - var contents = repository.GetByQuery(query); + var query = Query.Builder.Where(x => x.Id != content.Id && x.Path.StartsWith(content.Path) && x.Trashed == false); + var contents = repository.GetByPublishedVersion(query); return contents; } @@ -1333,19 +1333,19 @@ namespace Umbraco.Core.Services } return unpublished; - } + } } - /// - /// Saves and Publishes a single object - /// - /// The to save and publish - /// Optional Id of the User issueing the publishing + /// + /// Saves and Publishes a single object + /// + /// The to save and publish + /// Optional Id of the User issueing the publishing /// Optional boolean indicating whether or not to raise save events. - /// True if publishing succeeded, otherwise False + /// True if publishing succeeded, otherwise False private Attempt SaveAndPublishDo(IContent content, int userId = 0, bool raiseEvents = true) { - if(raiseEvents) + if (raiseEvents) { if (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this)) { @@ -1353,8 +1353,8 @@ namespace Umbraco.Core.Services } } - using (new WriteLock(Locker)) - { + using (new WriteLock(Locker)) + { //Has this content item previously been published? If so, we don't need to refresh the children var previouslyPublished = HasPublishedVersion(content.Id); var publishStatus = new PublishStatus(content, PublishStatusType.Success); //initially set to success @@ -1418,12 +1418,12 @@ namespace Umbraco.Core.Services ? uow.Database.Update( "SET xml = @Xml, timestamp = @Timestamp WHERE nodeId = @Id AND versionId = @Version", new - { - Xml = previewPoco.Xml, - Timestamp = previewPoco.Timestamp, - Id = previewPoco.NodeId, - Version = previewPoco.VersionId - }) + { + Xml = previewPoco.Xml, + Timestamp = previewPoco.Timestamp, + Id = previewPoco.NodeId, + Version = previewPoco.VersionId + }) : Convert.ToInt32(uow.Database.Insert(previewPoco)); if (published) @@ -1461,23 +1461,23 @@ namespace Umbraco.Core.Services } } - /// - /// Saves a single object - /// - /// The to save - /// Boolean indicating whether or not to change the Published state upon saving - /// Optional Id of the User saving the Content + /// + /// Saves a single object + /// + /// The to save + /// Boolean indicating whether or not to change the Published state upon saving + /// Optional Id of the User saving the Content /// Optional boolean indicating whether or not to raise events. - private void Save(IContent content, bool changeState, int userId = 0, bool raiseEvents = true) + private void Save(IContent content, bool changeState, int userId = 0, bool raiseEvents = true) { - if(raiseEvents) + if (raiseEvents) { if (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this)) - return; + return; } - using (new WriteLock(Locker)) - { + using (new WriteLock(Locker)) + { var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateContentRepository(uow)) { @@ -1519,7 +1519,7 @@ namespace Umbraco.Core.Services Saved.RaiseEvent(new SaveEventArgs(content, false), this); Audit.Add(AuditTypes.Save, "Save Content performed by user", userId, content.Id); - } + } } /// @@ -1623,98 +1623,98 @@ namespace Umbraco.Core.Services #region Event Handlers /// - /// Occurs before Delete - /// - public static event TypedEventHandler> Deleting; + /// Occurs before Delete + /// + public static event TypedEventHandler> Deleting; - /// - /// Occurs after Delete - /// - public static event TypedEventHandler> Deleted; + /// + /// Occurs after Delete + /// + public static event TypedEventHandler> Deleted; - /// - /// Occurs before Delete Versions - /// - public static event TypedEventHandler DeletingVersions; + /// + /// Occurs before Delete Versions + /// + public static event TypedEventHandler DeletingVersions; - /// - /// Occurs after Delete Versions - /// - public static event TypedEventHandler DeletedVersions; + /// + /// Occurs after Delete Versions + /// + public static event TypedEventHandler DeletedVersions; - /// - /// Occurs before Save - /// - public static event TypedEventHandler> Saving; - - /// - /// Occurs after Save - /// - public static event TypedEventHandler> Saved; - - /// - /// Occurs before Create - /// - public static event TypedEventHandler> Creating; + /// + /// Occurs before Save + /// + public static event TypedEventHandler> Saving; - /// - /// Occurs after Create - /// + /// + /// Occurs after Save + /// + public static event TypedEventHandler> Saved; + + /// + /// Occurs before Create + /// + public static event TypedEventHandler> Creating; + + /// + /// Occurs after Create + /// /// /// Please note that the Content object has been created, but not saved /// so it does not have an identity yet (meaning no Id has been set). /// - public static event TypedEventHandler> Created; + public static event TypedEventHandler> Created; - /// - /// Occurs before Copy - /// - public static event TypedEventHandler> Copying; + /// + /// Occurs before Copy + /// + public static event TypedEventHandler> Copying; - /// - /// Occurs after Copy - /// - public static event TypedEventHandler> Copied; + /// + /// Occurs after Copy + /// + public static event TypedEventHandler> Copied; - /// - /// Occurs before Content is moved to Recycle Bin - /// - public static event TypedEventHandler> Trashing; + /// + /// Occurs before Content is moved to Recycle Bin + /// + public static event TypedEventHandler> Trashing; - /// - /// Occurs after Content is moved to Recycle Bin - /// - public static event TypedEventHandler> Trashed; + /// + /// Occurs after Content is moved to Recycle Bin + /// + public static event TypedEventHandler> Trashed; - /// - /// Occurs before Move - /// - public static event TypedEventHandler> Moving; + /// + /// Occurs before Move + /// + public static event TypedEventHandler> Moving; - /// - /// Occurs after Move - /// - public static event TypedEventHandler> Moved; + /// + /// Occurs after Move + /// + public static event TypedEventHandler> Moved; - /// - /// Occurs before Rollback - /// - public static event TypedEventHandler> RollingBack; + /// + /// Occurs before Rollback + /// + public static event TypedEventHandler> RollingBack; - /// - /// Occurs after Rollback - /// - public static event TypedEventHandler> RolledBack; + /// + /// Occurs after Rollback + /// + public static event TypedEventHandler> RolledBack; - /// - /// Occurs before Send to Publish - /// - public static event TypedEventHandler> SendingToPublish; + /// + /// Occurs before Send to Publish + /// + public static event TypedEventHandler> SendingToPublish; - /// - /// Occurs after Send to Publish - /// - public static event TypedEventHandler> SentToPublish; - #endregion - } + /// + /// Occurs after Send to Publish + /// + public static event TypedEventHandler> SentToPublish; + #endregion + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index e67e1fca04..77a24cdc03 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -542,11 +542,11 @@ namespace Umbraco.Core.Services } /// - /// Permanently deletes an object + /// Permanently deletes an object as well as all of its Children. /// /// /// Please note that this method will completely remove the Media from the database, - /// but current not from the file system. + /// as well as associated media files from the file system. /// /// The to delete /// Id of the User deleting the Media @@ -555,6 +555,13 @@ namespace Umbraco.Core.Services if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(media), this)) return; + //Delete children before deleting the 'possible parent' + var children = GetChildren(media.Id); + foreach (var child in children) + { + Delete(child, userId); + } + var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateMediaRepository(uow)) { diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index ccfe885adf..36766382dc 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -196,9 +196,21 @@ namespace Umbraco.Core.Services /// Imports and saves package xml as /// /// Xml to import - /// + /// Optional id of the User performing the operation. Default is zero (admin). /// An enumrable list of generated ContentTypes public IEnumerable ImportContentTypes(XElement element, int userId = 0) + { + return ImportContentTypes(element, true, userId); + } + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Boolean indicating whether or not to import the + /// Optional id of the User performing the operation. Default is zero (admin). + /// An enumrable list of generated ContentTypes + public IEnumerable ImportContentTypes(XElement element, bool importStructure, int userId = 0) { var name = element.Name.LocalName; if (name.Equals("DocumentTypes") == false && name.Equals("DocumentType") == false) @@ -227,22 +239,25 @@ namespace Umbraco.Core.Services var list = _importedContentTypes.Select(x => x.Value).ToList(); _contentTypeService.Save(list, userId); - var updatedContentTypes = new List(); - //Update the structure here - we can't do it untill all DocTypes have been created - foreach (var documentType in documentTypes) + if (importStructure) { - var alias = documentType.Element("Info").Element("Alias").Value; - var structureElement = documentType.Element("Structure"); - //Ensure that we only update ContentTypes which has actual structure-elements - if (structureElement == null || structureElement.Elements("DocumentType").Any() == false) continue; + var updatedContentTypes = new List(); + //Update the structure here - we can't do it untill all DocTypes have been created + foreach (var documentType in documentTypes) + { + var alias = documentType.Element("Info").Element("Alias").Value; + var structureElement = documentType.Element("Structure"); + //Ensure that we only update ContentTypes which has actual structure-elements + if (structureElement == null || structureElement.Elements("DocumentType").Any() == false) continue; - var updated = UpdateContentTypesStructure(_importedContentTypes[alias], structureElement); - updatedContentTypes.Add(updated); + var updated = UpdateContentTypesStructure(_importedContentTypes[alias], structureElement); + updatedContentTypes.Add(updated); + } + //Update ContentTypes with a newly added structure/list of allowed children + if (updatedContentTypes.Any()) + _contentTypeService.Save(updatedContentTypes, userId); } - //Update ContentTypes with a newly added structure/list of allowed children - if(updatedContentTypes.Any()) - _contentTypeService.Save(updatedContentTypes, userId); - + return list; } @@ -423,7 +438,7 @@ namespace Umbraco.Core.Services if (_importedContentTypes.ContainsKey(alias)) { var allowedChild = _importedContentTypes[alias]; - if(allowedChild == null || allowedChildren.Any(x => x.Alias == alias)) continue; + if (allowedChild == null || allowedChildren.Any(x => x.Id.IsValueCreated && x.Id.Value == allowedChild.Id)) continue; allowedChildren.Add(new ContentTypeSort(new Lazy(() => allowedChild.Id), sortOrder, allowedChild.Alias)); sortOrder++; diff --git a/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs b/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs new file mode 100644 index 0000000000..0699370259 --- /dev/null +++ b/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs @@ -0,0 +1,423 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Tests.TestHelpers.Entities; + +namespace Umbraco.Tests.Models.Collections +{ + [TestFixture] + public class PropertyCollectionTests + { + [Test] + public void SimpleOrder_Returns_Null_On_FirstOrDefault_When_Empty() + { + var orders = new SimpleOrder(); + var item = orders.FirstOrDefault(); + + Assert.That(item == null, Is.True); + } + + [Test] + public void PropertyCollection_Returns_Null_On_FirstOrDefault_When_Empty() + { + var list = new List(); + var collection = new PropertyCollection(list); + + var first = collection.FirstOrDefault(); + var second = collection.FirstOrDefault(x => x.Alias.InvariantEquals("Test")); + + Assert.That(first, Is.Null); + Assert.That(first == null, Is.True); + Assert.That(second == null, Is.True); + } + + [Test] + public void PropertyTypeCollection_Returns_Null_On_FirstOrDefault_When_Empty() + { + var list = new List(); + var collection = new PropertyTypeCollection(list); + + Assert.That(collection.FirstOrDefault(), Is.Null); + Assert.That(collection.FirstOrDefault(x => x.Alias.InvariantEquals("Test")) == null, Is.True); + } + + [Test] + public void PropertyGroupCollection_Returns_Null_On_FirstOrDefault_When_Empty() + { + var list = new List(); + var collection = new PropertyGroupCollection(list); + + Assert.That(collection.FirstOrDefault(), Is.Null); + Assert.That(collection.FirstOrDefault(x => x.Name.InvariantEquals("Test")) == null, Is.True); + } + + [Test] + public void PropertyGroups_Collection_FirstOrDefault_Returns_Null() + { + var contentType = MockedContentTypes.CreateTextpageContentType(); + + Assert.That(contentType.PropertyGroups, Is.Not.Null); + Assert.That(contentType.PropertyGroups.FirstOrDefault(x => x.Name.InvariantEquals("Content")) == null, Is.False); + Assert.That(contentType.PropertyGroups.FirstOrDefault(x => x.Name.InvariantEquals("Test")) == null, Is.True); + Assert.That(contentType.PropertyGroups.Any(x => x.Name.InvariantEquals("Test")), Is.False); + } + } + + public class SimpleOrder : KeyedCollection, INotifyCollectionChanged + { + // The parameterless constructor of the base class creates a + // KeyedCollection with an internal dictionary. For this code + // example, no other constructors are exposed. + // + public SimpleOrder() : base() { } + + public SimpleOrder(IEnumerable properties) + { + Reset(properties); + } + + // This is the only method that absolutely must be overridden, + // because without it the KeyedCollection cannot extract the + // keys from the items. The input parameter type is the + // second generic type argument, in this case OrderItem, and + // the return value type is the first generic type argument, + // in this case int. + // + protected override int GetKeyForItem(OrderItem item) + { + // In this example, the key is the part number. + return item.PartNumber; + } + + internal void Reset(IEnumerable properties) + { + Clear(); + properties.ForEach(Add); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + protected override void SetItem(int index, OrderItem item) + { + base.SetItem(index, item); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); + } + + protected override void RemoveItem(int index) + { + var removed = this[index]; + base.RemoveItem(index); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed)); + } + + protected override void InsertItem(int index, OrderItem item) + { + base.InsertItem(index, item); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); + } + + protected override void ClearItems() + { + base.ClearItems(); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + public new bool Contains(int partNumber) + { + return this.Any(x => x.PartNumber == partNumber); + } + + public event NotifyCollectionChangedEventHandler CollectionChanged; + + protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args) + { + if (CollectionChanged != null) + { + CollectionChanged(this, args); + } + } + } + + public class OrderItem : Item + { + public readonly int PartNumber; + public readonly string Description; + public readonly double UnitPrice; + + private int _quantity = 0; + + public OrderItem(int partNumber, string description, + int quantity, double unitPrice) + { + this.PartNumber = partNumber; + this.Description = description; + this.Quantity = quantity; + this.UnitPrice = unitPrice; + } + + public int Quantity + { + get { return _quantity; } + set + { + if (value < 0) + throw new ArgumentException("Quantity cannot be negative."); + + _quantity = value; + } + } + + public override string ToString() + { + return String.Format( + "{0,9} {1,6} {2,-12} at {3,8:#,###.00} = {4,10:###,###.00}", + PartNumber, _quantity, Description, UnitPrice, + UnitPrice * _quantity); + } + } + + public abstract class Item : IEntity, ICanBeDirty + { + private bool _hasIdentity; + private int? _hash; + private int _id; + private Guid _key; + + protected Item() + { + _propertyChangedInfo = new Dictionary(); + } + + /// + /// Integer Id + /// + [DataMember] + public int Id + { + get + { + return _id; + } + set + { + _id = value; + HasIdentity = true; + } + } + + /// + /// Guid based Id + /// + /// The key is currectly used to store the Unique Id from the + /// umbracoNode table, which many of the entities are based on. + [DataMember] + public Guid Key + { + get + { + if (_key == Guid.Empty) + return _id.ToGuid(); + + return _key; + } + set { _key = value; } + } + + /// + /// Gets or sets the Created Date + /// + [DataMember] + public DateTime CreateDate { get; set; } + + /// + /// Gets or sets the Modified Date + /// + [DataMember] + public DateTime UpdateDate { get; set; } + + /// + /// Gets or sets the WasCancelled flag, which is used to track + /// whether some action against an entity was cancelled through some event. + /// This only exists so we have a way to check if an event was cancelled through + /// the new api, which also needs to take effect in the legacy api. + /// + [IgnoreDataMember] + internal bool WasCancelled { get; set; } + + /// + /// Property changed event + /// + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Method to call on a property setter. + /// + /// The property info. + protected virtual void OnPropertyChanged(PropertyInfo propertyInfo) + { + _propertyChangedInfo[propertyInfo.Name] = true; + + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs(propertyInfo.Name)); + } + } + + internal virtual void ResetIdentity() + { + _hasIdentity = false; + _id = default(int); + } + + /// + /// Method to call on entity saved when first added + /// + internal virtual void AddingEntity() + { + CreateDate = DateTime.Now; + UpdateDate = DateTime.Now; + } + + /// + /// Method to call on entity saved/updated + /// + internal virtual void UpdatingEntity() + { + UpdateDate = DateTime.Now; + } + + /// + /// Tracks the properties that have changed + /// + //private readonly IDictionary _propertyChangedInfo = new Dictionary(); + private IDictionary _propertyChangedInfo; + + /// + /// Indicates whether a specific property on the current entity is dirty. + /// + /// Name of the property to check + /// True if Property is dirty, otherwise False + public virtual bool IsPropertyDirty(string propertyName) + { + return _propertyChangedInfo.Any(x => x.Key == propertyName); + } + + /// + /// Indicates whether the current entity is dirty. + /// + /// True if entity is dirty, otherwise False + public virtual bool IsDirty() + { + return _propertyChangedInfo.Any(); + } + + /// + /// Resets dirty properties by clearing the dictionary used to track changes. + /// + /// + /// Please note that resetting the dirty properties could potentially + /// obstruct the saving of a new or updated entity. + /// + public virtual void ResetDirtyProperties() + { + _propertyChangedInfo.Clear(); + } + + /// + /// Indicates whether the current entity has an identity, eg. Id. + /// + public virtual bool HasIdentity + { + get + { + return _hasIdentity; + } + protected set + { + _hasIdentity = value; + } + } + + public static bool operator ==(Item left, Item right) + { + /*if (ReferenceEquals(null, left)) + return false; + + if(ReferenceEquals(null, right)) + return false;*/ + + return ReferenceEquals(left, right); + + return left.Equals(right); + } + + public static bool operator !=(Item left, Item right) + { + return !(left == right); + } + + /*public virtual bool SameIdentityAs(IEntity other) + { + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + + return SameIdentityAs(other as Entity); + } + + public virtual bool Equals(Entity other) + { + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + + return SameIdentityAs(other); + } + + public virtual Type GetRealType() + { + return GetType(); + } + + public virtual bool SameIdentityAs(Entity other) + { + if (ReferenceEquals(null, other)) + return false; + + if (ReferenceEquals(this, other)) + return true; + + if (GetType() == other.GetRealType() && HasIdentity && other.HasIdentity) + return other.Id.Equals(Id); + + return false; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + + return SameIdentityAs(obj as IEntity); + } + + public override int GetHashCode() + { + if (!_hash.HasValue) + _hash = !HasIdentity ? new int?(base.GetHashCode()) : new int?(Id.GetHashCode() * 397 ^ GetType().GetHashCode()); + return _hash.Value; + }*/ + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index cb59d155ac..b85c4c05b1 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -482,6 +482,50 @@ namespace Umbraco.Tests.Services Assert.That(published, Is.True); } + [Test] + public void Can_Get_Published_Descendant_Versions() + { + // Arrange + var contentService = ServiceContext.ContentService; + var root = contentService.GetById(1046); + var rootPublished = contentService.Publish(root); + var content = contentService.GetById(1048); + content.Properties["title"].Value = content.Properties["title"].Value + " Published"; + bool published = contentService.SaveAndPublish(content); + + var publishedVersion = content.Version; + + content.Properties["title"].Value = content.Properties["title"].Value + " Saved"; + contentService.Save(content); + + var savedVersion = content.Version; + + // Act + var publishedDescendants = ((ContentService) contentService).GetPublishedDescendants(root); + + // Assert + Assert.That(rootPublished, Is.True); + Assert.That(published, Is.True); + Assert.That(publishedDescendants.Any(x => x.Version == publishedVersion), Is.True); + Assert.That(publishedDescendants.Any(x => x.Version == savedVersion), Is.False); + + //Ensure that the published content version has the correct property value and is marked as published + var publishedContentVersion = publishedDescendants.First(x => x.Version == publishedVersion); + Assert.That(publishedContentVersion.Published, Is.True); + Assert.That(publishedContentVersion.Properties["title"].Value, Contains.Substring("Published")); + + //Ensure that the saved content version has the correct property value and is not marked as published + var savedContentVersion = contentService.GetByVersion(savedVersion); + Assert.That(savedContentVersion.Published, Is.False); + Assert.That(savedContentVersion.Properties["title"].Value, Contains.Substring("Saved")); + + //Ensure that the latest version of the content is the saved and not-yet-published one + var currentContent = contentService.GetById(1048); + Assert.That(currentContent.Published, Is.False); + Assert.That(currentContent.Properties["title"].Value, Contains.Substring("Saved")); + Assert.That(currentContent.Version, Is.EqualTo(savedVersion)); + } + [Test] public void Can_Save_Content() { diff --git a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs index f791894f92..69f6811846 100644 --- a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs +++ b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs @@ -230,11 +230,10 @@ namespace Umbraco.Tests.Services.Importing // Arrange string strXml = ImportResources.SingleDocType; var docTypeElement = XElement.Parse(strXml); - var packagingService = ServiceContext.PackagingService; // Act - var contentTypes = packagingService.ImportContentTypes(docTypeElement); - var contentTypesUpdated = packagingService.ImportContentTypes(docTypeElement); + var contentTypes = ServiceContext.PackagingService.ImportContentTypes(docTypeElement); + var contentTypesUpdated = ServiceContext.PackagingService.ImportContentTypes(docTypeElement); // Assert Assert.That(contentTypes.Any(), Is.True); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 221327e627..3a3650897f 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -203,6 +203,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 0cbdab8bb3..6bb9dbcb9d 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -387,6 +387,13 @@ Title.ascx + + ContentTypeControlNew.ascx + ASPXCodeBehind + + + ContentTypeControlNew.ascx + create.aspx ASPXCodeBehind @@ -477,6 +484,13 @@ QuickSearch.ascx + + EditNodeTypeNew.aspx + ASPXCodeBehind + + + EditNodeTypeNew.aspx + ASPXCodeBehind @@ -2039,6 +2053,7 @@ Designer + Web.Template.config Designer diff --git a/src/Umbraco.Web.UI/Views/Web.config.transform b/src/Umbraco.Web.UI/Views/Web.config.transform new file mode 100644 index 0000000000..f8333ed8ea --- /dev/null +++ b/src/Umbraco.Web.UI/Views/Web.config.transform @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx index 2a0af303c9..43eb7fb9a1 100644 --- a/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx +++ b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx @@ -1,5 +1,5 @@ <%@ Control Language="c#" AutoEventWireup="True" Codebehind="ContentTypeControlNew.ascx.cs" - Inherits="umbraco.controls.ContentTypeControlNew" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %> + Inherits="Umbraco.Web.UI.Umbraco.Controls.ContentTypeControlNew" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> <%@ Register TagPrefix="cc2" Namespace="umbraco.uicontrols" Assembly="controls" %> diff --git a/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.cs b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.cs new file mode 100644 index 0000000000..c3459b752e --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; + +namespace Umbraco.Web.UI.Umbraco.Controls +{ + public partial class ContentTypeControlNew : global::umbraco.controls.ContentTypeControlNew + { + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.designer.cs b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.designer.cs new file mode 100644 index 0000000000..582c67837d --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.designer.cs @@ -0,0 +1,15 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Umbraco.Web.UI.Umbraco.Controls { + + + public partial class ContentTypeControlNew { + } +} diff --git a/src/Umbraco.Web.UI/umbraco/settings/EditNodeTypeNew.aspx b/src/Umbraco.Web.UI/umbraco/settings/EditNodeTypeNew.aspx index 73975e64f2..cdda810a57 100644 --- a/src/Umbraco.Web.UI/umbraco/settings/EditNodeTypeNew.aspx +++ b/src/Umbraco.Web.UI/umbraco/settings/EditNodeTypeNew.aspx @@ -1,5 +1,5 @@ <%@ Page Language="c#" CodeBehind="EditNodeTypeNew.aspx.cs" AutoEventWireup="True" - Trace="false" Inherits="umbraco.settings.EditContentTypeNew" MasterPageFile="../masterpages/umbracoPage.Master" %> + Async="true" AsyncTimeOut="300" Trace="false" Inherits="Umbraco.Web.UI.Umbraco.Settings.EditNodeTypeNew" MasterPageFile="../masterpages/umbracoPage.Master" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> <%@ Register TagPrefix="uc1" TagName="ContentTypeControlNew" Src="../controls/ContentTypeControlNew.ascx" %> @@ -9,7 +9,7 @@ jQuery(document).ready(function () { // Auto selection/de-selection of default template based on allow templates - jQuery("#<%= templateList.ClientID %> input[type='checkbox']").on("change", function () { + jQuery("#<%= templateList.ClientID %> input[type='checkbox']").on("change", function() { var checkbox = jQuery(this); var ddl = jQuery("#<%= ddlTemplates.ClientID %>"); // If default template is not set, and an allowed template is selected, auto-select the default template @@ -17,7 +17,8 @@ if (ddl.val() == "0") { ddl.val(checkbox.val()); } - } else { + } + else { // If allowed template has been de-selected, and it's selected as the default, then de-select the default template if (ddl.val() == checkbox.val()) { ddl.val("0"); diff --git a/src/Umbraco.Web.UI/umbraco/settings/EditNodeTypeNew.aspx.cs b/src/Umbraco.Web.UI/umbraco/settings/EditNodeTypeNew.aspx.cs new file mode 100644 index 0000000000..f5705d25de --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/settings/EditNodeTypeNew.aspx.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; + +namespace Umbraco.Web.UI.Umbraco.Settings +{ + public partial class EditNodeTypeNew : global::umbraco.settings.EditContentTypeNew + { + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/settings/EditNodeTypeNew.aspx.designer.cs b/src/Umbraco.Web.UI/umbraco/settings/EditNodeTypeNew.aspx.designer.cs new file mode 100644 index 0000000000..349f5daab1 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/settings/EditNodeTypeNew.aspx.designer.cs @@ -0,0 +1,15 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Umbraco.Web.UI.Umbraco.Settings { + + + public partial class EditNodeTypeNew { + } +} diff --git a/src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx.cs b/src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx.cs index 0a184c9e04..9d6db3c41a 100644 --- a/src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx.cs +++ b/src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx.cs @@ -7,6 +7,7 @@ using System.Web.UI; using System.Web.UI.WebControls; using Umbraco.Core.IO; using Umbraco.Web.Trees; +using Umbraco.Web.UI.Controls; using umbraco; using umbraco.BasePages; using umbraco.cms.businesslogic.template; @@ -166,7 +167,7 @@ namespace Umbraco.Web.UI.Umbraco.Settings.Views 640, 550); umbDictionary.AltText = "Insert umbraco dictionary item"; - var macroSplitButton = new Controls.InsertMacroSplitButton + var macroSplitButton = new InsertMacroSplitButton { ClientCallbackInsertMacroMarkup = "function(alias) {editViewEditor.insertMacroMarkup(alias);}", ClientCallbackOpenMacroModel = "function(alias) {editViewEditor.openMacroModal(alias);}" diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 0e32c19aec..e3adc8ff6d 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -564,6 +564,9 @@ ASPXCodeBehind + + ASPXCodeBehind + ASPXCodeBehind @@ -758,13 +761,6 @@ UploadMediaImage.ascx - - ContentTypeControlNew.ascx - ASPXCodeBehind - - - ContentTypeControlNew.ascx - GenericProperty.ascx @@ -1478,13 +1474,6 @@ EditMediaType.aspx - - EditNodeTypeNew.aspx - ASPXCodeBehind - - - EditNodeTypeNew.aspx - editScript.aspx ASPXCodeBehind @@ -1822,9 +1811,6 @@ ASPXCodeBehind - - ASPXCodeBehind - ASPXCodeBehind @@ -1965,9 +1951,6 @@ ASPXCodeBehind - - ASPXCodeBehind - ASPXCodeBehind diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadScripts.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadScripts.cs index bea5e7d7a2..2dca3d3fba 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadScripts.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadScripts.cs @@ -3,10 +3,12 @@ using System.Collections; using System.Collections.Generic; using System.Data; using System.IO; +using System.Linq; using System.Text; using System.Web; using System.Xml; using System.Configuration; +using Umbraco.Core; using umbraco.BasePages; using umbraco.BusinessLogic; using umbraco.businesslogic; @@ -53,7 +55,10 @@ namespace umbraco protected override string FilePath { - get { return SystemDirectories.Scripts + "/"; } + get + { + return SystemDirectories.Scripts + "/"; + } } protected override string FileSearchPattern diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx deleted file mode 100644 index 2a0af303c9..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx +++ /dev/null @@ -1,114 +0,0 @@ -<%@ Control Language="c#" AutoEventWireup="True" Codebehind="ContentTypeControlNew.ascx.cs" - Inherits="umbraco.controls.ContentTypeControlNew" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %> -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> -<%@ Register TagPrefix="cc2" Namespace="umbraco.uicontrols" Assembly="controls" %> - - - - - - - - -

<%=umbraco.ui.GetText("settings", "contentTypeEnabled")%>
<%=umbraco.ui.GetText("settings", "contentTypeUses")%> <%=umbraco.ui.GetText("settings", "asAContentMasterType")%>

-
- - - -   - - - - - - - - - - - - - - - - - - - - - - -

-

-
-
- - - - - - - - - - - - - - - - - -
- -
-
- -
- -
-
- - - -
-
- - - - - -
- Only Content Types with this checked can be created at the root level of Content and Media trees -
-
- - - - - - - -
- - - - -

Master Content Type enabled
This Content Type uses as a Master Content Type. Properties from Master Content Types are not shown and can only be edited on the Master Content Type itself

-
- - -
- - -
-
-
-<%-- cannot put a <%= block here 'cos it prevents the Controls collection from being modified = use a literal --%> - \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs index f9f1626a5a..7d8ad896b2 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Data; @@ -6,13 +6,17 @@ using System.IO; using System.Linq; using System.Web; using System.Web.Mvc; +using System.Threading.Tasks; +using System.Web; using System.Web.Routing; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using ClientDependency.Core; using Umbraco.Core; +using Umbraco.Core.Logging; using Umbraco.Core.Models; +using umbraco.BusinessLogic; using umbraco.cms.businesslogic; using umbraco.cms.businesslogic.propertytype; using umbraco.cms.businesslogic.web; @@ -59,10 +63,13 @@ namespace umbraco.controls private ArrayList _genericProperties = new ArrayList(); private ArrayList _sortLists = new ArrayList(); - protected override void OnInit(EventArgs e) + //the async saving task + private Action _asyncSaveTask; + + override protected void OnInit(EventArgs e) { base.OnInit(e); - + LoadContentType(); SetupInfoPane(); @@ -103,108 +110,221 @@ namespace umbraco.controls checkTxtAliasJs.Text = string.Format("checkAlias('{0}');", txtAlias.ClientID); } - protected void save_click(object sender, ImageClickEventArgs e) + //SD: this is temporary in v4, in v6 we have a proper user control hierarchy + //containing this property. + //this is required due to this issue: http://issues.umbraco.org/issue/u4-493 + //because we need to execute some code in async but due to the localization + //framework requiring an httpcontext.current, it will not work. + //http://issues.umbraco.org/issue/u4-2143 + //so, we are going to make a property here and ensure that the basepage has + //resolved the user before we execute the async task so that in this method + //our calls to ui.text will include the current user and not rely on the + //httpcontext.current. This also improves performance: + // http://issues.umbraco.org/issue/U4-2142 + private User CurrentUser { - // 2011 01 06 - APN - Modified method to update Xml caches if a doctype alias changed, - // also added calls to update the tree if the name has changed - // --- + get { return ((BasePage)Page).getUser(); } + } - // Keep a reference of the original doctype alias and name - var originalDocTypeAlias = _contentType.Alias; - var originalDocTypeName = _contentType.Text; - - // Check if the doctype alias has changed as a result of either the user input or - // the alias checking performed upon saving - var docTypeAliasChanged = (string.Compare(originalDocTypeAlias, txtAlias.Text, true) != 0); - var docTypeNameChanged = (string.Compare(originalDocTypeName, txtName.Text, true) != 0); - - var ea = new SaveClickEventArgs("Saved"); - ea.IconType = BasePage.speechBubbleIcon.success; - - //NOTE The saving of the 5 properties (Name, Alias, Icon, Description and Thumbnail) are divided - //to avoid the multiple cache flushing when each property is set using the legacy ContentType class, - //which has been reduced to the else-clause. - //For IContentType and IMediaType the cache will only be flushed upon saving. - if (_contentType.ContentTypeItem is IContentType || _contentType.ContentTypeItem is IMediaType) + /// + /// A class to track the async state for saving the doc type + /// + private class SaveAsyncState + { + public SaveAsyncState( + SaveClickEventArgs saveArgs, + string originalAlias, + string originalName, + string newAlias, + string newName) { - _contentType.ContentTypeItem.Name = txtName.Text; - _contentType.ContentTypeItem.Alias = txtAlias.Text; - _contentType.ContentTypeItem.Icon = ddlIcons.SelectedValue; - _contentType.ContentTypeItem.Description = description.Text; - _contentType.ContentTypeItem.Thumbnail = ddlThumbnails.SelectedValue; - _contentType.ContentTypeItem.AllowedAsRoot = allowAtRoot.Checked; - - int i = 0; - var ids = SaveAllowedChildTypes(); - _contentType.ContentTypeItem.AllowedContentTypes = ids.Select(x => new ContentTypeSort { Id = new Lazy(() => x), SortOrder = i++ }); - - var tabs = SaveTabs(); - foreach (var tab in tabs) - { - if (_contentType.ContentTypeItem.PropertyGroups.Contains(tab.Item2)) - { - _contentType.ContentTypeItem.PropertyGroups[tab.Item2].SortOrder = tab.Item3; - } - else - { - _contentType.ContentTypeItem.PropertyGroups.Add(new PropertyGroup { Id = tab.Item1, Name = tab.Item2, SortOrder = tab.Item3 }); - } - } - - SavePropertyType(ref ea, _contentType.ContentTypeItem); - UpdatePropertyTypes(_contentType.ContentTypeItem); - - if (DocumentTypeCallback != null) - { - var documentType = _contentType as DocumentType; - if (documentType != null) - { - var result = DocumentTypeCallback(documentType); - } - } - - _contentType.Save(); + SaveArgs = saveArgs; + OriginalAlias = originalAlias; + OriginalName = originalName; + NewAlias = newAlias; + NewName = newName; } - else //Legacy approach for supporting MemberType + + public SaveClickEventArgs SaveArgs { get; private set; } + public string OriginalAlias { get; private set; } + public string OriginalName { get; private set; } + public string NewAlias { get; private set; } + public string NewName { get; private set; } + + public bool HasAliasChanged() { - if (docTypeNameChanged) - _contentType.Text = txtName.Text; - - if (docTypeAliasChanged) - _contentType.Alias = txtAlias.Text; - - _contentType.IconUrl = ddlIcons.SelectedValue; - _contentType.Description = description.Text; - _contentType.Thumbnail = ddlThumbnails.SelectedValue; - - SavePropertyTypesLegacy(ref ea); - - var tabs = SaveTabs(); - foreach (var tab in tabs) - { - _contentType.SetTabName(tab.Item1, tab.Item2); - _contentType.SetTabSortOrder(tab.Item1, tab.Item3); - } - - _contentType.AllowedChildContentTypeIDs = SaveAllowedChildTypes(); - _contentType.AllowAtRoot = allowAtRoot.Checked; - - _contentType.Save(); + return (string.Compare(OriginalAlias, NewAlias, StringComparison.OrdinalIgnoreCase) != 0); } + public bool HasNameChanged() + { + return (string.Compare(OriginalName, NewName, StringComparison.OrdinalIgnoreCase) != 0); + } + } + + /// + /// Called asynchronously in order to persist all of the data to the database + /// + /// + /// + /// + /// + /// + /// + /// This can be a long running operation depending on how many content nodes exist and if the node type alias + /// has changed as this will need to regenerate XML for all of the nodes. + /// + private IAsyncResult BeginAsyncSaveOperation(object sender, EventArgs e, AsyncCallback cb, object state) + { + Trace.Write("ContentTypeControlNew", "Start async operation"); + + //get the args from the async state + var args = (SaveAsyncState)state; + + //start the task + var result = _asyncSaveTask.BeginInvoke(args, cb, args); + return result; + } + + /// + /// Occurs once the async database save operation has completed + /// + /// + /// + /// This updates the UI elements + /// + private void EndAsyncSaveOperation(IAsyncResult ar) + { + Trace.Write("ContentTypeControlNew", "ending async operation"); + + //get the args from the async state + var state = (SaveAsyncState)ar.AsyncState; // reload content type (due to caching) LoadContentType(); BindTabs(); - BindDataGenericProperties(true); // we need to re-bind the alias as the SafeAlias method can have changed it txtAlias.Text = _contentType.Alias; - RaiseBubbleEvent(new object(), ea); + RaiseBubbleEvent(new object(), state.SaveArgs); - if (docTypeNameChanged) + if (state.HasNameChanged()) UpdateTreeNode(); + + Trace.Write("ContentTypeControlNew", "async operation ended"); + + //complete it + _asyncSaveTask.EndInvoke(ar); + } + + private void HandleAsyncSaveTimeout(IAsyncResult ar) + { + Trace.Write("ContentTypeControlNew", "async operation timed out!"); + + LogHelper.Error( + "The content type saving operation timed out", + new TimeoutException("The content type saving operation timed out. This could cause problems because the xml for the content node might not have been generated. ")); + + } + + /// + /// The save button click event handlers + /// + /// + /// + protected void save_click(object sender, ImageClickEventArgs e) + { + + var state = new SaveAsyncState(new SaveClickEventArgs("Saved") + { + IconType = BasePage.speechBubbleIcon.success + }, _contentType.Alias, _contentType.Text, txtAlias.Text, txtName.Text); + + //Add the async operation to the page + Page.RegisterAsyncTask(new PageAsyncTask(BeginAsyncSaveOperation, EndAsyncSaveOperation, HandleAsyncSaveTimeout, state)); + + //create the save task to be executed async + _asyncSaveTask = asyncState => + { + Trace.Write("ContentTypeControlNew", "executing task"); + + //NOTE The saving of the 5 properties (Name, Alias, Icon, Description and Thumbnail) are divided + //to avoid the multiple cache flushing when each property is set using the legacy ContentType class, + //which has been reduced to the else-clause. + //For IContentType and IMediaType the cache will only be flushed upon saving. + if (_contentType.ContentTypeItem is IContentType || _contentType.ContentTypeItem is IMediaType) + { + _contentType.ContentTypeItem.Name = txtName.Text; + _contentType.ContentTypeItem.Alias = txtAlias.Text; + _contentType.ContentTypeItem.Icon = ddlIcons.SelectedValue; + _contentType.ContentTypeItem.Description = description.Text; + _contentType.ContentTypeItem.Thumbnail = ddlThumbnails.SelectedValue; + _contentType.ContentTypeItem.AllowedAsRoot = allowAtRoot.Checked; + + int i = 0; + var ids = SaveAllowedChildTypes(); + _contentType.ContentTypeItem.AllowedContentTypes = ids.Select(x => new ContentTypeSort {Id = new Lazy(() => x), SortOrder = i++}); + + var tabs = SaveTabs(); + foreach (var tab in tabs) + { + if (_contentType.ContentTypeItem.PropertyGroups.Contains(tab.Item2)) + { + _contentType.ContentTypeItem.PropertyGroups[tab.Item2].SortOrder = tab.Item3; + } + else + { + _contentType.ContentTypeItem.PropertyGroups.Add(new PropertyGroup {Id = tab.Item1, Name = tab.Item2, SortOrder = tab.Item3}); + } + } + + SavePropertyType(asyncState.SaveArgs, _contentType.ContentTypeItem); + UpdatePropertyTypes(_contentType.ContentTypeItem); + + if (DocumentTypeCallback != null) + { + var documentType = _contentType as DocumentType; + if (documentType != null) + { + var result = DocumentTypeCallback(documentType); + } + } + + _contentType.Save(); + } + else //Legacy approach for supporting MemberType + { + if (asyncState.HasNameChanged()) + _contentType.Text = txtName.Text; + + if (asyncState.HasAliasChanged()) + _contentType.Alias = txtAlias.Text; + + _contentType.IconUrl = ddlIcons.SelectedValue; + _contentType.Description = description.Text; + _contentType.Thumbnail = ddlThumbnails.SelectedValue; + + SavePropertyTypesLegacy(asyncState.SaveArgs); + + var tabs = SaveTabs(); + foreach (var tab in tabs) + { + _contentType.SetTabName(tab.Item1, tab.Item2); + _contentType.SetTabSortOrder(tab.Item1, tab.Item3); + } + + _contentType.AllowedChildContentTypeIDs = SaveAllowedChildTypes(); + _contentType.AllowAtRoot = allowAtRoot.Checked; + + _contentType.Save(); + } + + Trace.Write("ContentTypeControlNew", "task completing"); + }; + + //execute the async tasks + Page.ExecuteRegisteredAsyncTasks(); } /// @@ -257,7 +377,7 @@ namespace umbraco.controls Save.Click += save_click; Save.ImageUrl = UmbracoPath + "/images/editor/save.gif"; - Save.AlternateText = ui.Text("save"); + Save.AlternateText = ui.Text("save", CurrentUser); Save.ID = "save"; var dirInfo = new DirectoryInfo(UmbracoContext.Current.Server.MapPath(SystemDirectories.Umbraco + "/images/umbraco")); @@ -273,8 +393,8 @@ namespace umbraco.controls foreach (var iconClass in CMSNode.DefaultIconClasses.Where(iconClass => iconClass.Equals(".sprNew", StringComparison.InvariantCultureIgnoreCase) == false)) { // Still shows the selected even if we tell it to hide sprite duplicates so as not to break an existing selection - if (_contentType.IconUrl.Equals(iconClass, StringComparison.InvariantCultureIgnoreCase) == false - && UmbracoSettings.IconPickerBehaviour == IconPickerBehaviour.HideSpriteDuplicates + if (_contentType.IconUrl.Equals(iconClass, StringComparison.InvariantCultureIgnoreCase) == false + && UmbracoSettings.IconPickerBehaviour == IconPickerBehaviour.HideSpriteDuplicates && diskFileNames.Contains(IconClassToIconFileName(iconClass))) continue; @@ -620,7 +740,7 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); } - private void SavePropertyType(ref SaveClickEventArgs e, IContentTypeComposition contentTypeItem) + private void SavePropertyType(SaveClickEventArgs e, IContentTypeComposition contentTypeItem) { this.CreateChildControls(); @@ -641,7 +761,6 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); ValidationRegExp = gpData.Validation, Description = gpData.Description }; - //gpData.Tab == 0 Generic Properties / No Group if (gpData.Tab == 0) { @@ -666,7 +785,6 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); } } } - gpData.Clear(); } else @@ -676,20 +794,16 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); } } } - private void UpdatePropertyTypes(IContentTypeComposition contentTypeItem) { //Loop through the _genericProperties ArrayList and update all existing PropertyTypes foreach (GenericPropertyWrapper gpw in _genericProperties) { if (gpw.PropertyType == null) continue; - if (contentTypeItem.PropertyTypes == null || contentTypeItem.PropertyTypes.Any(x => x.Alias == gpw.PropertyType.Alias) == false) continue; var propertyType = contentTypeItem.PropertyTypes.First(x => x.Alias == gpw.PropertyType.Alias); if (propertyType == null) continue; - var dataTypeDefinition = ApplicationContext.Current.Services.DataTypeService.GetDataTypeDefinitionById(gpw.GenricPropertyControl.Type); - propertyType.Alias = gpw.GenricPropertyControl.Alias; propertyType.Name = gpw.GenricPropertyControl.Name; propertyType.Description = gpw.GenricPropertyControl.Description; @@ -698,7 +812,6 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); propertyType.DataTypeDatabaseType = dataTypeDefinition.DatabaseType; propertyType.DataTypeDefinitionId = dataTypeDefinition.Id; propertyType.DataTypeId = dataTypeDefinition.ControlId; - if (propertyType.PropertyGroupId == null || propertyType.PropertyGroupId.Value != gpw.GenricPropertyControl.Tab) { if (gpw.GenricPropertyControl.Tab == 0) @@ -722,7 +835,7 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); } } } - + //Update the SortOrder of the PropertyTypes foreach (HtmlInputHidden propSorter in _sortLists) { @@ -738,7 +851,6 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); for (int i = 0; i < tempSO.Length; i++) { string propSO = tempSO[i].Substring(propSOPosition); - int propertyTypeId = int.Parse(propSO); if (contentTypeItem.PropertyTypes != null && contentTypeItem.PropertyTypes.Any(x => x.Id == propertyTypeId)) @@ -751,7 +863,7 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); } } - private void SavePropertyTypesLegacy(ref SaveClickEventArgs e) + private void SavePropertyTypesLegacy(SaveClickEventArgs e) { this.CreateChildControls(); @@ -778,7 +890,7 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); } else { - e.Message = ui.Text("contentTypeDublicatePropertyType"); + e.Message = ui.Text("contentTypeDublicatePropertyType", CurrentUser); e.IconType = BasePage.speechBubbleIcon.warning; } } @@ -869,8 +981,8 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); pt.delete(); } - RaiseBubbleEvent(new object(), new SaveClickEventArgs("Property ´" + rawName + "´ deleted")); - + RaiseBubbleEvent(new object(), new SaveClickEventArgs("Property ´" + rawName + "´ deleted")); + BindDataGenericProperties(false); } } @@ -1107,5 +1219,365 @@ Umbraco.Controls.TabView.onActiveTabChange(function(tabviewid, tabid, tabs) { } #endregion + + /// + /// TabView1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.TabView TabView1; + + /// + /// pnlGeneral control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlGeneral; + + /// + /// pnlTab control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlTab; + + /// + /// PaneTabsInherited control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane PaneTabsInherited; + + /// + /// tabsMasterContentTypeName control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal tabsMasterContentTypeName; + + /// + /// Pane2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane Pane2; + + /// + /// pp_newTab control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_newTab; + + /// + /// txtNewTab control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtNewTab; + + /// + /// btnNewTab control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button btnNewTab; + + /// + /// Pane1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane Pane1; + + /// + /// dgTabs control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.DataGrid dgTabs; + + /// + /// lttNoTabs control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal lttNoTabs; + + /// + /// pnlInfo control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlInfo; + + /// + /// Pane3 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane Pane3; + + /// + /// pp_name control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_name; + + /// + /// txtName control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtName; + + /// + /// RequiredFieldValidator1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1; + + /// + /// pp_alias control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_alias; + + /// + /// txtAlias control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtAlias; + + /// + /// pp_icon control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_icon; + + /// + /// ddlIcons control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.DropDownList ddlIcons; + + /// + /// pp_thumbnail control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_thumbnail; + + /// + /// ddlThumbnails control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.DropDownList ddlThumbnails; + + /// + /// pp_description control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_description; + + /// + /// description control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox description; + + /// + /// pnlStructure control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlStructure; + + /// + /// Pane6 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane Pane6; + + /// + /// pp_Root control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_Root; + + /// + /// allowAtRoot control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CheckBox allowAtRoot; + + /// + /// Pane5 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane Pane5; + + /// + /// pp_allowedChildren control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_allowedChildren; + + /// + /// lstAllowedContentTypes control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CheckBoxList lstAllowedContentTypes; + + /// + /// PlaceHolderAllowedContentTypes control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder PlaceHolderAllowedContentTypes; + + /// + /// pnlProperties control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlProperties; + + /// + /// PanePropertiesInherited control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane PanePropertiesInherited; + + /// + /// propertiesMasterContentTypeName control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal propertiesMasterContentTypeName; + + /// + /// Pane4 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane Pane4; + + /// + /// PropertyTypeNew control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder PropertyTypeNew; + + /// + /// PropertyTypes control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder PropertyTypes; + + /// + /// theClientId control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal theClientId; } } \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.designer.cs deleted file mode 100644 index 0bbd56ebbd..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.designer.cs +++ /dev/null @@ -1,375 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.controls { - - - public partial class ContentTypeControlNew { - - /// - /// TabView1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.TabView TabView1; - - /// - /// pnlGeneral control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel pnlGeneral; - - /// - /// pnlTab control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel pnlTab; - - /// - /// PaneTabsInherited control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane PaneTabsInherited; - - /// - /// tabsMasterContentTypeName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal tabsMasterContentTypeName; - - /// - /// Pane2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane Pane2; - - /// - /// pp_newTab control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_newTab; - - /// - /// txtNewTab control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox txtNewTab; - - /// - /// btnNewTab control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button btnNewTab; - - /// - /// Pane1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane Pane1; - - /// - /// dgTabs control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DataGrid dgTabs; - - /// - /// lttNoTabs control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lttNoTabs; - - /// - /// pnlInfo control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel pnlInfo; - - /// - /// Pane3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane Pane3; - - /// - /// pp_name control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_name; - - /// - /// txtName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox txtName; - - /// - /// RequiredFieldValidator1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1; - - /// - /// pp_alias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_alias; - - /// - /// txtAlias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox txtAlias; - - /// - /// pp_icon control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_icon; - - /// - /// ddlIcons control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList ddlIcons; - - /// - /// pp_thumbnail control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_thumbnail; - - /// - /// ddlThumbnails control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList ddlThumbnails; - - /// - /// pp_description control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_description; - - /// - /// description control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox description; - - /// - /// pnlStructure control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel pnlStructure; - - /// - /// Pane6 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane Pane6; - - /// - /// pp_Root control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_Root; - - /// - /// allowAtRoot control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBox allowAtRoot; - - /// - /// Pane5 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane Pane5; - - /// - /// pp_allowedChildren control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_allowedChildren; - - /// - /// lstAllowedContentTypes control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList lstAllowedContentTypes; - - /// - /// PlaceHolderAllowedContentTypes control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder PlaceHolderAllowedContentTypes; - - /// - /// pnlProperties control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel pnlProperties; - - /// - /// PanePropertiesInherited control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane PanePropertiesInherited; - - /// - /// propertiesMasterContentTypeName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal propertiesMasterContentTypeName; - - /// - /// Pane4 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane Pane4; - - /// - /// PropertyTypeNew control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder PropertyTypeNew; - - /// - /// PropertyTypes control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder PropertyTypes; - - /// - /// checkTxtAliasJs control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal checkTxtAliasJs; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/GenericProperties/GenericProperty.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/GenericProperties/GenericProperty.ascx.cs index baa67b17bc..955b885692 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/GenericProperties/GenericProperty.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/GenericProperties/GenericProperty.ascx.cs @@ -8,8 +8,9 @@ using System.Linq; using System.Web.UI.WebControls; using ClientDependency.Core; using Umbraco.Core; +using Umbraco.Core.IO; using umbraco.BasePages; -using umbraco.IO; +using umbraco.BusinessLogic; using umbraco.cms.businesslogic.propertytype; namespace umbraco.controls.GenericProperties @@ -141,6 +142,22 @@ namespace umbraco.controls.GenericProperties } } + //SD: this is temporary in v4, in v6 we have a proper user control hierarchy + //containing this property. + //this is required due to this issue: http://issues.umbraco.org/issue/u4-493 + //because we need to execute some code in async but due to the localization + //framework requiring an httpcontext.current, it will not work. + //http://issues.umbraco.org/issue/u4-2143 + //so, we are going to make a property here and ensure that the basepage has + //resolved the user before we execute the async task so that in this method + //our calls to ui.text will include the current user and not rely on the + //httpcontext.current. This also makes it perform better: + // http://issues.umbraco.org/issue/U4-2142 + private User CurrentUser + { + get { return ((BasePage) Page).getUser(); } + } + public void UpdateInterface() { // Name and alias @@ -155,11 +172,11 @@ namespace umbraco.controls.GenericProperties DeleteButton.Visible = true; DeleteButton.ImageUrl = SystemDirectories.Umbraco + "/images/delete_button.png"; DeleteButton.Attributes.Add("style", "float: right; cursor: hand;"); - DeleteButton.Attributes.Add("onclick", "return confirm('" + ui.Text("areyousure") + "');"); + DeleteButton.Attributes.Add("onclick", "return confirm('" + ui.Text("areyousure", CurrentUser) + "');"); DeleteButton2.Visible = true; DeleteButton2.ImageUrl = SystemDirectories.Umbraco + "/images/delete_button.png"; DeleteButton2.Attributes.Add("style", "float: right; cursor: hand;"); - DeleteButton2.Attributes.Add("onclick", "return confirm('" + ui.Text("areyousure") + "');"); + DeleteButton2.Attributes.Add("onclick", "return confirm('" + ui.Text("areyousure", CurrentUser) + "');"); } else { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/MediaTypeTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/MediaTypeTasks.cs index 45955828ca..65af089855 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/MediaTypeTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/MediaTypeTasks.cs @@ -42,10 +42,10 @@ namespace umbraco } public bool Save() - { - var mediaType = cms.businesslogic.media.MediaType.MakeNew(User.GetUser(_userID), Alias.Replace("'", "''"), - ParentID); - + { + var mediaType = cms.businesslogic.media.MediaType.MakeNew(User.GetUser(_userID), Alias.Replace("'", "''")); + mediaType.IconUrl = UmbracoSettings.IconPickerBehaviour == IconPickerBehaviour.HideFileDuplicates ? ".sprTreeFolder" : "folder.gif"; + if (ParentID != -1) { mediaType.MasterContentType = ParentID; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs index aec5421174..23005d62d2 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs @@ -72,8 +72,10 @@ namespace umbraco.presentation.umbraco.dialogs var xd = new XmlDocument(); xd.Load(tempFile.Value); + var userId = base.getUser().Id; + var element = XElement.Parse(xd.InnerXml); - var importContentTypes = ApplicationContext.Current.Services.PackagingService.ImportContentTypes(element); + var importContentTypes = ApplicationContext.Current.Services.PackagingService.ImportContentTypes(element, userId); var contentType = importContentTypes.FirstOrDefault(); if (contentType != null) dtNameConfirm.Text = contentType.Name; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx deleted file mode 100644 index 73975e64f2..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx +++ /dev/null @@ -1,58 +0,0 @@ -<%@ Page Language="c#" CodeBehind="EditNodeTypeNew.aspx.cs" AutoEventWireup="True" - Trace="false" Inherits="umbraco.settings.EditContentTypeNew" MasterPageFile="../masterpages/umbracoPage.Master" %> - -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> -<%@ Register TagPrefix="uc1" TagName="ContentTypeControlNew" Src="../controls/ContentTypeControlNew.ascx" %> - - - - - - - - - -
- -
-
- - - -
- -
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.cs index 8aefde9786..3dd21c5082 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Globalization; @@ -133,5 +133,32 @@ namespace umbraco.settings if (ddlTemplatesSelect != null) ddlTemplatesSelect.Selected = true; } + + /// + /// tmpPane control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane tmpPane; + + /// + /// templateList control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CheckBoxList templateList; + + /// + /// ddlTemplates control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.DropDownList ddlTemplates; } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.designer.cs deleted file mode 100644 index 19f03326bd..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.designer.cs +++ /dev/null @@ -1,42 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.settings { - - - public partial class EditContentTypeNew { - - /// - /// tmpPane control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane tmpPane; - - /// - /// templateList control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList templateList; - - /// - /// ddlTemplates control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList ddlTemplates; - } -} diff --git a/src/umbraco.cms/businesslogic/CMSNode.cs b/src/umbraco.cms/businesslogic/CMSNode.cs index 64858dce8a..1087a5b5ae 100644 --- a/src/umbraco.cms/businesslogic/CMSNode.cs +++ b/src/umbraco.cms/businesslogic/CMSNode.cs @@ -759,6 +759,7 @@ order by level,sortOrder"; public virtual int ParentId { get { return _parentid; } + internal set { _parentid = value; } } /// diff --git a/src/umbraco.cms/businesslogic/Packager/Installer.cs b/src/umbraco.cms/businesslogic/Packager/Installer.cs index a3d8618a57..c890dbe6d7 100644 --- a/src/umbraco.cms/businesslogic/Packager/Installer.cs +++ b/src/umbraco.cms/businesslogic/Packager/Installer.cs @@ -936,7 +936,7 @@ namespace umbraco.cms.businesslogic.packager public static void ImportDocumentType(XmlNode n, User u, bool ImportStructure) { var element = n.GetXElement(); - var contentTypes = ApplicationContext.Current.Services.PackagingService.ImportContentTypes(element, u.Id); + var contentTypes = ApplicationContext.Current.Services.PackagingService.ImportContentTypes(element, ImportStructure, u.Id); } #endregion diff --git a/src/umbraco.cms/businesslogic/web/Document.cs b/src/umbraco.cms/businesslogic/web/Document.cs index 90caed38e9..61d06e0e41 100644 --- a/src/umbraco.cms/businesslogic/web/Document.cs +++ b/src/umbraco.cms/businesslogic/web/Document.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; +using System.Threading; using System.Xml; using Umbraco.Core; using Umbraco.Core.Models; @@ -402,8 +403,9 @@ namespace umbraco.cms.businesslogic.web var children = ApplicationContext.Current.Services.ContentService.GetChildrenByName(NodeId, searchString); return children.Select(x => new Document(x)).ToList(); } - + [Obsolete("Obsolete, Use Umbraco.Core.Services.ContentService.RePublishAll()", false)] + [MethodImpl(MethodImplOptions.Synchronized)] public static void RePublishAll() { XmlDocument xd = new XmlDocument(); @@ -1047,6 +1049,10 @@ namespace umbraco.cms.businesslogic.web var current = User.GetCurrent(); int userId = current == null ? 0 : current.Id; ApplicationContext.Current.Services.ContentService.Move(Content, newParentId, userId); + + //We need to manually update this property as the above change is not directly reflected in + //the current object unless its reloaded. + base.ParentId = newParentId; } base.FireAfterMove(e); diff --git a/src/umbraco.cms/businesslogic/web/DocumentType.cs b/src/umbraco.cms/businesslogic/web/DocumentType.cs index 5b30ed7845..099d8f4ca1 100644 --- a/src/umbraco.cms/businesslogic/web/DocumentType.cs +++ b/src/umbraco.cms/businesslogic/web/DocumentType.cs @@ -342,11 +342,11 @@ namespace umbraco.cms.businesslogic.web // info section XmlElement info = xd.CreateElement("Info"); doc.AppendChild(info); - info.AppendChild(XmlHelper.AddTextNode(xd, "Name", Text)); + info.AppendChild(XmlHelper.AddTextNode(xd, "Name", GetRawText())); info.AppendChild(XmlHelper.AddTextNode(xd, "Alias", Alias)); info.AppendChild(XmlHelper.AddTextNode(xd, "Icon", IconUrl)); info.AppendChild(XmlHelper.AddTextNode(xd, "Thumbnail", Thumbnail)); - info.AppendChild(XmlHelper.AddTextNode(xd, "Description", Description)); + info.AppendChild(XmlHelper.AddTextNode(xd, "Description", GetRawDescription())); info.AppendChild(XmlHelper.AddTextNode(xd, "AllowAtRoot", AllowAtRoot.ToString())); //TODO: Add support for mixins! @@ -385,7 +385,7 @@ namespace umbraco.cms.businesslogic.web if (pt.ContentTypeId == this.Id) { XmlElement ptx = xd.CreateElement("GenericProperty"); - ptx.AppendChild(XmlHelper.AddTextNode(xd, "Name", pt.Name)); + ptx.AppendChild(XmlHelper.AddTextNode(xd, "Name", pt.GetRawName())); ptx.AppendChild(XmlHelper.AddTextNode(xd, "Alias", pt.Alias)); ptx.AppendChild(XmlHelper.AddTextNode(xd, "Type", pt.DataTypeDefinition.DataType.Id.ToString())); @@ -395,7 +395,7 @@ namespace umbraco.cms.businesslogic.web ptx.AppendChild(XmlHelper.AddTextNode(xd, "Tab", Tab.GetCaptionById(pt.TabId))); ptx.AppendChild(XmlHelper.AddTextNode(xd, "Mandatory", pt.Mandatory.ToString())); ptx.AppendChild(XmlHelper.AddTextNode(xd, "Validation", pt.ValidationRegExp)); - ptx.AppendChild(XmlHelper.AddCDataNode(xd, "Description", pt.Description)); + ptx.AppendChild(XmlHelper.AddCDataNode(xd, "Description", pt.GetRawDescription())); pts.AppendChild(ptx); } } diff --git a/src/umbraco.editorControls/imagecropper/ImageManipulation.cs b/src/umbraco.editorControls/imagecropper/ImageManipulation.cs index b74f88c755..e9d67645b8 100644 --- a/src/umbraco.editorControls/imagecropper/ImageManipulation.cs +++ b/src/umbraco.editorControls/imagecropper/ImageManipulation.cs @@ -19,7 +19,7 @@ namespace umbraco.editorControls.imagecropper byte[] buffer = null; - using(FileStream fs = new FileStream(sourceFile, FileMode.Open, FileAccess.Read)) + using (FileStream fs = new FileStream(sourceFile, FileMode.Open, FileAccess.Read)) { buffer = new byte[fs.Length]; fs.Read(buffer, 0, (int)fs.Length); @@ -31,9 +31,9 @@ namespace umbraco.editorControls.imagecropper DirectoryInfo di = new DirectoryInfo(path); if (!di.Exists) di.Create(); - using(Image croppedImage = CropImage(image, new Rectangle(cropX, cropY, cropWidth, cropHeight))) + using (Image croppedImage = CropImage(image, new Rectangle(cropX, cropY, cropWidth, cropHeight))) { - using(Image resizedImage = ResizeImage(croppedImage, new Size(sizeWidth, sizeHeight))) + using (Image resizedImage = ResizeImage(croppedImage, new Size(sizeWidth, sizeHeight))) { using (Bitmap b = new Bitmap(resizedImage)) { @@ -76,11 +76,11 @@ namespace umbraco.editorControls.imagecropper { var bmpImage = new Bitmap(img); - if (cropArea.Right > img.Width) - cropArea.Width -= (cropArea.Right - img.Width); + if (cropArea.Right > img.Width) + cropArea.Width -= (cropArea.Right - img.Width); - if (cropArea.Bottom > img.Height) - cropArea.Height -= (cropArea.Bottom - img.Height); + if (cropArea.Bottom > img.Height) + cropArea.Height -= (cropArea.Bottom - img.Height); var bmpCrop = bmpImage.Clone(cropArea, bmpImage.PixelFormat); return bmpCrop; @@ -104,7 +104,7 @@ namespace umbraco.editorControls.imagecropper g.SmoothingMode = SmoothingMode.HighQuality; g.DrawImage(imgToResize, new Rectangle(0, 0, destWidth, destHeight), 0, 0, imgToResize.Width, imgToResize.Height, GraphicsUnit.Pixel, ia); - + ia.Dispose(); g.Dispose();