diff --git a/.gitignore b/.gitignore index 04e39e37c9..32e7c297db 100644 --- a/.gitignore +++ b/.gitignore @@ -131,3 +131,4 @@ src/*.boltdata/ src/umbraco.sln.ide/* build/UmbracoCms.*/ src/.vs/ +src/Umbraco.Web.UI/umbraco/js/install.loader.js diff --git a/build/NuSpecs/tools/Dashboard.config.install.xdt b/build/NuSpecs/tools/Dashboard.config.install.xdt index 605bbb825e..197f9c1b6f 100644 --- a/build/NuSpecs/tools/Dashboard.config.install.xdt +++ b/build/NuSpecs/tools/Dashboard.config.install.xdt @@ -62,13 +62,6 @@ views/dashboard/default/startupdashboardintro.html - - - - - views/dashboard/ChangePassword.html - - diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index 4aef011d05..a05abe2a50 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -283,7 +283,12 @@ namespace Umbraco.Core { var configStatus = ConfigurationStatus; var currentVersion = UmbracoVersion.GetSemanticVersion(); - var ok = configStatus == currentVersion; + + var ok = + //we are not configured if this is null + string.IsNullOrWhiteSpace(configStatus) == false + //they must match + && configStatus == currentVersion; if (ok) { @@ -308,8 +313,9 @@ namespace Umbraco.Core return ok; } - catch + catch (Exception ex) { + LogHelper.Error("Error determining if application is configured, returning false", ex); return false; } diff --git a/src/Umbraco.Core/Constants-ObjectTypes.cs b/src/Umbraco.Core/Constants-ObjectTypes.cs index 09915dc936..560cd4b306 100644 --- a/src/Umbraco.Core/Constants-ObjectTypes.cs +++ b/src/Umbraco.Core/Constants-ObjectTypes.cs @@ -14,16 +14,31 @@ namespace Umbraco.Core /// public const string DataTypeContainer = "521231E3-8B37-469C-9F9D-51AFC91FEB7B"; + /// + /// Guid for a data type container + /// + public static readonly Guid DataTypeContainerGuid = new Guid(DataTypeContainer); + /// /// Guid for a doc type container /// public const string DocumentTypeContainer = "2F7A2769-6B0B-4468-90DD-AF42D64F7F16"; + /// + /// Guid for a doc type container + /// + public static readonly Guid DocumentTypeContainerGuid = new Guid(DocumentTypeContainer); + /// /// Guid for a doc type container /// public const string MediaTypeContainer = "42AEF799-B288-4744-9B10-BE144B73CDC4"; + /// + /// Guid for a doc type container + /// + public static readonly Guid MediaTypeContainerGuid = new Guid(MediaTypeContainer); + /// /// Guid for a Content Item object. /// @@ -44,6 +59,11 @@ namespace Umbraco.Core /// public const string DataType = "30A2A501-1978-4DDB-A57B-F7EFED43BA3C"; + /// + /// Guid for a DataType object. + /// + public static readonly Guid DataTypeGuid = new Guid(DataType); + /// /// Guid for a Document object. /// @@ -54,6 +74,11 @@ namespace Umbraco.Core /// public const string DocumentType = "A2CB7800-F571-4787-9638-BC48539A0EFB"; + /// + /// Guid for a Document Type object. + /// + public static readonly Guid DocumentTypeGuid = new Guid(DocumentType); + /// /// Guid for a Media object. /// @@ -69,6 +94,11 @@ namespace Umbraco.Core /// public const string MediaType = "4EA4382B-2F5A-4C2B-9587-AE9B3CF3602E"; + /// + /// Guid for a Media Type object. + /// + public static readonly Guid MediaTypeGuid = new Guid(MediaType); + /// /// Guid for a Member object. /// @@ -84,6 +114,11 @@ namespace Umbraco.Core /// public const string MemberType = "9B5416FB-E72F-45A9-A07B-5A9A2709CE43"; + /// + /// Guid for a Member Type object. + /// + public static readonly Guid MemberTypeGuid = new Guid(MemberType); + /// /// Guid for a Stylesheet object. /// diff --git a/src/Umbraco.Core/Constants-PropertyTypeGroups.cs b/src/Umbraco.Core/Constants-PropertyTypeGroups.cs new file mode 100644 index 0000000000..5dabe90029 --- /dev/null +++ b/src/Umbraco.Core/Constants-PropertyTypeGroups.cs @@ -0,0 +1,31 @@ +namespace Umbraco.Core +{ + public static partial class Constants + { + /// + /// Defines the identifiers for property-type groups conventions that are used within the Umbraco core. + /// + public static class PropertyTypeGroups + { + /// + /// Guid for a Image PropertyTypeGroup object. + /// + public const string Image = "79ED4D07-254A-42CF-8FA9-EBE1C116A596"; + + /// + /// Guid for a File PropertyTypeGroup object. + /// + public const string File = "50899F9C-023A-4466-B623-ABA9049885FE"; + + /// + /// Guid for a Image PropertyTypeGroup object. + /// + public const string Contents = "79995FA2-63EE-453C-A29B-2E66F324CDBE"; + + /// + /// Guid for a Image PropertyTypeGroup object. + /// + public const string Membership = "0756729D-D665-46E3-B84A-37ACEAA614F8"; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs index 13dee96b97..60fba0ae40 100644 --- a/src/Umbraco.Core/Constants-Web.cs +++ b/src/Umbraco.Core/Constants-Web.cs @@ -1,4 +1,7 @@ -namespace Umbraco.Core +using System; +using System.ComponentModel; + +namespace Umbraco.Core { public static partial class Constants { @@ -15,6 +18,8 @@ /// /// The auth cookie name /// + [Obsolete("DO NOT USE THIS, USE ISecuritySection.AuthCookieName, this will be removed in future versions")] + [EditorBrowsable(EditorBrowsableState.Never)] public const string AuthCookieName = "UMB_UCONTEXT"; } diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 88b6308c29..e91996e32a 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -560,8 +560,7 @@ namespace Umbraco.Core.Models //Additional thumbnails configured as prevalues on the DataType if (thumbnailSizes != null) { - var sep = (thumbnailSizes.Contains("") == false && thumbnailSizes.Contains(",")) ? ',' : ';'; - foreach (var thumb in thumbnailSizes.Split(sep)) + foreach (var thumb in thumbnailSizes.Split(new[] { ";", "," }, StringSplitOptions.RemoveEmptyEntries)) { int thumbSize; if (thumb != "" && int.TryParse(thumb, out thumbSize)) diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index 301894e45d..a83defb7b5 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -373,43 +373,35 @@ namespace Umbraco.Core.Models { _propertyGroups = value; _propertyGroups.CollectionChanged += PropertyGroupsChanged; + PropertyGroupsChanged(_propertyGroups, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } } /// - /// List of PropertyTypes available on this ContentType. - /// This list aggregates PropertyTypes across the PropertyGroups. + /// Gets all property types, across all property groups. /// - /// - /// - /// The setter is used purely to set the property types that DO NOT belong to a group! - /// - /// Marked as DoNotClone because the result of this property is not the natural result of the data, it is - /// a union of data so when auto-cloning if the setter is used it will be setting the unnatural result of the - /// data. We manually clone this instead. - /// [IgnoreDataMember] [DoNotClone] public virtual IEnumerable PropertyTypes { get { - var types = _propertyTypes.Union(PropertyGroups.SelectMany(x => x.PropertyTypes)); - return types; - } - internal set - { - _propertyTypes = new PropertyTypeCollection(value); - _propertyTypes.CollectionChanged += PropertyTypesChanged; + return _propertyTypes.Union(PropertyGroups.SelectMany(x => x.PropertyTypes)); } } /// - /// Returns the property type collection containing types that are non-groups - used for tests + /// Gets or sets the property types that are not in a group. /// - internal IEnumerable NonGroupedPropertyTypes + public IEnumerable NoGroupPropertyTypes { get { return _propertyTypes; } + set + { + _propertyTypes = new PropertyTypeCollection(value); + _propertyTypes.CollectionChanged += PropertyTypesChanged; + PropertyTypesChanged(_propertyTypes, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } } /// @@ -480,24 +472,37 @@ namespace Umbraco.Core.Models /// Alias of the PropertyType to move /// Name of the PropertyGroup to move the PropertyType to /// + /// If is null then the property is moved back to + /// "generic properties" ie does not have a tab anymore. public bool MovePropertyType(string propertyTypeAlias, string propertyGroupName) { - if (PropertyTypes.Any(x => x.Alias == propertyTypeAlias) == false || PropertyGroups.Any(x => x.Name == propertyGroupName) == false) - return false; + // note: not dealing with alias casing at all here? - var propertyType = PropertyTypes.First(x => x.Alias == propertyTypeAlias); - //The PropertyType already belongs to a PropertyGroup, so we have to remove the PropertyType from that group - if (PropertyGroups.Any(x => x.PropertyTypes.Any(y => y.Alias == propertyTypeAlias))) - { - var oldPropertyGroup = PropertyGroups.First(x => x.PropertyTypes.Any(y => y.Alias == propertyTypeAlias)); - oldPropertyGroup.PropertyTypes.RemoveItem(propertyTypeAlias); - } + // get property, ensure it exists + var propertyType = PropertyTypes.FirstOrDefault(x => x.Alias == propertyTypeAlias); + if (propertyType == null) return false; + // get new group, if required, and ensure it exists + var newPropertyGroup = propertyGroupName == null + ? null + : PropertyGroups.FirstOrDefault(x => x.Name == propertyGroupName); + if (propertyGroupName != null && newPropertyGroup == null) return false; + + // get old group + var oldPropertyGroup = PropertyGroups.FirstOrDefault(x => + x.PropertyTypes.Any(y => y.Alias == propertyTypeAlias)); + + // reset PropertyGroupId, which will be re-evaluated when the content type + // is saved - what is important is group.PropertyTypes - see code in + // ContentTypeBaseRepository.PersistUpdatedBaseContentType propertyType.PropertyGroupId = new Lazy(() => default(int)); - propertyType.ResetDirtyProperties(); + propertyType.ResetDirtyProperties(); // PropertyGroupId must not be dirty - var propertyGroup = PropertyGroups.First(x => x.Name == propertyGroupName); - propertyGroup.PropertyTypes.Add(propertyType); + // remove from old group, if any - add to new group, if any + if (oldPropertyGroup != null) + oldPropertyGroup.PropertyTypes.RemoveItem(propertyTypeAlias); + if (newPropertyGroup != null) + newPropertyGroup.PropertyTypes.Add(propertyType); return true; } diff --git a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs index ea5a8fbcbf..5ac21885d7 100644 --- a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs @@ -37,16 +37,21 @@ namespace Umbraco.Core.Models x => x.ContentTypeComposition); /// - /// List of ContentTypes that make up a composition of PropertyGroups and PropertyTypes for the current ContentType + /// Gets or sets the content types that compose this content type. /// [DataMember] public IEnumerable ContentTypeComposition { get { return _contentTypeComposition; } + set + { + _contentTypeComposition = value.ToList(); + OnPropertyChanged(ContentTypeCompositionSelector); + } } /// - /// Returns a list of objects from the composition + /// Gets the property groups for the entire composition. /// [IgnoreDataMember] public IEnumerable CompositionPropertyGroups @@ -59,7 +64,7 @@ namespace Umbraco.Core.Models } /// - /// Returns a list of objects from the composition + /// Gets the property types for the entire composition. /// [IgnoreDataMember] public IEnumerable CompositionPropertyTypes @@ -72,10 +77,10 @@ namespace Umbraco.Core.Models } /// - /// Adds a new ContentType to the list of composite ContentTypes + /// Adds a content type to the composition. /// - /// to add - /// True if ContentType was added, otherwise returns False + /// The content type to add. + /// True if the content type was added, otherwise false. public bool AddContentType(IContentTypeComposition contentType) { if (contentType.ContentTypeComposition.Any(x => x.CompositionAliases().Any(ContentTypeCompositionExists))) @@ -104,10 +109,10 @@ namespace Umbraco.Core.Models } /// - /// Removes a ContentType with the supplied alias from the the list of composite ContentTypes + /// Removes a content type with a specified alias from the composition. /// - /// Alias of a - /// True if ContentType was removed, otherwise returns False + /// The alias of the content type to remove. + /// True if the content type was removed, otherwise false. public bool RemoveContentType(string alias) { if (ContentTypeCompositionExists(alias)) @@ -157,31 +162,44 @@ namespace Umbraco.Core.Models /// /// Adds a PropertyGroup. - /// This method will also check if a group already exists with the same name and link it to the parent. /// /// Name of the PropertyGroup to add /// Returns True if a PropertyGroup with the passed in name was added, otherwise False public override bool AddPropertyGroup(string groupName) { - if (PropertyGroups.Any(x => x.Name == groupName)) - return false; + return AddAndReturnPropertyGroup(groupName) != null; + } - var propertyGroup = new PropertyGroup {Name = groupName, SortOrder = 0}; + private PropertyGroup AddAndReturnPropertyGroup(string name) + { + // ensure we don't have it already + if (PropertyGroups.Any(x => x.Name == name)) + return null; - if (CompositionPropertyGroups.Any(x => x.Name == groupName)) + // create the new group + var group = new PropertyGroup { Name = name, SortOrder = 0 }; + + // check if it is inherited - there might be more than 1 but we want the 1st, to + // reuse its sort order - if there are more than 1 and they have different sort + // orders... there isn't much we can do anyways + var inheritGroup = CompositionPropertyGroups.FirstOrDefault(x => x.Name == name); + if (inheritGroup == null) { - var firstGroup = CompositionPropertyGroups.First(x => x.Name == groupName && x.ParentId.HasValue == false); - propertyGroup.SetLazyParentId(new Lazy(() => firstGroup.Id)); + // no, just local, set sort order + var lastGroup = PropertyGroups.LastOrDefault(); + if (lastGroup != null) + group.SortOrder = lastGroup.SortOrder + 1; + } + else + { + // yes, inherited, re-use sort order + group.SortOrder = inheritGroup.SortOrder; } - if (PropertyGroups.Any()) - { - var last = PropertyGroups.Last(); - propertyGroup.SortOrder = last.SortOrder + 1; - } + // add + PropertyGroups.Add(group); - PropertyGroups.Add(propertyGroup); - return true; + return group; } /// @@ -193,38 +211,24 @@ namespace Umbraco.Core.Models public override bool AddPropertyType(PropertyType propertyType, string propertyGroupName) { if (propertyType.HasIdentity == false) - { propertyType.Key = Guid.NewGuid(); - } - if (PropertyTypeExists(propertyType.Alias) == false) - { - if (PropertyGroups.Contains(propertyGroupName)) - { - propertyType.PropertyGroupId = new Lazy(() => PropertyGroups[propertyGroupName].Id); - PropertyGroups[propertyGroupName].PropertyTypes.Add(propertyType); - } - else - { - //If the PropertyGroup doesn't already exist we create a new one - var propertyTypes = new List { propertyType }; - var propertyGroup = new PropertyGroup(new PropertyTypeCollection(propertyTypes)) { Name = propertyGroupName, SortOrder = 1 }; - //and check if its an inherited PropertyGroup, which exists in the composition - if (CompositionPropertyGroups.Any(x => x.Name == propertyGroupName)) - { - var parentPropertyGroup = CompositionPropertyGroups.First(x => x.Name == propertyGroupName && x.ParentId.HasValue == false); - propertyGroup.SortOrder = parentPropertyGroup.SortOrder; - //propertyGroup.ParentId = parentPropertyGroup.Id; - propertyGroup.SetLazyParentId(new Lazy(() => parentPropertyGroup.Id)); - } + // ensure no duplicate alias - over all composition properties + if (PropertyTypeExists(propertyType.Alias)) + return false; - PropertyGroups.Add(propertyGroup); - } + // get and ensure a group local to this content type + var group = PropertyGroups.Contains(propertyGroupName) + ? PropertyGroups[propertyGroupName] + : AddAndReturnPropertyGroup(propertyGroupName); + if (group == null) + return false; - return true; - } + // add property to group + propertyType.PropertyGroupId = new Lazy(() => group.Id); + group.PropertyTypes.Add(propertyType); - return false; + return true; } /// diff --git a/src/Umbraco.Core/Models/EntityContainer.cs b/src/Umbraco.Core/Models/EntityContainer.cs index 5c4deef037..92965bf238 100644 --- a/src/Umbraco.Core/Models/EntityContainer.cs +++ b/src/Umbraco.Core/Models/EntityContainer.cs @@ -1,23 +1,93 @@ -using Umbraco.Core.Models.EntityBase; +using System; +using System.Collections.Generic; +using System.Linq; namespace Umbraco.Core.Models { /// - /// Represents a folder for organizing entities such as content types and data types + /// Represents a folder for organizing entities such as content types and data types. /// - public sealed class EntityContainer : UmbracoEntity, IAggregateRoot + public sealed class EntityContainer : UmbracoEntity { - public EntityContainer() - { + private readonly Guid _containedObjectType; + + private static readonly Dictionary ObjectTypeMap = new Dictionary + { + { Constants.ObjectTypes.DataTypeGuid, Constants.ObjectTypes.DataTypeContainerGuid }, + { Constants.ObjectTypes.DocumentTypeGuid, Constants.ObjectTypes.DocumentTypeContainerGuid }, + { Constants.ObjectTypes.MediaTypeGuid, Constants.ObjectTypes.MediaTypeContainerGuid } + }; + + /// + /// Initializes a new instance of an class. + /// + public EntityContainer(Guid containedObjectType) + { + if (ObjectTypeMap.ContainsKey(containedObjectType) == false) + throw new ArgumentException("Not a contained object type.", "containedObjectType"); + _containedObjectType = containedObjectType; + + ParentId = -1; + Path = "-1"; + Level = 0; + SortOrder = 0; } - public EntityContainer(int id, int parentId, string name, int userId, string path) + /// + /// Initializes a new instance of an class. + /// + internal EntityContainer(int id, Guid uniqueId, int parentId, string path, int level, int sortOrder, Guid containedObjectType, string name, int userId) + : this(containedObjectType) { Id = id; + Key = uniqueId; ParentId = parentId; Name = name; - CreatorId = userId; Path = path; + Level = level; + SortOrder = sortOrder; + CreatorId = userId; + } + + /// + /// Gets or sets the node object type of the contained objects. + /// + public Guid ContainedObjectType + { + get { return _containedObjectType; } + } + + /// + /// Gets the node object type of the container objects. + /// + public Guid ContainerObjectType + { + get { return ObjectTypeMap[_containedObjectType]; } + } + + /// + /// Gets the container object type corresponding to a contained object type. + /// + /// The contained object type. + /// The object type of containers containing objects of the contained object type. + public static Guid GetContainerObjectType(Guid containedObjectType) + { + if (ObjectTypeMap.ContainsKey(containedObjectType) == false) + throw new ArgumentException("Not a contained object type.", "containedObjectType"); + return ObjectTypeMap[containedObjectType]; + } + + /// + /// Gets the contained object type corresponding to a container object type. + /// + /// The container object type. + /// The object type of objects that containers of the container object type can contain. + public static Guid GetContainedObjectType(Guid containerObjectType) + { + var contained = ObjectTypeMap.FirstOrDefault(x => x.Value == containerObjectType).Key; + if (contained == null) + throw new ArgumentException("Not a container object type.", "containerObjectType"); + return contained; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/IContentTypeBase.cs b/src/Umbraco.Core/Models/IContentTypeBase.cs index 89fca20b8e..80e62f50cf 100644 --- a/src/Umbraco.Core/Models/IContentTypeBase.cs +++ b/src/Umbraco.Core/Models/IContentTypeBase.cs @@ -54,10 +54,15 @@ namespace Umbraco.Core.Models PropertyGroupCollection PropertyGroups { get; set; } /// - /// Gets an enumerable list of Property Types aggregated for all groups + /// Gets all property types, across all property groups. /// IEnumerable PropertyTypes { get; } + /// + /// Gets or sets the property types that are not in a group. + /// + IEnumerable NoGroupPropertyTypes { get; set; } + /// /// Removes a PropertyType from the current ContentType /// diff --git a/src/Umbraco.Core/Models/IContentTypeComposition.cs b/src/Umbraco.Core/Models/IContentTypeComposition.cs index 8b5049f236..ab7e068fdd 100644 --- a/src/Umbraco.Core/Models/IContentTypeComposition.cs +++ b/src/Umbraco.Core/Models/IContentTypeComposition.cs @@ -8,17 +8,17 @@ namespace Umbraco.Core.Models public interface IContentTypeComposition : IContentTypeBase { /// - /// Gets a list of ContentTypes that make up a composition of PropertyGroups and PropertyTypes for the current ContentType + /// Gets or sets the content types that compose this content type. /// - IEnumerable ContentTypeComposition { get; } + IEnumerable ContentTypeComposition { get; set; } /// - /// Gets a list of objects from the composition + /// Gets the property groups for the entire composition. /// IEnumerable CompositionPropertyGroups { get; } /// - /// Gets a list of objects from the composition + /// Gets the property types for the entire composition. /// IEnumerable CompositionPropertyTypes { get; } diff --git a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs b/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs index f57d6683a2..0dc95a8987 100644 --- a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs +++ b/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs @@ -4,6 +4,7 @@ using AutoMapper; using Umbraco.Core.Models.Mapping; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Security; namespace Umbraco.Core.Models.Identity { @@ -24,6 +25,18 @@ namespace Umbraco.Core.Models.Identity .ForMember(user => user.UserTypeAlias, expression => expression.MapFrom(user => user.UserType.Alias)) .ForMember(user => user.AccessFailedCount, expression => expression.MapFrom(user => user.FailedPasswordAttempts)) .ForMember(user => user.AllowedSections, expression => expression.MapFrom(user => user.AllowedSections.ToArray())); + + config.CreateMap() + .ConstructUsing((BackOfficeIdentityUser user) => new UserData(Guid.NewGuid().ToString("N"))) //this is the 'session id' + .ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id)) + .ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections)) + .ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name)) + .ForMember(detail => detail.Roles, opt => opt.MapFrom(user => new[] { user.UserTypeAlias })) + .ForMember(detail => detail.StartContentNode, opt => opt.MapFrom(user => user.StartContentId)) + .ForMember(detail => detail.StartMediaNode, opt => opt.MapFrom(user => user.StartMediaId)) + .ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.UserName)) + .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.Culture)) + .ForMember(detail => detail.SessionId, opt => opt.MapFrom(user => user.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : user.SecurityStamp)); } private string GetPasswordHash(string storedPass) diff --git a/src/Umbraco.Core/Models/PropertyGroup.cs b/src/Umbraco.Core/Models/PropertyGroup.cs index 7976a7eac6..de88012c0e 100644 --- a/src/Umbraco.Core/Models/PropertyGroup.cs +++ b/src/Umbraco.Core/Models/PropertyGroup.cs @@ -17,7 +17,6 @@ namespace Umbraco.Core.Models public class PropertyGroup : Entity, IEquatable { private string _name; - private Lazy _parentId; private int _sortOrder; private PropertyTypeCollection _propertyTypes; @@ -31,7 +30,6 @@ namespace Umbraco.Core.Models } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); - private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ParentId); private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder); private readonly static PropertyInfo PropertyTypeCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyTypes); void PropertyTypesChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -56,29 +54,6 @@ namespace Umbraco.Core.Models } } - /// - /// Gets or sets the Id of the Parent PropertyGroup. - /// - /// - /// A Parent PropertyGroup corresponds to an inherited PropertyGroup from a composition. - /// If a PropertyType is inserted into an inherited group then a new group will be created with an Id reference to the parent. - /// - [DataMember] - public int? ParentId - { - get - { - if (_parentId == null) - return default(int?); - return _parentId.Value; - } - set - { - _parentId = new Lazy(() => value); - OnPropertyChanged(ParentIdSelector); - } - } - /// /// Gets or sets the Sort Order of the Group /// @@ -117,15 +92,6 @@ namespace Umbraco.Core.Models } } - /// - /// Sets the ParentId from the lazy integer id - /// - /// Id of the Parent - internal void SetLazyParentId(Lazy id) - { - _parentId = id; - } - public bool Equals(PropertyGroup other) { if (base.Equals(other)) return true; diff --git a/src/Umbraco.Core/Models/Rdbms/DocumentTypeDto.cs b/src/Umbraco.Core/Models/Rdbms/ContentTypeTemplateDto.cs similarity index 93% rename from src/Umbraco.Core/Models/Rdbms/DocumentTypeDto.cs rename to src/Umbraco.Core/Models/Rdbms/ContentTypeTemplateDto.cs index e3fdfb9810..88ef02ea90 100644 --- a/src/Umbraco.Core/Models/Rdbms/DocumentTypeDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/ContentTypeTemplateDto.cs @@ -1,28 +1,28 @@ -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.DatabaseAnnotations; - -namespace Umbraco.Core.Models.Rdbms -{ - [TableName("cmsDocumentType")] - [PrimaryKey("contentTypeNodeId", autoIncrement = false)] - [ExplicitColumns] - internal class DocumentTypeDto - { - [Column("contentTypeNodeId")] - [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsDocumentType", OnColumns = "contentTypeNodeId, templateNodeId")] - [ForeignKey(typeof(ContentTypeDto), Column = "nodeId")] - [ForeignKey(typeof(NodeDto))] - public int ContentTypeNodeId { get; set; } - - [Column("templateNodeId")] - [ForeignKey(typeof(TemplateDto), Column = "nodeId")] - public int TemplateNodeId { get; set; } - - [Column("IsDefault")] - [Constraint(Default = "0")] - public bool IsDefault { get; set; } - - [ResultColumn] - public ContentTypeDto ContentTypeDto { get; set; } - } +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.DatabaseAnnotations; + +namespace Umbraco.Core.Models.Rdbms +{ + [TableName("cmsDocumentType")] + [PrimaryKey("contentTypeNodeId", autoIncrement = false)] + [ExplicitColumns] + internal class ContentTypeTemplateDto + { + [Column("contentTypeNodeId")] + [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsDocumentType", OnColumns = "contentTypeNodeId, templateNodeId")] + [ForeignKey(typeof(ContentTypeDto), Column = "nodeId")] + [ForeignKey(typeof(NodeDto))] + public int ContentTypeNodeId { get; set; } + + [Column("templateNodeId")] + [ForeignKey(typeof(TemplateDto), Column = "nodeId")] + public int TemplateNodeId { get; set; } + + [Column("IsDefault")] + [Constraint(Default = "0")] + public bool IsDefault { get; set; } + + [ResultColumn] + public ContentTypeDto ContentTypeDto { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs index 51cb9872fe..42abd9ed49 100644 --- a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { @@ -9,16 +11,16 @@ namespace Umbraco.Core.Models.Rdbms [ExplicitColumns] internal class PropertyTypeGroupDto { + public PropertyTypeGroupDto() + { + //by default always create a new guid + UniqueId = Guid.NewGuid(); + } + [Column("id")] [PrimaryKeyColumn(IdentitySeed = 12)] public int Id { get; set; } - [Column("parentGroupId")] - [NullSetting(NullSetting = NullSettings.Null)] - //[Constraint(Default = "NULL")] - [ForeignKey(typeof(PropertyTypeGroupDto))] - public int? ParentGroupId { get; set; } - [Column("contenttypeNodeId")] [ForeignKey(typeof(ContentTypeDto), Column = "nodeId")] public int ContentTypeNodeId { get; set; } @@ -31,5 +33,11 @@ namespace Umbraco.Core.Models.Rdbms [ResultColumn] public List PropertyTypeDtos { get; set; } + + [Column("uniqueID")] + [NullSetting(NullSetting = NullSettings.NotNull)] + [Constraint(Default = SystemMethods.NewGuid)] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsPropertyTypeGroupUniqueID")] + public Guid UniqueId { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs index 8dcc4af29c..beebef9eeb 100644 --- a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs @@ -10,9 +10,6 @@ namespace Umbraco.Core.Models.Rdbms [Column("PropertyTypeGroupId")] public int? Id { get; set; } - [Column("parentGroupId")] - public int? ParentGroupId { get; set; } - [Column("PropertyGroupName")] public string Text { get; set; } diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs index bd670f3836..5b9f63cf48 100644 --- a/src/Umbraco.Core/Models/UserExtensions.cs +++ b/src/Umbraco.Core/Models/UserExtensions.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.Linq; using System.Threading; +using Umbraco.Core.Models.Identity; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; diff --git a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs index fdd2759d76..54c7d8d2c9 100644 --- a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs @@ -1,98 +1,144 @@ using System; +using System.Collections.Generic; using System.Globalization; +using System.Linq; using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; namespace Umbraco.Core.Persistence.Factories { + // factory for + // IContentType (document types) + // IMediaType (media types) + // IMemberType (member types) + // internal class ContentTypeFactory { - private readonly Guid _nodeObjectType; + #region IContentType - public ContentTypeFactory(Guid nodeObjectType) + public IContentType BuildContentTypeEntity(ContentTypeDto dto) { - _nodeObjectType = nodeObjectType; - } + var contentType = new ContentType(dto.NodeDto.ParentId); + BuildCommonEntity(contentType, dto); - #region Implementation of IEntityFactory - - public IContentType BuildEntity(DocumentTypeDto dto) - { - var contentType = new ContentType(dto.ContentTypeDto.NodeDto.ParentId) - { - Id = dto.ContentTypeDto.NodeDto.NodeId, - Key = dto.ContentTypeDto.NodeDto.UniqueId, - Alias = dto.ContentTypeDto.Alias, - Name = dto.ContentTypeDto.NodeDto.Text, - Icon = dto.ContentTypeDto.Icon, - Thumbnail = dto.ContentTypeDto.Thumbnail, - SortOrder = dto.ContentTypeDto.NodeDto.SortOrder, - Description = dto.ContentTypeDto.Description, - CreateDate = dto.ContentTypeDto.NodeDto.CreateDate, - Path = dto.ContentTypeDto.NodeDto.Path, - Level = dto.ContentTypeDto.NodeDto.Level, - CreatorId = dto.ContentTypeDto.NodeDto.UserId.Value, - AllowedAsRoot = dto.ContentTypeDto.AllowAtRoot, - IsContainer = dto.ContentTypeDto.IsContainer, - Trashed = dto.ContentTypeDto.NodeDto.Trashed, - DefaultTemplateId = dto.TemplateNodeId - }; //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 contentType.ResetDirtyProperties(false); - return contentType; - } - public DocumentTypeDto BuildDto(IContentType entity) - { - var documentTypeDto = new DocumentTypeDto - {ContentTypeDto = BuildContentTypeDto(entity), ContentTypeNodeId = entity.Id}; - - var contentType = entity as ContentType; - if(contentType != null) - { - documentTypeDto.TemplateNodeId = contentType.DefaultTemplateId; - documentTypeDto.IsDefault = true; - } - return documentTypeDto; + return contentType; } #endregion - private ContentTypeDto BuildContentTypeDto(IContentType entity) + #region IMediaType + + public IMediaType BuildMediaTypeEntity(ContentTypeDto dto) { + var contentType = new MediaType(dto.NodeDto.ParentId); + BuildCommonEntity(contentType, dto); + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + contentType.ResetDirtyProperties(false); + + return contentType; + } + + #endregion + + #region IMemberType + + public IMemberType BuildMemberTypeEntity(ContentTypeDto dto) + { + throw new NotImplementedException(); + } + + public IEnumerable BuildMemberTypeDtos(IMemberType entity) + { + var memberType = entity as MemberType; + if (memberType == null || memberType.PropertyTypes.Any() == false) + return Enumerable.Empty(); + + var dtos = memberType.PropertyTypes.Select(x => new MemberTypeDto + { + NodeId = entity.Id, + PropertyTypeId = x.Id, + CanEdit = memberType.MemberCanEditProperty(x.Alias), + ViewOnProfile = memberType.MemberCanViewProperty(x.Alias) + }).ToList(); + return dtos; + } + + #endregion + + #region Common + + private static void BuildCommonEntity(ContentTypeBase entity, ContentTypeDto dto) + { + entity.Id = dto.NodeDto.NodeId; + entity.Key = dto.NodeDto.UniqueId; + entity.Alias = dto.Alias; + entity.Name = dto.NodeDto.Text; + entity.Icon = dto.Icon; + entity.Thumbnail = dto.Thumbnail; + entity.SortOrder = dto.NodeDto.SortOrder; + entity.Description = dto.Description; + entity.CreateDate = dto.NodeDto.CreateDate; + entity.Path = dto.NodeDto.Path; + entity.Level = dto.NodeDto.Level; + entity.CreatorId = dto.NodeDto.UserId.Value; + entity.AllowedAsRoot = dto.AllowAtRoot; + entity.IsContainer = dto.IsContainer; + entity.Trashed = dto.NodeDto.Trashed; + } + + public ContentTypeDto BuildContentTypeDto(IContentTypeBase entity) + { + Guid nodeObjectType; + if (entity is IContentType) + nodeObjectType = Constants.ObjectTypes.DocumentTypeGuid; + else if (entity is IMediaType) + nodeObjectType = Constants.ObjectTypes.MediaTypeGuid; + else if (entity is IMemberType) + nodeObjectType = Constants.ObjectTypes.MemberTypeGuid; + else + throw new Exception("oops: invalid entity."); + var contentTypeDto = new ContentTypeDto - { - Alias = entity.Alias, - Description = entity.Description, - Icon = entity.Icon, - Thumbnail = entity.Thumbnail, - NodeId = entity.Id, - AllowAtRoot = entity.AllowedAsRoot, - IsContainer = entity.IsContainer, - NodeDto = BuildNodeDto(entity) - }; + { + Alias = entity.Alias, + Description = entity.Description, + Icon = entity.Icon, + Thumbnail = entity.Thumbnail, + NodeId = entity.Id, + AllowAtRoot = entity.AllowedAsRoot, + IsContainer = entity.IsContainer, + NodeDto = BuildNodeDto(entity, nodeObjectType) + }; return contentTypeDto; } - private NodeDto BuildNodeDto(IContentType entity) + private static NodeDto BuildNodeDto(IUmbracoEntity entity, Guid nodeObjectType) { var nodeDto = new NodeDto - { - CreateDate = entity.CreateDate, - NodeId = entity.Id, - Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)), - NodeObjectType = _nodeObjectType, - ParentId = entity.ParentId, - Path = entity.Path, - SortOrder = entity.SortOrder, - Text = entity.Name, - Trashed = false, - UniqueId = entity.Key, - UserId = entity.CreatorId - }; + { + CreateDate = entity.CreateDate, + NodeId = entity.Id, + Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)), + NodeObjectType = nodeObjectType, + ParentId = entity.ParentId, + Path = entity.Path, + SortOrder = entity.SortOrder, + Text = entity.Name, + Trashed = false, + UniqueId = entity.Key, + UserId = entity.CreatorId + }; return nodeDto; } + + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/MediaTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/MediaTypeFactory.cs deleted file mode 100644 index 98048cd3a7..0000000000 --- a/src/Umbraco.Core/Persistence/Factories/MediaTypeFactory.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Globalization; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Factories -{ - internal class MediaTypeFactory - { - private readonly Guid _nodeObjectType; - - public MediaTypeFactory(Guid nodeObjectType) - { - _nodeObjectType = nodeObjectType; - } - - #region Implementation of IEntityFactory - - public IMediaType BuildEntity(ContentTypeDto dto) - { - var contentType = new MediaType(dto.NodeDto.ParentId) - { - Id = dto.NodeDto.NodeId, - Key = dto.NodeDto.UniqueId, - Alias = dto.Alias, - Name = dto.NodeDto.Text, - Icon = dto.Icon, - Thumbnail = dto.Thumbnail, - SortOrder = dto.NodeDto.SortOrder, - Description = dto.Description, - CreateDate = dto.NodeDto.CreateDate, - Path = dto.NodeDto.Path, - Level = dto.NodeDto.Level, - CreatorId = dto.NodeDto.UserId.Value, - AllowedAsRoot = dto.AllowAtRoot, - IsContainer = dto.IsContainer, - Trashed = dto.NodeDto.Trashed - }; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - contentType.ResetDirtyProperties(false); - return contentType; - } - - public ContentTypeDto BuildDto(IMediaType entity) - { - var contentTypeDto = new ContentTypeDto - { - Alias = entity.Alias, - Description = entity.Description, - Icon = entity.Icon, - Thumbnail = entity.Thumbnail, - NodeId = entity.Id, - AllowAtRoot = entity.AllowedAsRoot, - IsContainer = entity.IsContainer, - NodeDto = BuildNodeDto(entity) - }; - return contentTypeDto; - } - - #endregion - - private NodeDto BuildNodeDto(IMediaType entity) - { - var nodeDto = new NodeDto - { - CreateDate = entity.CreateDate, - NodeId = entity.Id, - Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)), - NodeObjectType = _nodeObjectType, - ParentId = entity.ParentId, - Path = entity.Path, - SortOrder = entity.SortOrder, - Text = entity.Name, - Trashed = false, - UniqueId = entity.Key, - UserId = entity.CreatorId - }; - return nodeDto; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeFactory.cs deleted file mode 100644 index 5879016fa6..0000000000 --- a/src/Umbraco.Core/Persistence/Factories/MemberTypeFactory.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Factories -{ - internal class MemberTypeFactory - { - private readonly Guid _nodeObjectType; - - public MemberTypeFactory(Guid nodeObjectType) - { - _nodeObjectType = nodeObjectType; - } - - public IMemberType BuildEntity(ContentTypeDto dto) - { - throw new System.NotImplementedException(); - } - - public ContentTypeDto BuildDto(IMemberType entity) - { - var contentTypeDto = new ContentTypeDto - { - Alias = entity.Alias, - Description = entity.Description, - Icon = entity.Icon, - Thumbnail = entity.Thumbnail, - NodeId = entity.Id, - AllowAtRoot = entity.AllowedAsRoot, - IsContainer = entity.IsContainer, - NodeDto = BuildNodeDto(entity) - }; - return contentTypeDto; - } - - public IEnumerable BuildMemberTypeDtos(IMemberType entity) - { - var memberType = entity as MemberType; - if (memberType == null || memberType.PropertyTypes.Any() == false) - return Enumerable.Empty(); - - var memberTypes = new List(); - foreach (var propertyType in memberType.PropertyTypes) - { - memberTypes.Add(new MemberTypeDto - { - NodeId = entity.Id, - PropertyTypeId = propertyType.Id, - CanEdit = memberType.MemberCanEditProperty(propertyType.Alias), - ViewOnProfile = memberType.MemberCanViewProperty(propertyType.Alias) - }); - } - - return memberTypes; - } - - private NodeDto BuildNodeDto(IMemberType entity) - { - var nodeDto = new NodeDto - { - CreateDate = entity.CreateDate, - NodeId = entity.Id, - Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)), - NodeObjectType = _nodeObjectType, - ParentId = entity.ParentId, - Path = entity.Path, - SortOrder = entity.SortOrder, - Text = entity.Name, - Trashed = false, - UniqueId = entity.Key, - UserId = entity.CreatorId - }; - return nodeDto; - } - - private int DeterminePropertyTypeId(int initialId, string alias, IEnumerable propertyTypes) - { - if (initialId == 0 || initialId == default(int)) - { - var propertyType = propertyTypes.SingleOrDefault(x => x.Alias.Equals(alias)); - if (propertyType == null) - return default(int); - - return propertyType.Id; - } - - return initialId; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs index 38ef4542f4..93ad99a5c1 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs @@ -51,31 +51,28 @@ namespace Umbraco.Core.Persistence.Factories memberType.MemberTypePropertyTypes.Add(standardPropertyType.Key, new MemberTypePropertyProfileAccess(false, false)); } - memberType.PropertyTypes = propertyTypes; + memberType.NoGroupPropertyTypes = propertyTypes; return memberType; } private PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary standardProps) { - var propertyGroups = new PropertyGroupCollection(); - + // see PropertyGroupFactory, repeating code here... + + var propertyGroups = new PropertyGroupCollection(); foreach (var groupDto in dto.PropertyTypeGroups.Where(x => x.Id.HasValue)) { var group = new PropertyGroup(); - - //Only assign an Id if the PropertyGroup belongs to this ContentType + + // if the group is defined on the current member type, + // assign its identifier, else it will be zero if (groupDto.ContentTypeNodeId == memberType.Id) { + // note: no idea why Id is nullable here, but better check + if (groupDto.Id.HasValue == false) + throw new Exception("oops: groupDto.Id has no value."); group.Id = groupDto.Id.Value; - - if (groupDto.ParentGroupId.HasValue) - group.ParentId = groupDto.ParentGroupId.Value; - } - else - { - //If the PropertyGroup is inherited, we add a reference to the group as a Parent. - group.ParentId = groupDto.Id; } group.Name = groupDto.Text; diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs index 443457b0d9..5b2cad3415 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs @@ -8,21 +8,21 @@ namespace Umbraco.Core.Persistence.Factories { internal class PropertyGroupFactory { - private readonly int _id; + private readonly int _contentTypeId; private readonly DateTime _createDate; private readonly DateTime _updateDate; //a callback to create a property type which can be injected via a contructor private readonly Func _propertyTypeCtor; - public PropertyGroupFactory(int id) + public PropertyGroupFactory(int contentTypeId) { - _id = id; + _contentTypeId = contentTypeId; _propertyTypeCtor = (propertyEditorAlias, dbType, alias) => new PropertyType(propertyEditorAlias, dbType); } - public PropertyGroupFactory(int id, DateTime createDate, DateTime updateDate, Func propertyTypeCtor) + public PropertyGroupFactory(int contentTypeId, DateTime createDate, DateTime updateDate, Func propertyTypeCtor) { - _id = id; + _contentTypeId = contentTypeId; _createDate = createDate; _updateDate = updateDate; _propertyTypeCtor = propertyTypeCtor; @@ -30,29 +30,24 @@ namespace Umbraco.Core.Persistence.Factories #region Implementation of IEntityFactory,IEnumerable> - public IEnumerable BuildEntity(IEnumerable dto) + public IEnumerable BuildEntity(IEnumerable groupDtos) { + // groupDtos contains all the groups, those that are defined on the current + // content type, and those that are inherited from composition content types var propertyGroups = new PropertyGroupCollection(); - foreach (var groupDto in dto) + foreach (var groupDto in groupDtos) { var group = new PropertyGroup(); - //Only assign an Id if the PropertyGroup belongs to this ContentType - if (groupDto.ContentTypeNodeId == _id) - { - group.Id = groupDto.Id; - if (groupDto.ParentGroupId.HasValue) - group.ParentId = groupDto.ParentGroupId.Value; - } - else - { - //If the PropertyGroup is inherited, we add a reference to the group as a Parent. - group.ParentId = groupDto.Id; - } + // if the group is defined on the current content type, + // assign its identifier, else it will be zero + if (groupDto.ContentTypeNodeId == _contentTypeId) + group.Id = groupDto.Id; group.Name = groupDto.Text; group.SortOrder = groupDto.SortOrder; group.PropertyTypes = new PropertyTypeCollection(); + group.Key = groupDto.UniqueId; //Because we are likely to have a group with no PropertyTypes we need to ensure that these are excluded var typeDtos = groupDto.PropertyTypeDtos.Where(x => x.Id > 0); @@ -101,14 +96,14 @@ namespace Umbraco.Core.Persistence.Factories { var dto = new PropertyTypeGroupDto { - ContentTypeNodeId = _id, + ContentTypeNodeId = _contentTypeId, SortOrder = propertyGroup.SortOrder, - Text = propertyGroup.Name + Text = propertyGroup.Name, + UniqueId = propertyGroup.Key == Guid.Empty + ? Guid.NewGuid() + : propertyGroup.Key }; - if (propertyGroup.ParentId.HasValue) - dto.ParentGroupId = propertyGroup.ParentId.Value; - if (propertyGroup.HasIdentity) dto.Id = propertyGroup.Id; @@ -122,18 +117,16 @@ namespace Umbraco.Core.Persistence.Factories var propertyTypeDto = new PropertyTypeDto { Alias = propertyType.Alias, - ContentTypeId = _id, + ContentTypeId = _contentTypeId, DataTypeId = propertyType.DataTypeDefinitionId, Description = propertyType.Description, Mandatory = propertyType.Mandatory, Name = propertyType.Name, SortOrder = propertyType.SortOrder, ValidationRegExp = propertyType.ValidationRegExp, - UniqueId = propertyType.HasIdentity - ? propertyType.Key == Guid.Empty - ? Guid.NewGuid() - : propertyType.Key - : Guid.NewGuid() + UniqueId = propertyType.Key == Guid.Empty + ? Guid.NewGuid() + : propertyType.Key }; if (tabId != default(int)) diff --git a/src/Umbraco.Core/Persistence/Mappers/PropertyGroupMapper.cs b/src/Umbraco.Core/Persistence/Mappers/PropertyGroupMapper.cs index d911db1967..655c40fc61 100644 --- a/src/Umbraco.Core/Persistence/Mappers/PropertyGroupMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/PropertyGroupMapper.cs @@ -32,7 +32,7 @@ namespace Umbraco.Core.Persistence.Mappers internal override void BuildMap() { CacheMap(src => src.Id, dto => dto.Id); - CacheMap(src => src.ParentId, dto => dto.ParentGroupId); + CacheMap(src => src.Key, dto => dto.UniqueId); CacheMap(src => src.SortOrder, dto => dto.SortOrder); CacheMap(src => src.Name, dto => dto.Text); } diff --git a/src/Umbraco.Core/Persistence/Mappers/TemplateMapper.cs b/src/Umbraco.Core/Persistence/Mappers/TemplateMapper.cs index 08a7de0777..0d5b485ffa 100644 --- a/src/Umbraco.Core/Persistence/Mappers/TemplateMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/TemplateMapper.cs @@ -39,6 +39,7 @@ namespace Umbraco.Core.Persistence.Mappers { CacheMap(src => src.Id, dto => dto.NodeId); CacheMap(src => src.MasterTemplateId, dto => dto.ParentId); + CacheMap(src => src.Key, dto => dto.UniqueId); CacheMap(src => src.Alias, dto => dto.Alias); CacheMap(src => src.Content, dto => dto.Design); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index 9204cef8ed..761bda29c6 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -178,11 +178,11 @@ namespace Umbraco.Core.Persistence.Migrations.Initial private void CreateCmsPropertyTypeGroupData() { - _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 3, ContentTypeNodeId = 1032, Text = "Image", SortOrder = 1 }); - _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 4, ContentTypeNodeId = 1033, Text = "File", SortOrder = 1 }); - _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 5, ContentTypeNodeId = 1031, Text = "Contents", SortOrder = 1 }); + _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 3, ContentTypeNodeId = 1032, Text = "Image", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Image) }); + _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 4, ContentTypeNodeId = 1033, Text = "File", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.File) }); + _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 5, ContentTypeNodeId = 1031, Text = "Contents", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Contents) }); //membership property group - _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 11, ContentTypeNodeId = 1044, Text = "Membership", SortOrder = 1 }); + _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 11, ContentTypeNodeId = 1044, Text = "Membership", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Membership) }); } private void CreateCmsPropertyTypeData() diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs index 0ba269a3ba..9ffce4b8de 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs @@ -44,7 +44,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial {4, typeof (ContentVersionDto)}, {5, typeof (DocumentDto)}, - {6, typeof (DocumentTypeDto)}, + {6, typeof (ContentTypeTemplateDto)}, {7, typeof (DataTypeDto)}, {8, typeof (DataTypePreValueDto)}, {9, typeof (DictionaryDto)}, diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUniqueIdPropertyTypeGroupColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUniqueIdPropertyTypeGroupColumn.cs new file mode 100644 index 0000000000..2a164b6e0d --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUniqueIdPropertyTypeGroupColumn.cs @@ -0,0 +1,80 @@ +using System; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero +{ + [Migration("7.4.0", 2, GlobalSettings.UmbracoMigrationName)] + public class AddUniqueIdPropertyTypeGroupColumn : MigrationBase + { + public AddUniqueIdPropertyTypeGroupColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) + : base(sqlSyntax, logger) + { } + + public override void Up() + { + // don't execute if the column is already there + var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray(); + + if (columns.Any(x => x.TableName.InvariantEquals("cmsPropertyTypeGroup") && x.ColumnName.InvariantEquals("uniqueID")) == false) + { + Create.Column("uniqueID").OnTable("cmsPropertyTypeGroup").AsGuid().NotNullable().WithDefault(SystemMethods.NewGuid); + + // unique constraint on name + version + Create.Index("IX_cmsPropertyTypeGroupUniqueID").OnTable("cmsPropertyTypeGroup") + .OnColumn("uniqueID").Ascending() + .WithOptions() + .NonClustered() + .WithOptions() + .Unique(); + + // fill in the data in a way that is consistent over all environments + // (ie cannot use random guids, http://issues.umbraco.org/issue/U4-6942) + + foreach (var data in Context.Database.Query(@" +SELECT cmsPropertyTypeGroup.id grId, cmsPropertyTypeGroup.text grName, cmsContentType.alias ctAlias, umbracoNode.nodeObjectType nObjType +FROM cmsPropertyTypeGroup +INNER JOIN cmsContentType +ON cmsPropertyTypeGroup.contentTypeNodeId = cmsContentType.nodeId +INNER JOIN umbracoNode +ON cmsContentType.nodeId = umbracoNode.id")) + { + Guid guid; + // see BaseDataCreation... built-in groups have their own guids + if (data.grId == 3) + { + guid = new Guid(Constants.PropertyTypeGroups.Image); + } + else if (data.grId == 4) + { + guid = new Guid(Constants.PropertyTypeGroups.File); + } + else if (data.grId == 5) + { + guid = new Guid(Constants.PropertyTypeGroups.Contents); + } + else if (data.grId == 11) + { + guid = new Guid(Constants.PropertyTypeGroups.Membership); + } + else + { + // create a consistent guid from + // group name + content type alias + object type + string guidSource = data.grName + data.ctAlias + data.nObjType; + guid = guidSource.ToGuid(); + } + + // set the Unique Id to the one we've generated + Update.Table("cmsPropertyTypeGroup").Set(new { uniqueID = guid }).Where(new { id = data.grId }); + } + } + } + + public override void Down() + { } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs index e9bb153913..84877fa4de 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs @@ -17,35 +17,39 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZer { public EnsureContentTypeUniqueIdsAreConsistent(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) - { - } + { } public override void Up() { - var docTypeGuid = new Guid(Constants.ObjectTypes.DocumentType); - var mediaTypeGuid = new Guid(Constants.ObjectTypes.MediaType); - var memberTypeGuid = new Guid(Constants.ObjectTypes.MemberType); + var objectTypes = new[] + { + Constants.ObjectTypes.DocumentTypeGuid, + Constants.ObjectTypes.MediaTypeGuid, + Constants.ObjectTypes.MemberTypeGuid, + }; var sql = new Sql() .Select("umbracoNode.id,cmsContentType.alias,umbracoNode.nodeObjectType") .From(SqlSyntax) .InnerJoin(SqlSyntax) - .On(SqlSyntax, dto => dto.NodeId, dto => dto.NodeId); + .On(SqlSyntax, dto => dto.NodeId, dto => dto.NodeId) + .WhereIn(x => x.NodeObjectType, objectTypes); + + var rows = Context.Database.Fetch(sql); - var rows = Context.Database.Fetch(sql) - .Where(x => x.nodeObjectType == docTypeGuid || x.nodeObjectType == mediaTypeGuid || x.nodeObjectType == memberTypeGuid); foreach (var row in rows) { - // casting to string to gain access to ToGuid extension method. - var alias = (string)row.alias.ToString(); - var nodeType = ((Guid) row.nodeObjectType).ToString(); - var consistentGuid = (alias + nodeType).ToGuid(); - Update.Table("umbracoNode").Set(new { uniqueID = consistentGuid }).Where(new { id = row.id }); + // create a consistent guid from + // alias + object type + var guidSource = ((string) row.alias) + ((Guid) row.nodeObjectType); + var guid = guidSource.ToGuid(); + + // set the Unique Id to the one we've generated + Update.Table("umbracoNode").Set(new { uniqueID = guid }).Where(new { id = row.id }); } } public override void Down() - { - } + { } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/RemoveParentIdPropertyTypeGroupColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/RemoveParentIdPropertyTypeGroupColumn.cs new file mode 100644 index 0000000000..59882a25ec --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/RemoveParentIdPropertyTypeGroupColumn.cs @@ -0,0 +1,30 @@ +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero +{ + [Migration("7.4.0", 3, GlobalSettings.UmbracoMigrationName)] + public class RemoveParentIdPropertyTypeGroupColumn : MigrationBase + { + public RemoveParentIdPropertyTypeGroupColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) + : base(sqlSyntax, logger) + { } + + public override void Up() + { + // don't execute if the column is not there anymore + var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray(); + + if (columns.Any(x => x.TableName.InvariantEquals("cmsPropertyTypeGroup") && x.ColumnName.InvariantEquals("parentGroupId")) == false) + return; + + Delete.ForeignKey("FK_cmsPropertyTypeGroup_cmsPropertyTypeGroup_id").OnTable("cmsPropertyTypeGroup"); + Delete.Column("parentGroupId").FromTable("cmsPropertyTypeGroup"); + } + + public override void Down() + { } + } +} diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs index 58278eff84..7589c7000d 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs @@ -5,15 +5,13 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero -{ - +{ [Migration("7.3.0", 13, GlobalSettings.UmbracoMigrationName)] public class AddUniqueIdPropertyTypeColumn : MigrationBase { public AddUniqueIdPropertyTypeColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) - { - } + { } public override void Up() { @@ -24,7 +22,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe { Create.Column("uniqueID").OnTable("cmsPropertyType").AsGuid().NotNullable().WithDefault(SystemMethods.NewGuid); - //unique constraint on name + version + // unique constraint on name + version Create.Index("IX_cmsPropertyTypeUniqueID").OnTable("cmsPropertyType") .OnColumn("uniqueID").Ascending() .WithOptions() @@ -32,8 +30,8 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe .WithOptions() .Unique(); - //now we need to fill in the data so that it is consistent, we can't have it generating random GUIDs for - // the already existing data, see: http://issues.umbraco.org/issue/U4-6942 + // fill in the data in a way that is consistent over all environments + // (ie cannot use random guids, http://issues.umbraco.org/issue/U4-6942) foreach (var data in Context.Database.Query(@" SELECT cmsPropertyType.id ptId, cmsPropertyType.Alias ptAlias, cmsContentType.alias ctAlias, umbracoNode.nodeObjectType nObjType @@ -43,22 +41,18 @@ ON cmsPropertyType.contentTypeId = cmsContentType.nodeId INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id")) { - //create a guid from the concatenation of the: - // property type alias + the doc type alias + the content type node object type - // - the latter is required because there can be a content type and media type with the same alias!! - string concatAlias = data.ptAlias + data.ctAlias + data.nObjType; - var ptGuid = concatAlias.ToGuid(); + // create a consistent guid from + // property alias + content type alias + object type + string guidSource = data.ptAlias + data.ctAlias + data.nObjType; + var guid = guidSource.ToGuid(); - //set the Unique Id to the one we've generated - Update.Table("cmsPropertyType").Set(new {uniqueID = ptGuid}).Where(new {id = data.ptId }); + // set the Unique Id to the one we've generated + Update.Table("cmsPropertyType").Set(new { uniqueID = guid }).Where(new { id = data.ptId }); } } - - } public override void Down() - { - } + { } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs index 6598719454..ff71aec142 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs @@ -31,54 +31,49 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixZeroOne //Fetch all PropertyTypes that belongs to a PropertyTypeGroup //NOTE: We are writing the full query because we've added a column to the PropertyTypeDto in later versions so one of the columns // won't exist yet - var propertyTypes = database.Fetch("SELECT * FROM cmsPropertyType WHERE propertyTypeGroupId > 0"); + var propertyTypes = database.Fetch("SELECT * FROM cmsPropertyType WHERE propertyTypeGroupId > 0"); var propertyGroups = database.Fetch("WHERE id > 0"); foreach (var propertyType in propertyTypes) { - //Get the PropertyTypeGroup that the current PropertyType references - var parentPropertyTypeGroup = propertyGroups.FirstOrDefault(x => x.Id == propertyType.PropertyTypeGroupId); - if (parentPropertyTypeGroup != null) + // get the PropertyTypeGroup of the current PropertyType, skip if not found + var propertyTypeGroup = propertyGroups.FirstOrDefault(x => x.Id == propertyType.propertyTypeGroupId); + if (propertyTypeGroup == null) continue; + + // if the PropretyTypeGroup belongs to the same content type as the PropertyType, then fine + if (propertyTypeGroup.ContentTypeNodeId == propertyType.contentTypeId) continue; + + // else we want to assign the PropertyType to a proper PropertyTypeGroup + // ie one that does belong to the same content - look for it + var okPropertyTypeGroup = propertyGroups.FirstOrDefault(x => + x.Text == propertyTypeGroup.Text && // same name + x.ContentTypeNodeId == propertyType.contentTypeId); // but for proper content type + + if (okPropertyTypeGroup == null) { - //If the ContentType is the same on the PropertyType and the PropertyTypeGroup the group is valid and we skip to the next - if (parentPropertyTypeGroup.ContentTypeNodeId == propertyType.ContentTypeId) continue; - - //Check if the 'new' PropertyTypeGroup has already been created - var existingPropertyTypeGroup = - propertyGroups.FirstOrDefault( - x => - x.ParentGroupId == parentPropertyTypeGroup.Id && x.Text == parentPropertyTypeGroup.Text && - x.ContentTypeNodeId == propertyType.ContentTypeId); - - //This should ensure that we don't create duplicate groups for a single ContentType - if (existingPropertyTypeGroup == null) + // does not exist, create a new PropertyTypeGroup, + var propertyGroup = new PropertyTypeGroupDto { + ContentTypeNodeId = propertyType.contentTypeId, + Text = propertyTypeGroup.Text, + SortOrder = propertyTypeGroup.SortOrder + }; - //Create a new PropertyTypeGroup that references the parent group that the PropertyType was referencing pre-6.0.1 - var propertyGroup = new PropertyTypeGroupDto - { - ContentTypeNodeId = propertyType.ContentTypeId, - ParentGroupId = parentPropertyTypeGroup.Id, - Text = parentPropertyTypeGroup.Text, - SortOrder = parentPropertyTypeGroup.SortOrder - }; + // save + add to list of groups + int id = Convert.ToInt16(database.Insert(propertyGroup)); + propertyGroup.Id = id; + propertyGroups.Add(propertyGroup); - //Save the PropertyTypeGroup in the database and update the list of groups with this new group - int id = Convert.ToInt16(database.Insert(propertyGroup)); - propertyGroup.Id = id; - propertyGroups.Add(propertyGroup); - //Update the reference to the new PropertyTypeGroup on the current PropertyType - propertyType.PropertyTypeGroupId = id; - database.Update(propertyType); - } - else - { - //Update the reference to the existing PropertyTypeGroup on the current PropertyType - propertyType.PropertyTypeGroupId = existingPropertyTypeGroup.Id; - database.Update(propertyType); - } + // update the PropertyType to use the new PropertyTypeGroup + propertyType.propertyTypeGroupId = id; } + else + { + // exists, update PropertyType to use the PropertyTypeGroup + propertyType.propertyTypeGroupId = okPropertyTypeGroup.Id; + } + database.Update("cmsPropertyType", "id", propertyType); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 2de5d0c641..5ed0f62124 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -30,38 +30,25 @@ namespace Umbraco.Core.Persistence.Repositories internal abstract class ContentTypeBaseRepository : PetaPocoRepositoryBase, IReadRepository where TEntity : class, IContentTypeComposition { - - protected ContentTypeBaseRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, - Guid containerType) - : base(work, cache, logger, sqlSyntax) - { - _guidRepo = new GuidReadOnlyContentTypeBaseRepository(this, work, cache, logger, sqlSyntax); - ContainerRepository = new EntityContainerRepository(work, cache, logger, sqlSyntax, containerType, NodeObjectTypeId); - } - protected ContentTypeBaseRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { _guidRepo = new GuidReadOnlyContentTypeBaseRepository(this, work, cache, logger, sqlSyntax); - ContainerRepository = null; } - protected EntityContainerRepository ContainerRepository { get; private set; } private readonly GuidReadOnlyContentTypeBaseRepository _guidRepo; - public IEnumerable> Move(TEntity toMove, int parentId) + public IEnumerable> Move(TEntity toMove, EntityContainer container) { - if (parentId > 0) + var parentId = -1; + if (container != null) { - var container = ContainerRepository.Get(parentId); - if (container == null) - throw new DataOperationException(MoveOperationStatusType.FailedParentNotFound); - // Check on paths if ((string.Format(",{0},", container.Path)).IndexOf(string.Format(",{0},", toMove.Id), StringComparison.Ordinal) > -1) { throw new DataOperationException(MoveOperationStatusType.FailedNotAllowedByPath); } + parentId = container.Id; } //used to track all the moved entities to be given to the event @@ -91,32 +78,6 @@ namespace Umbraco.Core.Persistence.Repositories return moveInfo; } - /// - /// Deletes a folder - this will move all contained entities into their parent - /// - /// - public void DeleteContainer(int containerId) - { - if (ContainerRepository == null) throw new NotSupportedException("The repository type " + GetType() + " does not support containers"); - - var found = ContainerRepository.Get(containerId); - ContainerRepository.Delete(found); - } - - public EntityContainer CreateContainer(int parentId, string name, int userId) - { - if (ContainerRepository == null) throw new NotSupportedException("The repository type " + GetType() + " does not support containers"); - - var container = new EntityContainer - { - ParentId = parentId, - Name = name, - CreatorId = userId - }; - ContainerRepository.AddOrUpdate(container); - return container; - } - /// /// Returns the content type ids that match the query /// @@ -149,8 +110,11 @@ namespace Umbraco.Core.Persistence.Repositories return new PropertyType(propertyEditorAlias, dbType, propertyTypeAlias); } - protected void PersistNewBaseContentType(ContentTypeDto dto, IContentTypeComposition entity) + protected void PersistNewBaseContentType(IContentTypeComposition entity) { + var factory = new ContentTypeFactory(); + var dto = factory.BuildContentTypeDto(entity); + //Cannot add a duplicate content type type var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsContentType INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id @@ -262,38 +226,35 @@ AND umbracoNode.nodeObjectType = @objectType", } } - protected void PersistUpdatedBaseContentType(ContentTypeDto dto, IContentTypeComposition entity) + protected void PersistUpdatedBaseContentType(IContentTypeComposition entity) { + var factory = new ContentTypeFactory(); + var dto = factory.BuildContentTypeDto(entity); - //Cannot update to a duplicate alias + // ensure the alias is not used already var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsContentType INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id WHERE cmsContentType." + SqlSyntax.GetQuotedColumnName("alias") + @"= @alias AND umbracoNode.nodeObjectType = @objectType AND umbracoNode.id <> @id", - new { id = dto.NodeId, alias = entity.Alias, objectType = NodeObjectTypeId }); + new { id = dto.NodeId, alias = dto.Alias, objectType = NodeObjectTypeId }); if (exists > 0) - { - throw new DuplicateNameException("An item with the alias " + entity.Alias + " already exists"); - } - - var propertyGroupFactory = new PropertyGroupFactory(entity.Id); + throw new DuplicateNameException("An item with the alias " + dto.Alias + " already exists"); + // handle (update) the node var nodeDto = dto.NodeDto; - var o = Database.Update(nodeDto); + Database.Update(nodeDto); + // fixme - why? we are UPDATING so we should ALREADY have a PK! //Look up ContentType entry to get PrimaryKey for updating the DTO var dtoPk = Database.First("WHERE nodeId = @Id", new { Id = entity.Id }); dto.PrimaryKey = dtoPk.PrimaryKey; Database.Update(dto); - //Delete the ContentType composition entries before adding the updated collection + // handle (delete then recreate) compositions Database.Delete("WHERE childContentTypeId = @Id", new { Id = entity.Id }); - //Update ContentType composition in new table foreach (var composition in entity.ContentTypeComposition) - { Database.Insert(new ContentType2ContentTypeDto { ParentId = composition.Id, ChildId = entity.Id }); - } //Removing a ContentType from a composition (U4-1690) //1. Find content based on the current ContentType: entity.Id @@ -372,54 +333,43 @@ AND umbracoNode.id <> @id", } } - if (entity.IsPropertyDirty("PropertyGroups") || - entity.PropertyGroups.Any(x => x.IsDirty())) + if (entity.IsPropertyDirty("PropertyGroups") || entity.PropertyGroups.Any(x => x.IsDirty())) { - //Delete Tabs/Groups by excepting entries from db with entries from collections - var dbPropertyGroups = - Database.Fetch("WHERE contenttypeNodeId = @Id", new { Id = entity.Id }) - .Select(x => new Tuple(x.Id, x.Text)) - .ToList(); - var entityPropertyGroups = entity.PropertyGroups.Select(x => new Tuple(x.Id, x.Name)).ToList(); - var tabsToDelete = dbPropertyGroups.Select(x => x.Item1).Except(entityPropertyGroups.Select(x => x.Item1)); - var tabs = dbPropertyGroups.Where(x => tabsToDelete.Any(y => y == x.Item1)); - //Update Tab name downstream to ensure renaming is done properly - foreach (var propertyGroup in entityPropertyGroups) + // todo + // we used to try to propagate tabs renaming downstream, relying on ParentId, but + // 1) ParentId makes no sense (if a tab can be inherited from multiple composition + // types) so we would need to figure things out differently, visiting downstream + // content types and looking for tabs with the same name... + // 2) It was not deployable as changing a content type changes other content types + // that was not deterministic, because it would depend on the order of the changes. + // That last point could be fixed if (1) is fixed, but then it still is an issue with + // deploy because changing a content type changes other content types that are not + // dependencies but dependents, and then what? + // + // So... for the time being, all renaming propagation is disabled. We just don't do it. + + // (all gone) + + // delete tabs that do not exist anymore + // get the tabs that are currently existing (in the db) + // get the tabs that we want, now + // and derive the tabs that we want to delete + var existingPropertyGroups = Database.Fetch("WHERE contentTypeNodeId = @id", new { id = entity.Id }) + .Select(x => x.Id) + .ToList(); + var newPropertyGroups = entity.PropertyGroups.Select(x => x.Id).ToList(); + var tabsToDelete = existingPropertyGroups + .Except(newPropertyGroups) + .ToArray(); + + // move properties to generic properties, and delete the tabs + if (tabsToDelete.Length > 0) { - Database.Update("SET Text = @TabName WHERE parentGroupId = @TabId", - new { TabName = propertyGroup.Item2, TabId = propertyGroup.Item1 }); - - var childGroups = Database.Fetch("WHERE parentGroupId = @TabId", new { TabId = propertyGroup.Item1 }); - foreach (var childGroup in childGroups) - { - var sibling = Database.Fetch("WHERE contenttypeNodeId = @Id AND text = @Name", - new { Id = childGroup.ContentTypeNodeId, Name = propertyGroup.Item2 }) - .FirstOrDefault(x => x.ParentGroupId.HasValue == false || x.ParentGroupId.Value.Equals(propertyGroup.Item1) == false); - //If the child group doesn't have a sibling there is no chance of duplicates and we continue - if (sibling == null || (sibling.ParentGroupId.HasValue && sibling.ParentGroupId.Value.Equals(propertyGroup.Item1))) continue; - - //Since the child group has a sibling with the same name we need to point any PropertyTypes to the sibling - //as this child group is about to leave the party. - Database.Update( - "SET propertyTypeGroupId = @PropertyTypeGroupId WHERE propertyTypeGroupId = @PropertyGroupId AND ContentTypeId = @ContentTypeId", - new { PropertyTypeGroupId = sibling.Id, PropertyGroupId = childGroup.Id, ContentTypeId = childGroup.ContentTypeNodeId }); - - //Since the parent group has been renamed and we have duplicates we remove this group - //and leave our sibling in charge of the part. - Database.Delete(childGroup); - } - } - //Do Tab updates - foreach (var tab in tabs) - { - Database.Update("SET propertyTypeGroupId = NULL WHERE propertyTypeGroupId = @PropertyGroupId", - new { PropertyGroupId = tab.Item1 }); - Database.Update("SET parentGroupId = NULL WHERE parentGroupId = @TabId", - new { TabId = tab.Item1 }); - Database.Delete("WHERE contenttypeNodeId = @Id AND text = @Name", - new { Id = entity.Id, Name = tab.Item2 }); + Database.Update("SET propertyTypeGroupId=NULL WHERE propertyTypeGroupId IN (@ids)", new { ids = tabsToDelete }); + Database.Delete("WHERE id IN (@ids)", new { ids = tabsToDelete }); } } + var propertyGroupFactory = new PropertyGroupFactory(entity.Id); //Run through all groups to insert or update entries foreach (var propertyGroup in entity.PropertyGroups) @@ -463,25 +413,6 @@ AND umbracoNode.id <> @id", if (propertyType.HasIdentity == false) propertyType.Id = typePrimaryKey; //Set Id on new PropertyType } - - //If a Composition is removed we need to update/reset references to the PropertyGroups on that ContentType - if (entity.IsPropertyDirty("ContentTypeComposition") && - compositionBase != null && - compositionBase.RemovedContentTypeKeyTracker != null && - compositionBase.RemovedContentTypeKeyTracker.Any()) - { - foreach (var compositionId in compositionBase.RemovedContentTypeKeyTracker) - { - var dbPropertyGroups = - Database.Fetch("WHERE contenttypeNodeId = @Id", new { Id = compositionId }) - .Select(x => x.Id); - foreach (var propertyGroup in dbPropertyGroups) - { - Database.Update("SET parentGroupId = NULL WHERE parentGroupId = @TabId AND contenttypeNodeId = @ContentTypeNodeId", - new { TabId = propertyGroup, ContentTypeNodeId = entity.Id }); - } - } - } } protected IEnumerable GetAllowedContentTypeIds(int id) @@ -701,7 +632,7 @@ AND umbracoNode.id <> @id", foreach (var contentType in contentTypes) { contentType.PropertyGroups = allPropGroups[contentType.Id]; - ((ContentTypeBase)contentType).PropertyTypes = allPropTypes[contentType.Id]; + contentType.NoGroupPropertyTypes = allPropTypes[contentType.Id]; } //NOTE: SQL call #3++ @@ -901,8 +832,8 @@ AND umbracoNode.id <> @id", //now create the media type object - var factory = new MediaTypeFactory(new Guid(Constants.ObjectTypes.MediaType)); - var mediaType = factory.BuildEntity(contentTypeDto); + var factory = new ContentTypeFactory(); + var mediaType = factory.BuildMediaTypeEntity(contentTypeDto); //map the allowed content types //map the child content type ids @@ -1038,7 +969,7 @@ AND umbracoNode.id <> @id", var defaultTemplate = defaultTemplates.FirstOrDefault(x => x.Item1.Value) ?? defaultTemplates.FirstOrDefault(); - var dtDto = new DocumentTypeDto + var dtDto = new ContentTypeTemplateDto { //create the content type dto ContentTypeDto = new ContentTypeDto @@ -1090,8 +1021,15 @@ AND umbracoNode.id <> @id", //now create the content type object - var factory = new ContentTypeFactory(new Guid(Constants.ObjectTypes.DocumentType)); - var contentType = factory.BuildEntity(dtDto); + var factory = new ContentTypeFactory(); + var contentType = factory.BuildContentTypeEntity(dtDto.ContentTypeDto); + + // NOTE + // that was done by the factory but makes little sense, moved here, so + // now we have to reset dirty props again (as the factory does it) and yet, + // we are not managing allowed templates... the whole thing is weird. + ((ContentType) contentType).DefaultTemplateId = dtDto.TemplateNodeId; + contentType.ResetDirtyProperties(false); //map the allowed content types //map the child content type ids @@ -1165,7 +1103,7 @@ AND umbracoNode.id <> @id", var sqlBuilder = new StringBuilder(@"SELECT PG.contenttypeNodeId as contentTypeId, PT.ptUniqueId as ptUniqueID, PT.ptId, PT.ptAlias, PT.ptDesc,PT.ptMandatory,PT.ptName,PT.ptSortOrder,PT.ptRegExp, PT.dtId,PT.dtDbType,PT.dtPropEdAlias, - PG.id as pgId, PG.parentGroupId as pgParentGroupId, PG.sortorder as pgSortOrder, PG." + sqlSyntax.GetQuotedColumnName("text") + @" as pgText + PG.id as pgId, PG.uniqueID as pgKey, PG.sortorder as pgSortOrder, PG." + sqlSyntax.GetQuotedColumnName("text") + @" as pgText FROM cmsPropertyTypeGroup as PG LEFT JOIN ( @@ -1186,7 +1124,7 @@ AND umbracoNode.id <> @id", PT.uniqueID as ptUniqueID, PT.id as ptId, PT.Alias as ptAlias, PT." + sqlSyntax.GetQuotedColumnName("Description") + @" as ptDesc, PT.mandatory as ptMandatory, PT.Name as ptName, PT.sortOrder as ptSortOrder, PT.validationRegExp as ptRegExp, DT.nodeId as dtId, DT.dbType as dtDbType, DT.propertyEditorAlias as dtPropEdAlias, - PG.id as pgId, PG.parentGroupId as pgParentGroupId, PG.sortorder as pgSortOrder, PG." + sqlSyntax.GetQuotedColumnName("text") + @" as pgText + PG.id as pgId, PG.uniqueID as pgKey, PG.sortorder as pgSortOrder, PG." + sqlSyntax.GetQuotedColumnName("text") + @" as pgText FROM cmsPropertyType as PT INNER JOIN cmsDataType as DT ON PT.dataTypeId = DT.nodeId @@ -1219,7 +1157,7 @@ AND umbracoNode.id <> @id", //filter based on the current content type .Where(x => x.contentTypeId == currId) //turn that into a custom object containing only the group info - .Select(x => new { GroupId = x.pgId, ParentGroupId = x.pgParentGroupId, SortOrder = x.pgSortOrder, Text = x.pgText }) + .Select(x => new { GroupId = x.pgId, SortOrder = x.pgSortOrder, Text = x.pgText, Key = x.pgKey }) //get distinct data by id .DistinctBy(x => (int)x.GroupId) //for each of these groups, create a group object with it's associated properties @@ -1243,8 +1181,8 @@ AND umbracoNode.id <> @id", //fill in the rest of the group properties Id = group.GroupId, Name = group.Text, - ParentId = group.ParentGroupId, - SortOrder = group.SortOrder + SortOrder = group.SortOrder, + Key = group.Key }).ToArray()); allPropertyGroupCollection[currId] = propertyGroupCollection; diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs index 04b3e46e3a..a6d9b2b58b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs @@ -25,7 +25,7 @@ namespace Umbraco.Core.Persistence.Repositories private readonly ITemplateRepository _templateRepository; public ContentTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, ITemplateRepository templateRepository) - : base(work, cache, logger, sqlSyntax, new Guid(Constants.ObjectTypes.DocumentTypeContainer)) + : base(work, cache, logger, sqlSyntax) { _templateRepository = templateRepository; } @@ -60,7 +60,7 @@ namespace Umbraco.Core.Persistence.Repositories var sql = translator.Translate() .OrderBy(x => x.Text, SqlSyntax); - var dtos = Database.Fetch(sql); + var dtos = Database.Fetch(sql); return dtos.Any() ? GetAll(dtos.DistinctBy(x => x.ContentTypeDto.NodeId).Select(x => x.ContentTypeDto.NodeId).ToArray()) : Enumerable.Empty(); @@ -96,8 +96,8 @@ namespace Umbraco.Core.Persistence.Repositories .From(SqlSyntax) .InnerJoin(SqlSyntax) .On(SqlSyntax, left => left.NodeId, right => right.NodeId) - .LeftJoin(SqlSyntax) - .On(SqlSyntax ,left => left.ContentTypeNodeId, right => right.NodeId) + .LeftJoin(SqlSyntax) + .On(SqlSyntax, left => left.ContentTypeNodeId, right => right.NodeId) .Where(x => x.NodeObjectType == NodeObjectTypeId); return sql; @@ -171,26 +171,39 @@ namespace Umbraco.Core.Persistence.Repositories ((ContentType)entity).AddingEntity(); - var factory = new ContentTypeFactory(NodeObjectTypeId); - var dto = factory.BuildDto(entity); - - PersistNewBaseContentType(dto.ContentTypeDto, entity); - //Inserts data into the cmsDocumentType table if a template exists - if (dto.TemplateNodeId > 0) - { - dto.ContentTypeNodeId = entity.Id; - Database.Insert(dto); - } - - //Insert allowed Templates not including the default one, as that has already been inserted - foreach (var template in entity.AllowedTemplates.Where(x => x != null && x.Id != dto.TemplateNodeId)) - { - Database.Insert(new DocumentTypeDto { ContentTypeNodeId = entity.Id, TemplateNodeId = template.Id, IsDefault = false }); - } + PersistNewBaseContentType(entity); + PersistTemplates(entity, false); entity.ResetDirtyProperties(); } + protected void PersistTemplates(IContentType entity, bool clearAll) + { + // remove and insert, if required + Database.Delete("WHERE contentTypeNodeId = @Id", new { Id = entity.Id }); + + // we could do it all in foreach if we assume that the default template is an allowed template?? + var defaultTemplateId = ((ContentType) entity).DefaultTemplateId; + if (defaultTemplateId > 0) + { + Database.Insert(new ContentTypeTemplateDto + { + ContentTypeNodeId = entity.Id, + TemplateNodeId = defaultTemplateId, + IsDefault = true + }); + } + foreach (var template in entity.AllowedTemplates.Where(x => x != null && x.Id != defaultTemplateId)) + { + Database.Insert(new ContentTypeTemplateDto + { + ContentTypeNodeId = entity.Id, + TemplateNodeId = template.Id, + IsDefault = false + }); + } + } + protected override void PersistUpdatedItem(IContentType entity) { ValidateAlias(entity); @@ -211,24 +224,8 @@ namespace Umbraco.Core.Persistence.Repositories entity.SortOrder = maxSortOrder + 1; } - var factory = new ContentTypeFactory(NodeObjectTypeId); - var dto = factory.BuildDto(entity); - - PersistUpdatedBaseContentType(dto.ContentTypeDto, entity); - - //Look up DocumentType entries for updating - this could possibly be a "remove all, insert all"-approach - Database.Delete("WHERE contentTypeNodeId = @Id", new { Id = entity.Id }); - //Insert the updated DocumentTypeDto if a template exists - if (dto.TemplateNodeId > 0) - { - Database.Insert(dto); - } - - //Insert allowed Templates not including the default one, as that has already been inserted - foreach (var template in entity.AllowedTemplates.Where(x => x != null && x.Id != dto.TemplateNodeId)) - { - Database.Insert(new DocumentTypeDto { ContentTypeNodeId = entity.Id, TemplateNodeId = template.Id, IsDefault = false }); - } + PersistUpdatedBaseContentType(entity); + PersistTemplates(entity, true); entity.ResetDirtyProperties(); } diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index a72582acae..499e2ba849 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -29,7 +29,6 @@ namespace Umbraco.Core.Persistence.Repositories private readonly CacheHelper _cacheHelper; private readonly IContentTypeRepository _contentTypeRepository; private readonly DataTypePreValueRepository _preValRepository; - private readonly EntityContainerRepository _containerRepository; public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, CacheHelper cache, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider sqlSyntax, IContentTypeRepository contentTypeRepository) @@ -38,29 +37,6 @@ namespace Umbraco.Core.Persistence.Repositories _cacheHelper = cacheHelper; _contentTypeRepository = contentTypeRepository; _preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax); - _containerRepository = new EntityContainerRepository(work, cache, logger, sqlSyntax, new Guid(Constants.ObjectTypes.DataTypeContainer), NodeObjectTypeId); - } - - /// - /// Deletes a folder - this will move all contained entities into their parent - /// - /// - public void DeleteContainer(int containerId) - { - var found = _containerRepository.Get(containerId); - _containerRepository.Delete(found); - } - - public EntityContainer CreateContainer(int parentId, string name, int userId) - { - var container = new EntityContainer - { - ParentId = parentId, - Name = name, - CreatorId = userId - }; - _containerRepository.AddOrUpdate(container); - return container; } #region Overrides of RepositoryBase @@ -355,19 +331,17 @@ AND umbracoNode.id <> @id", AddOrUpdatePreValues(dtd, values); } - public IEnumerable> Move(IDataTypeDefinition toMove, int parentId) + public IEnumerable> Move(IDataTypeDefinition toMove, EntityContainer container) { - if (parentId > 0) + var parentId = -1; + if (container != null) { - var container = _containerRepository.Get(parentId); - if (container == null) - throw new DataOperationException(MoveOperationStatusType.FailedParentNotFound); - // Check on paths if ((string.Format(",{0},", container.Path)).IndexOf(string.Format(",{0},", toMove.Id), StringComparison.Ordinal) > -1) { throw new DataOperationException(MoveOperationStatusType.FailedNotAllowedByPath); } + parentId = container.Id; } //used to track all the moved entities to be given to the event diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs index 033b5dc6e0..6fd5045e04 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs @@ -14,23 +14,13 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { /// - /// An internal repository for managing entity containers such as doc type, media type, data type containers + /// An internal repository for managing entity containers such as doc type, media type, data type containers. /// - /// - /// All we're supporting here is a single get, creating and deleting - /// internal class EntityContainerRepository : PetaPocoRepositoryBase { - private readonly Guid _containerObjectType; - private readonly Guid _entityObjectType; - - public EntityContainerRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, - Guid containerObjectType, Guid entityObjectType) + public EntityContainerRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) - { - _containerObjectType = containerObjectType; - _entityObjectType = entityObjectType; - } + { } /// /// Do not cache anything @@ -42,22 +32,19 @@ namespace Umbraco.Core.Persistence.Repositories protected override EntityContainer PerformGet(int id) { - var sql = GetBaseQuery(false); - sql.Where(GetBaseWhereClause(), new { Id = id, NodeObjectType = _containerObjectType }); + var sql = GetBaseQuery(false).Where(GetBaseWhereClause(), new { id }); - var containerDto = Database.Fetch(sql).FirstOrDefault(); - if (containerDto == null) - return null; + var nodeDto = Database.Fetch(sql).FirstOrDefault(); + return nodeDto == null ? null : CreateEntity(nodeDto); + } - var entity = new EntityContainer(containerDto.NodeId, - containerDto.ParentId, containerDto.Text, containerDto.UserId ?? 0, - containerDto.Path); + // temp - so we don't have to implement GetByQuery + public EntityContainer Get(Guid id) + { + var sql = GetBaseQuery(false).Where("UniqueId=@uniqueId", new { uniqueId = id }); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - entity.ResetDirtyProperties(false); - - return entity; + var nodeDto = Database.Fetch(sql).FirstOrDefault(); + return nodeDto == null ? null : CreateEntity(nodeDto); } protected override IEnumerable PerformGetAll(params int[] ids) @@ -70,6 +57,26 @@ namespace Umbraco.Core.Persistence.Repositories throw new NotImplementedException(); } + private static EntityContainer CreateEntity(NodeDto nodeDto) + { + if (nodeDto.NodeObjectType.HasValue == false) + throw new InvalidOperationException("Node with id " + nodeDto.NodeId + " has no object type."); + + // throws if node is not a container + var containedObjectType = EntityContainer.GetContainedObjectType(nodeDto.NodeObjectType.Value); + + var entity = new EntityContainer(nodeDto.NodeId, nodeDto.UniqueId, + nodeDto.ParentId, nodeDto.Path, nodeDto.Level, nodeDto.SortOrder, + containedObjectType, + nodeDto.Text, nodeDto.UserId ?? 0); + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + entity.ResetDirtyProperties(false); + + return entity; + } + protected override Sql GetBaseQuery(bool isCount) { var sql = new Sql(); @@ -86,7 +93,7 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoNode.id = @Id and nodeObjectType = @NodeObjectType"; + return "umbracoNode.id = @id"; //" and nodeObjectType = @NodeObjectType"; } protected override IEnumerable GetDeleteClauses() @@ -96,97 +103,150 @@ namespace Umbraco.Core.Persistence.Repositories protected override Guid NodeObjectTypeId { - get { return _containerObjectType; } + get { throw new NotImplementedException(); } } protected override void PersistDeletedItem(EntityContainer entity) { - var exists = Database.FirstOrDefault( - new Sql().Select("*") - .From(SqlSyntax) - .Where(dto => dto.NodeId == entity.Id && dto.NodeObjectType == _containerObjectType)); + var nodeDto = Database.FirstOrDefault(new Sql().Select("*") + .From(SqlSyntax) + .Where(dto => dto.NodeId == entity.Id && dto.NodeObjectType == entity.ContainerObjectType)); - if (exists == null) return; + if (nodeDto == null) return; - //We need to move the content types and folders that exist under this folder to it's parent folder - var children = Database.Fetch( - new Sql().Select("*") - .From(SqlSyntax) - .Where("parentID=@parentID AND (nodeObjectType=@entityObjectType OR nodeObjectType=@containerObjectType)", - new {parentID = entity.Id, entityObjectType = _entityObjectType, containerObjectType = _containerObjectType})); + // move children to the parent so they are not orphans + var childDtos = Database.Fetch(new Sql().Select("*") + .From(SqlSyntax) + .Where("parentID=@parentID AND (nodeObjectType=@containedObjectType OR nodeObjectType=@containerObjectType)", + new + { + parentID = entity.Id, + containedObjectType = entity.ContainedObjectType, + containerObjectType = entity.ContainerObjectType + })); - foreach (var childDto in children) + foreach (var childDto in childDtos) { - childDto.ParentId = exists.ParentId; + childDto.ParentId = nodeDto.ParentId; Database.Update(childDto); } - //now that everything is moved up a level, we need to delete the container - Database.Delete(exists); + // delete + Database.Delete(nodeDto); } protected override void PersistNewItem(EntityContainer entity) { entity.Name = entity.Name.Trim(); - Mandate.ParameterNotNullOrEmpty(entity.Name, "entity.Name"); - var exists = Database.FirstOrDefault( - new Sql().Select("*") - .From(SqlSyntax) - .Where(dto => dto.ParentId == entity.ParentId && dto.Text == entity.Name && dto.NodeObjectType == _containerObjectType)); - - if (exists != null) - { - throw new InvalidOperationException("A folder with the same name already exists"); - } + // guard against duplicates + var nodeDto = Database.FirstOrDefault(new Sql().Select("*") + .From(SqlSyntax) + .Where(dto => dto.ParentId == entity.ParentId && dto.Text == entity.Name && dto.NodeObjectType == entity.ContainerObjectType)); + if (nodeDto != null) + throw new InvalidOperationException("A container with the same name already exists."); + // create var level = 0; var path = "-1"; if (entity.ParentId > -1) { - var parent = Database.FirstOrDefault( - new Sql().Select("*") - .From(SqlSyntax) - .Where(dto => dto.NodeId == entity.ParentId && dto.NodeObjectType == _containerObjectType)); + var parentDto = Database.FirstOrDefault(new Sql().Select("*") + .From(SqlSyntax) + .Where(dto => dto.NodeId == entity.ParentId && dto.NodeObjectType == entity.ContainerObjectType)); - if (parent == null) - { - throw new NullReferenceException("No content type container found with parent id " + entity.ParentId); - } - level = parent.Level; - path = parent.Path; + if (parentDto == null) + throw new NullReferenceException("Could not find parent container with id " + entity.ParentId); + + level = parentDto.Level; + path = parentDto.Path; } - var nodeDto = new NodeDto + // note: sortOrder is NOT managed and always zero for containers + + nodeDto = new NodeDto { CreateDate = DateTime.Now, Level = Convert.ToInt16(level + 1), - NodeObjectType = _containerObjectType, + NodeObjectType = entity.ContainerObjectType, ParentId = entity.ParentId, Path = path, SortOrder = 0, Text = entity.Name, - Trashed = false, - UniqueId = Guid.NewGuid(), UserId = entity.CreatorId }; - var id = Convert.ToInt32(Database.Insert(nodeDto)); + if (entity.Key != default(Guid)) + nodeDto.UniqueId = entity.Key; - //update the path + // insert, get the id, update the path with the id + var id = Convert.ToInt32(Database.Insert(nodeDto)); nodeDto.Path = nodeDto.Path + "," + nodeDto.NodeId; Database.Save(nodeDto); + // refresh the entity entity.Id = id; + entity.Key = nodeDto.UniqueId; entity.Path = nodeDto.Path; - + entity.Level = nodeDto.Level; + entity.SortOrder = 0; + entity.CreateDate = nodeDto.CreateDate; entity.ResetDirtyProperties(); } + // beware! does NOT manage descendants in case of a new parent + // protected override void PersistUpdatedItem(EntityContainer entity) { - throw new NotImplementedException(); + entity.Name = entity.Name.Trim(); + Mandate.ParameterNotNullOrEmpty(entity.Name, "entity.Name"); + + // find container to update + var nodeDto = Database.FirstOrDefault(new Sql().Select("*") + .From(SqlSyntax) + .Where(dto => dto.NodeId == entity.Id && dto.NodeObjectType == entity.ContainerObjectType)); + if (nodeDto == null) + throw new InvalidOperationException("Could not find container with id " + entity.Id); + + // guard against duplicates + var dupNodeDto = Database.FirstOrDefault(new Sql().Select("*") + .From(SqlSyntax) + .Where(dto => dto.ParentId == entity.ParentId && dto.Text == entity.Name && dto.NodeObjectType == entity.ContainerObjectType)); + if (dupNodeDto != null && dupNodeDto.NodeId != nodeDto.NodeId) + throw new InvalidOperationException("A container with the same name already exists."); + + // update + nodeDto.Text = entity.Name; + if (nodeDto.ParentId != entity.ParentId) + { + nodeDto.Level = 0; + nodeDto.Path = "-1"; + if (entity.ParentId > -1) + { + var parent = Database.FirstOrDefault(new Sql().Select("*") + .From(SqlSyntax) + .Where(dto => dto.NodeId == entity.ParentId && dto.NodeObjectType == entity.ContainerObjectType)); + + if (parent == null) + throw new NullReferenceException("Could not find parent container with id " + entity.ParentId); + + nodeDto.Level = Convert.ToInt16(parent.Level + 1); + nodeDto.Path = parent.Path + "," + nodeDto.NodeId; + } + nodeDto.ParentId = entity.ParentId; + } + + // note: sortOrder is NOT managed and always zero for containers + + // update + Database.Update(nodeDto); + + // refresh the entity + entity.Path = nodeDto.Path; + entity.Level = nodeDto.Level; + entity.SortOrder = 0; + entity.ResetDirtyProperties(); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs index 9a90af21d6..625023fd9e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs @@ -21,21 +21,6 @@ namespace Umbraco.Core.Persistence.Repositories /// IEnumerable GetAllPropertyTypeAliases(); - /// - /// Creates a folder for content types - /// - /// - /// - /// - /// - EntityContainer CreateContainer(int parentId, string name, int userId); - - /// - /// Deletes a folder - this will move all contained content types into their parent - /// - /// - void DeleteContainer(int containerId); - - IEnumerable> Move(IContentType toMove, int parentId); + IEnumerable> Move(IContentType toMove, EntityContainer container); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IDataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IDataTypeDefinitionRepository.cs index 2ae9dfe551..6d7265f717 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IDataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IDataTypeDefinitionRepository.cs @@ -7,14 +7,11 @@ namespace Umbraco.Core.Persistence.Repositories { public interface IDataTypeDefinitionRepository : IRepositoryQueryable { - EntityContainer CreateContainer(int parentId, string name, int userId); - void DeleteContainer(int containerId); - PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId); string GetPreValueAsString(int preValueId); void AddOrUpdatePreValues(IDataTypeDefinition dataType, IDictionary values); void AddOrUpdatePreValues(int dataTypeId, IDictionary values); - IEnumerable> Move(IDataTypeDefinition toMove, int parentId); + IEnumerable> Move(IDataTypeDefinition toMove, EntityContainer container); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaTypeRepository.cs index 17073d18b3..6d9331a22a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaTypeRepository.cs @@ -15,21 +15,6 @@ namespace Umbraco.Core.Persistence.Repositories /// An enumerable list of objects IEnumerable GetByQuery(IQuery query); - /// - /// Creates a folder for content types - /// - /// - /// - /// - /// - EntityContainer CreateContainer(int parentId, string name, int userId); - - /// - /// Deletes a folder - this will move all contained content types into their parent - /// - /// - void DeleteContainer(int folderId); - - IEnumerable> Move(IMediaType toMove, int parentId); + IEnumerable> Move(IMediaType toMove, EntityContainer container); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs index c4fa80a7d5..3616a145ce 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs @@ -23,7 +23,7 @@ namespace Umbraco.Core.Persistence.Repositories { public MediaTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) - : base(work, cache, logger, sqlSyntax, new Guid(Constants.ObjectTypes.MediaTypeContainer)) + : base(work, cache, logger, sqlSyntax) { } @@ -120,10 +120,7 @@ namespace Umbraco.Core.Persistence.Repositories { ((MediaType)entity).AddingEntity(); - var factory = new MediaTypeFactory(NodeObjectTypeId); - var dto = factory.BuildDto(entity); - - PersistNewBaseContentType(dto, entity); + PersistNewBaseContentType(entity); entity.ResetDirtyProperties(); } @@ -148,10 +145,7 @@ namespace Umbraco.Core.Persistence.Repositories entity.SortOrder = maxSortOrder + 1; } - var factory = new MediaTypeFactory(NodeObjectTypeId); - var dto = factory.BuildDto(entity); - - PersistUpdatedBaseContentType(dto, entity); + PersistUpdatedBaseContentType(entity); entity.ResetDirtyProperties(); } diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs index 5b4c1ff877..91b6e67cf9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs @@ -102,7 +102,7 @@ namespace Umbraco.Core.Persistence.Repositories "cmsPropertyType.validationRegExp", "cmsPropertyType.dataTypeId", "cmsPropertyType.sortOrder AS PropertyTypeSortOrder", "cmsPropertyType.propertyTypeGroupId AS PropertyTypesGroupId", "cmsMemberType.memberCanEdit", "cmsMemberType.viewOnProfile", "cmsDataType.propertyEditorAlias", "cmsDataType.dbType", "cmsPropertyTypeGroup.id AS PropertyTypeGroupId", - "cmsPropertyTypeGroup.text AS PropertyGroupName", "cmsPropertyTypeGroup.parentGroupId", + "cmsPropertyTypeGroup.text AS PropertyGroupName", "cmsPropertyTypeGroup.sortorder AS PropertyGroupSortOrder", "cmsPropertyTypeGroup.contenttypeNodeId") .From() .InnerJoin().On(left => left.NodeId, right => right.NodeId) @@ -182,12 +182,10 @@ namespace Umbraco.Core.Persistence.Repositories entity.AddPropertyType(standardPropertyType.Value, Constants.Conventions.Member.StandardPropertiesGroupName); } - var factory = new MemberTypeFactory(NodeObjectTypeId); - var dto = factory.BuildDto(entity); + var factory = new ContentTypeFactory(); EnsureExplicitDataTypeForBuiltInProperties(entity); - - PersistNewBaseContentType(dto, entity); + PersistNewBaseContentType(entity); //Handles the MemberTypeDto (cmsMemberType table) var memberTypeDtos = factory.BuildMemberTypeDtos(entity); @@ -219,17 +217,13 @@ namespace Umbraco.Core.Persistence.Repositories entity.SortOrder = maxSortOrder + 1; } - var factory = new MemberTypeFactory(NodeObjectTypeId); - var dto = factory.BuildDto(entity); + var factory = new ContentTypeFactory(); EnsureExplicitDataTypeForBuiltInProperties(entity); + PersistUpdatedBaseContentType(entity); - PersistUpdatedBaseContentType(dto, entity); - - //Remove existing entries before inserting new ones + // remove and insert - handle cmsMemberType table Database.Delete("WHERE NodeId = @Id", new { Id = entity.Id }); - - //Handles the MemberTypeDto (cmsMemberType table) var memberTypeDtos = factory.BuildMemberTypeDtos(entity); foreach (var memberTypeDto in memberTypeDtos) { diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index f9367d8433..69ca5f78e2 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -303,5 +303,13 @@ namespace Umbraco.Core.Persistence CacheHelper.CreateDisabledCacheHelper(), //never cache _logger, _sqlSyntax); } + + internal virtual EntityContainerRepository CreateEntityContainerRepository(IDatabaseUnitOfWork uow) + { + return new EntityContainerRepository( + uow, + _cacheHelper, + _logger, _sqlSyntax); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Security/AuthenticationExtensions.cs b/src/Umbraco.Core/Security/AuthenticationExtensions.cs index e332cb6dca..1c7c544ed8 100644 --- a/src/Umbraco.Core/Security/AuthenticationExtensions.cs +++ b/src/Umbraco.Core/Security/AuthenticationExtensions.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +using System.ComponentModel; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; @@ -15,7 +16,6 @@ using Microsoft.Owin; using Newtonsoft.Json; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Membership; -using Microsoft.Owin; using Umbraco.Core.Logging; namespace Umbraco.Core.Security @@ -157,9 +157,6 @@ namespace Umbraco.Core.Security return new HttpContextWrapper(http).GetCurrentIdentity(authenticateRequestIfNotFound); } - /// - /// This clears the forms authentication cookie - /// public static void UmbracoLogout(this HttpContextBase http) { if (http == null) throw new ArgumentNullException("http"); @@ -170,6 +167,8 @@ namespace Umbraco.Core.Security /// This clears the forms authentication cookie for webapi since cookies are handled differently /// /// + [Obsolete("Use OWIN IAuthenticationManager.SignOut instead")] + [EditorBrowsable(EditorBrowsableState.Never)] public static void UmbracoLogoutWebApi(this HttpResponseMessage response) { if (response == null) throw new ArgumentNullException("response"); @@ -195,11 +194,8 @@ namespace Umbraco.Core.Security response.Headers.AddCookies(new[] { authCookie, prevCookie, extLoginCookie }); } - /// - /// This adds the forms authentication cookie for webapi since cookies are handled differently - /// - /// - /// + [Obsolete("Use WebSecurity.SetPrincipalForRequest")] + [EditorBrowsable(EditorBrowsableState.Never)] public static FormsAuthenticationTicket UmbracoLoginWebApi(this HttpResponseMessage response, IUser user) { if (response == null) throw new ArgumentNullException("response"); @@ -250,26 +246,29 @@ namespace Umbraco.Core.Security if (http == null) throw new ArgumentNullException("http"); new HttpContextWrapper(http).UmbracoLogout(); } - + /// - /// Renews the Umbraco authentication ticket + /// This will force ticket renewal in the OWIN pipeline /// /// /// public static bool RenewUmbracoAuthTicket(this HttpContextBase http) { if (http == null) throw new ArgumentNullException("http"); - return RenewAuthTicket(http, - UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, - UmbracoConfig.For.UmbracoSettings().Security.AuthCookieDomain, - //Umbraco has always persisted it's original cookie for 1 day so we'll keep it that way - 1440); + http.Items["umbraco-force-auth"] = true; + return true; } + /// + /// This will force ticket renewal in the OWIN pipeline + /// + /// + /// internal static bool RenewUmbracoAuthTicket(this HttpContext http) { if (http == null) throw new ArgumentNullException("http"); - return new HttpContextWrapper(http).RenewUmbracoAuthTicket(); + http.Items["umbraco-force-auth"] = true; + return true; } /// @@ -390,8 +389,7 @@ namespace Umbraco.Core.Security //ensure there's def an expired cookie http.Response.Cookies.Add(new HttpCookie(c) { Expires = DateTime.Now.AddYears(-1) }); } - } - + } } private static FormsAuthenticationTicket GetAuthTicket(this HttpContextBase http, string cookieName) @@ -432,51 +430,6 @@ namespace Umbraco.Core.Security return FormsAuthentication.Decrypt(formsCookie); } - /// - /// Renews the forms authentication ticket & cookie - /// - /// - /// - /// - /// - /// true if there was a ticket to renew otherwise false if there was no ticket - private static bool RenewAuthTicket(this HttpContextBase http, string cookieName, string cookieDomain, int minutesPersisted) - { - if (http == null) throw new ArgumentNullException("http"); - //get the ticket - var ticket = GetAuthTicket(http, cookieName); - //renew the ticket - var renewed = FormsAuthentication.RenewTicketIfOld(ticket); - if (renewed == null) - { - return false; - } - - //get the request cookie to get it's expiry date, - //NOTE: this will never be null becaues we already do this - // check in teh GetAuthTicket. - var formsCookie = http.Request.Cookies[cookieName]; - - //encrypt it - var hash = FormsAuthentication.Encrypt(renewed); - //write it to the response - var cookie = new HttpCookie(cookieName, hash) - { - Expires = DateTime.Now.AddMinutes(minutesPersisted), - Domain = cookieDomain - }; - - if (GlobalSettings.UseSSL) - cookie.Secure = true; - - //ensure http only, this should only be able to be accessed via the server - cookie.HttpOnly = true; - - //rewrite the cooke - http.Response.Cookies.Set(cookie); - return true; - } - /// /// Creates a custom FormsAuthentication ticket with the data specified /// diff --git a/src/Umbraco.Core/Security/BackOfficeCookieAuthenticationProvider.cs b/src/Umbraco.Core/Security/BackOfficeCookieAuthenticationProvider.cs index 5247ea0af4..086d1a77bf 100644 --- a/src/Umbraco.Core/Security/BackOfficeCookieAuthenticationProvider.cs +++ b/src/Umbraco.Core/Security/BackOfficeCookieAuthenticationProvider.cs @@ -1,12 +1,38 @@ +using System; using System.Globalization; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; +using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; +using Umbraco.Core.Configuration; namespace Umbraco.Core.Security { public class BackOfficeCookieAuthenticationProvider : CookieAuthenticationProvider { + public override void ResponseSignOut(CookieResponseSignOutContext context) + { + base.ResponseSignOut(context); + + //Make sure the definitely all of these cookies are cleared when signing out with cookies + context.Response.Cookies.Append(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, "", new CookieOptions + { + Expires = DateTime.Now.AddYears(-1), + Path = "/" + }); + context.Response.Cookies.Append(Constants.Web.PreviewCookieName, "", new CookieOptions + { + Expires = DateTime.Now.AddYears(-1), + Path = "/" + }); + context.Response.Cookies.Append(Constants.Security.BackOfficeExternalCookieName, "", new CookieOptions + { + Expires = DateTime.Now.AddYears(-1), + Path = "/" + }); + } + /// /// Ensures that the culture is set correctly for the current back office user /// diff --git a/src/Umbraco.Core/Security/BackOfficeSignInManager.cs b/src/Umbraco.Core/Security/BackOfficeSignInManager.cs index 85d6a0c715..f1d18b9d0f 100644 --- a/src/Umbraco.Core/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Core/Security/BackOfficeSignInManager.cs @@ -53,6 +53,11 @@ namespace Umbraco.Core.Security switch (result) { case SignInStatus.Success: + _logger.WriteCore(TraceEventType.Information, 0, + string.Format( + "User: {0} logged in from IP address {1}", + userName, + _request.RemoteIpAddress), null, null); break; case SignInStatus.LockedOut: _logger.WriteCore(TraceEventType.Information, 0, diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index eccdae1ab8..eff7202201 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -41,14 +41,22 @@ namespace Umbraco.Core.Services _mediaService = mediaService; } + #region Containers + public Attempt CreateContentTypeContainer(int parentId, string name, int userId = 0) { var uow = UowProvider.GetUnitOfWork(); - using (var repo = RepositoryFactory.CreateContentTypeRepository(uow)) + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) { try { - var container = repo.CreateContainer(parentId, name, userId); + var container = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) + { + Name = name, + ParentId = parentId, + CreatorId = userId + }; + repo.AddOrUpdate(container); uow.Commit(); return Attempt.Succeed(container.Id); } @@ -63,15 +71,21 @@ namespace Umbraco.Core.Services public Attempt CreateMediaTypeContainer(int parentId, string name, int userId = 0) { var uow = UowProvider.GetUnitOfWork(); - using (var repo = RepositoryFactory.CreateMediaTypeRepository(uow)) + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) { try { - var container = repo.CreateContainer(parentId, name, userId); + var container = new EntityContainer(Constants.ObjectTypes.MediaTypeGuid) + { + Name = name, + ParentId = parentId, + CreatorId = userId + }; + repo.AddOrUpdate(container); uow.Commit(); return Attempt.Succeed(container.Id); } - catch (System.Exception ex) + catch (Exception ex) { return Attempt.Fail(ex); } @@ -79,28 +93,106 @@ namespace Umbraco.Core.Services } } - public void DeleteContentTypeContainer(int folderId, int userId = 0) + public void SaveContentTypeContainer(EntityContainer container, int userId = 0) { + SaveContainer(container, Constants.ObjectTypes.DocumentTypeGuid, "document type", userId); + } + + public void SaveMediaTypeContainer(EntityContainer container, int userId = 0) + { + SaveContainer(container, Constants.ObjectTypes.MediaTypeGuid, "media type", userId); + } + + private void SaveContainer(EntityContainer container, Guid containedObjectType, string objectTypeName, int userId) + { + if (container.ContainedObjectType != containedObjectType) + throw new InvalidOperationException("Not a " + objectTypeName + " container."); + if (container.HasIdentity && container.IsPropertyDirty("ParentId")) + throw new InvalidOperationException("Cannot save a container with a modified parent, move the container instead."); + var uow = UowProvider.GetUnitOfWork(); - using (var repo = RepositoryFactory.CreateContentTypeRepository(uow)) + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) { - repo.DeleteContainer(folderId); + repo.AddOrUpdate(container); uow.Commit(); //TODO: Audit trail ? } } - public void DeleteMediaTypeContainer(int folderId, int userId = 0) + public EntityContainer GetContentTypeContainer(int containerId) + { + return GetContainer(containerId, Constants.ObjectTypes.DocumentTypeGuid); + } + + public EntityContainer GetMediaTypeContainer(int containerId) + { + return GetContainer(containerId, Constants.ObjectTypes.MediaTypeGuid); + } + + private EntityContainer GetContainer(int containerId, Guid containedObjectType) { var uow = UowProvider.GetUnitOfWork(); - using (var repo = RepositoryFactory.CreateMediaTypeRepository(uow)) + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) { - repo.DeleteContainer(folderId); + var container = repo.Get(containerId); + return container != null && container.ContainedObjectType == containedObjectType + ? container + : null; + } + } + + public EntityContainer GetContentTypeContainer(Guid containerId) + { + return GetContainer(containerId, Constants.ObjectTypes.DocumentTypeGuid); + } + + public EntityContainer GetMediaTypeContainer(Guid containerId) + { + return GetContainer(containerId, Constants.ObjectTypes.MediaTypeGuid); + } + + private EntityContainer GetContainer(Guid containerId, Guid containedObjectType) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) + { + var container = repo.Get(containerId); + return container != null && container.ContainedObjectType == containedObjectType + ? container + : null; + } + } + + public void DeleteContentTypeContainer(int containerId, int userId = 0) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) + { + var container = repo.Get(containerId); + if (container == null) return; + if (container.ContainedObjectType != Constants.ObjectTypes.DocumentTypeGuid) return; + repo.Delete(container); uow.Commit(); //TODO: Audit trail ? } } + public void DeleteMediaTypeContainer(int containerId, int userId = 0) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) + { + var container = repo.Get(containerId); + if (container == null) return; + if (container.ContainedObjectType != Constants.ObjectTypes.MediaTypeGuid) return; + repo.Delete(container); + uow.Commit(); + //TODO: Audit trail ? + } + } + + #endregion + /// /// Gets all property type aliases. /// @@ -712,12 +804,12 @@ namespace Umbraco.Core.Services } } - public Attempt> MoveMediaType(IMediaType toMove, int parentId) + public Attempt> MoveMediaType(IMediaType toMove, int containerId) { var evtMsgs = EventMessagesFactory.Get(); if (MovingMediaType.IsRaisedEventCancelled( - new MoveEventArgs(evtMsgs, new MoveEventInfo(toMove, toMove.Path, parentId)), + new MoveEventArgs(evtMsgs, new MoveEventInfo(toMove, toMove.Path, containerId)), this)) { return Attempt.Fail( @@ -727,11 +819,19 @@ namespace Umbraco.Core.Services var moveInfo = new List>(); var uow = UowProvider.GetUnitOfWork(); + using (var containerRepository = RepositoryFactory.CreateEntityContainerRepository(uow)) using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) { try { - moveInfo.AddRange(repository.Move(toMove, parentId)); + EntityContainer container = null; + if (containerId > 0) + { + container = containerRepository.Get(containerId); + if (container == null || container.ContainedObjectType != Constants.ObjectTypes.MediaTypeGuid) + throw new DataOperationException(MoveOperationStatusType.FailedParentNotFound); + } + moveInfo.AddRange(repository.Move(toMove, container)); } catch (DataOperationException ex) { @@ -747,12 +847,12 @@ namespace Umbraco.Core.Services new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); } - public Attempt> MoveContentType(IContentType toMove, int parentId) + public Attempt> MoveContentType(IContentType toMove, int containerId) { var evtMsgs = EventMessagesFactory.Get(); if (MovingContentType.IsRaisedEventCancelled( - new MoveEventArgs(evtMsgs, new MoveEventInfo(toMove, toMove.Path, parentId)), + new MoveEventArgs(evtMsgs, new MoveEventInfo(toMove, toMove.Path, containerId)), this)) { return Attempt.Fail( @@ -762,11 +862,19 @@ namespace Umbraco.Core.Services var moveInfo = new List>(); var uow = UowProvider.GetUnitOfWork(); + using (var containerRepository = RepositoryFactory.CreateEntityContainerRepository(uow)) using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) { try { - moveInfo.AddRange(repository.Move(toMove, parentId)); + EntityContainer container = null; + if (containerId > 0) + { + container = containerRepository.Get(containerId); + if (container == null || container.ContainedObjectType != Constants.ObjectTypes.DocumentTypeGuid) + throw new DataOperationException(MoveOperationStatusType.FailedParentNotFound); + } + moveInfo.AddRange(repository.Move(toMove, container)); } catch (DataOperationException ex) { diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index 4def86836a..bd505ec283 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -27,14 +27,22 @@ namespace Umbraco.Core.Services { } + #region Containers + public Attempt CreateContainer(int parentId, string name, int userId = 0) { var uow = UowProvider.GetUnitOfWork(); - using (var repo = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) { try { - var container = repo.CreateContainer(parentId, name, userId); + var container = new EntityContainer(Constants.ObjectTypes.DataTypeGuid) + { + Name = name, + ParentId = parentId, + CreatorId = userId + }; + repo.AddOrUpdate(container); uow.Commit(); return Attempt.Succeed(container.Id); } @@ -46,17 +54,62 @@ namespace Umbraco.Core.Services } } - public void DeleteContainer(int containerId, int userId = 0) + public EntityContainer GetContainer(int containerId) { var uow = UowProvider.GetUnitOfWork(); - using (var repo = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) { - repo.DeleteContainer(containerId); + var container = repo.Get(containerId); + return container != null && container.ContainedObjectType == Constants.ObjectTypes.DataTypeGuid + ? container + : null; + } + } + + public EntityContainer GetContainer(Guid containerId) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) + { + var container = repo.Get(containerId); + return container != null && container.ContainedObjectType == Constants.ObjectTypes.DataTypeGuid + ? container + : null; + } + } + + public void SaveContainer(EntityContainer container, int userId = 0) + { + if (container.ContainedObjectType != Constants.ObjectTypes.DataTypeGuid) + throw new InvalidOperationException("Not a data type container."); + if (container.HasIdentity && container.IsPropertyDirty("ParentId")) + throw new InvalidOperationException("Cannot save a container with a modified parent, move the container instead."); + + var uow = UowProvider.GetUnitOfWork(); + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) + { + repo.AddOrUpdate(container); uow.Commit(); //TODO: Audit trail ? } } + public void DeleteContainer(int containerId, int userId = 0) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) + { + var container = repo.Get(containerId); + if (container == null) return; + if (container.ContainedObjectType != Constants.ObjectTypes.DataTypeGuid) return; + repo.Delete(container); + uow.Commit(); + //TODO: Audit trail ? + } + } + + #endregion + /// /// Gets a by its Name /// @@ -199,11 +252,19 @@ namespace Umbraco.Core.Services var moveInfo = new List>(); var uow = UowProvider.GetUnitOfWork(); + using (var containerRepository = RepositoryFactory.CreateEntityContainerRepository(uow)) using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) { try { - moveInfo.AddRange(repository.Move(toMove, parentId)); + EntityContainer container = null; + if (parentId > 0) + { + container = containerRepository.Get(parentId); + if (container == null || container.ContainedObjectType != Constants.ObjectTypes.DataTypeGuid) + throw new DataOperationException(MoveOperationStatusType.FailedParentNotFound); + } + moveInfo.AddRange(repository.Move(toMove, container)); } catch (DataOperationException ex) { diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/EntityXmlSerializer.cs index 772009e89a..712f98aeb2 100644 --- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/EntityXmlSerializer.cs @@ -100,6 +100,8 @@ namespace Umbraco.Core.Services xml.Add(new XAttribute("loginName", member.Username)); xml.Add(new XAttribute("email", member.Email)); + + xml.Add(new XAttribute("icon", member.ContentType.Icon)); return xml; } diff --git a/src/Umbraco.Core/Services/FileService.cs b/src/Umbraco.Core/Services/FileService.cs index aa14b1afa0..c3d6c3f2de 100644 --- a/src/Umbraco.Core/Services/FileService.cs +++ b/src/Umbraco.Core/Services/FileService.cs @@ -294,10 +294,10 @@ namespace Umbraco.Core.Services } /// - /// Gets a object by its alias + /// Gets a object by its alias. /// - /// Alias of the template - /// A object + /// The alias of the template. + /// The object matching the alias, or null. public ITemplate GetTemplate(string alias) { using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork())) @@ -307,10 +307,10 @@ namespace Umbraco.Core.Services } /// - /// Gets a object by its alias + /// Gets a object by its identifier. /// - /// Id of the template - /// A object + /// The identifer of the template. + /// The object matching the identifier, or null. public ITemplate GetTemplate(int id) { using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork())) @@ -319,6 +319,20 @@ namespace Umbraco.Core.Services } } + /// + /// Gets a object by its guid identifier. + /// + /// The guid identifier of the template. + /// The object matching the identifier, or null. + public ITemplate GetTemplate(Guid id) + { + using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.Key == id); + return repository.GetByQuery(query).SingleOrDefault(); + } + } + public IEnumerable GetTemplateDescendants(string alias) { using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork())) diff --git a/src/Umbraco.Core/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs index 6be2174fdf..9e59308a39 100644 --- a/src/Umbraco.Core/Services/IContentTypeService.cs +++ b/src/Umbraco.Core/Services/IContentTypeService.cs @@ -20,8 +20,14 @@ namespace Umbraco.Core.Services Attempt CreateContentTypeContainer(int parentId, string name, int userId = 0); Attempt CreateMediaTypeContainer(int parentId, string name, int userId = 0); + void SaveContentTypeContainer(EntityContainer container, int userId = 0); + void SaveMediaTypeContainer(EntityContainer container, int userId = 0); + EntityContainer GetContentTypeContainer(int containerId); + EntityContainer GetContentTypeContainer(Guid containerId); + EntityContainer GetMediaTypeContainer(int containerId); + EntityContainer GetMediaTypeContainer(Guid containerId); void DeleteMediaTypeContainer(int folderId, int userId = 0); - void DeleteContentTypeContainer(int folderId, int userId = 0); + void DeleteContentTypeContainer(int containerId, int userId = 0); /// /// Gets all property type aliases. @@ -263,7 +269,7 @@ namespace Umbraco.Core.Services /// True if the media type has any children otherwise False bool MediaTypeHasChildren(Guid id); - Attempt> MoveMediaType(IMediaType toMove, int parentId); - Attempt> MoveContentType(IContentType toMove, int parentId); + Attempt> MoveMediaType(IMediaType toMove, int containerId); + Attempt> MoveContentType(IContentType toMove, int containerId); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IDataTypeService.cs b/src/Umbraco.Core/Services/IDataTypeService.cs index 6a9137030b..ce9e4a3901 100644 --- a/src/Umbraco.Core/Services/IDataTypeService.cs +++ b/src/Umbraco.Core/Services/IDataTypeService.cs @@ -11,6 +11,9 @@ namespace Umbraco.Core.Services public interface IDataTypeService : IService { Attempt CreateContainer(int parentId, string name, int userId = 0); + void SaveContainer(EntityContainer container, int userId = 0); + EntityContainer GetContainer(int containerId); + EntityContainer GetContainer(Guid containerId); void DeleteContainer(int containerId, int userId = 0); /// diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index e56abc71b5..18318de1db 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -117,19 +117,26 @@ namespace Umbraco.Core.Services IEnumerable GetTemplates(int masterTemplateId); /// - /// Gets a object by its alias + /// Gets a object by its alias. /// - /// Alias of the template - /// A object + /// The alias of the template. + /// The object matching the alias, or null. ITemplate GetTemplate(string alias); /// - /// Gets a object by its alias + /// Gets a object by its identifier. /// - /// Id of the template - /// A object + /// The identifer of the template. + /// The object matching the identifier, or null. ITemplate GetTemplate(int id); + /// + /// Gets a object by its guid identifier. + /// + /// The guid identifier of the template. + /// The object matching the identifier, or null. + ITemplate GetTemplate(Guid id); + /// /// Gets the template descendants /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 5080a31a5c..997f57b0ca 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -409,6 +409,8 @@ + + @@ -727,7 +729,6 @@ - @@ -803,7 +804,6 @@ - @@ -1129,7 +1129,7 @@ - + @@ -1333,6 +1333,9 @@ Constants.cs + + Constants.cs + Constants.cs diff --git a/src/Umbraco.Tests/Models/ContentTypeTests.cs b/src/Umbraco.Tests/Models/ContentTypeTests.cs index 325c8aefc1..0589d7d173 100644 --- a/src/Umbraco.Tests/Models/ContentTypeTests.cs +++ b/src/Umbraco.Tests/Models/ContentTypeTests.cs @@ -205,7 +205,7 @@ namespace Umbraco.Tests.Models } Assert.AreNotSame(clone.PropertyTypes, contentType.PropertyTypes); Assert.AreEqual(clone.PropertyTypes.Count(), contentType.PropertyTypes.Count()); - Assert.AreEqual(0, ((ContentTypeBase)clone).NonGroupedPropertyTypes.Count()); + Assert.AreEqual(0, clone.NoGroupPropertyTypes.Count()); for (var index = 0; index < contentType.PropertyTypes.Count(); index++) { Assert.AreNotSame(clone.PropertyTypes.ElementAt(index), contentType.PropertyTypes.ElementAt(index)); diff --git a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs index bea607bb26..76d1220ea3 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs @@ -246,7 +246,10 @@ namespace Umbraco.Tests.Models.Mapping [Test] public void PropertyGroupBasic_To_PropertyGroup() { - var basic = new PropertyGroupBasic() + _dataTypeService.Setup(x => x.GetDataTypeDefinitionById(It.IsAny())) + .Returns(new DataTypeDefinition("test")); + + var basic = new PropertyGroupBasic { Id = 222, Name = "Group 1", @@ -286,7 +289,19 @@ namespace Umbraco.Tests.Models.Mapping } }; - var result = Mapper.Map(basic); + var contentType = new ContentTypeSave + { + Id = 0, + ParentId = -1, + Alias = "alias", + AllowedTemplates = Enumerable.Empty(), + Groups = new[] { basic } + }; + + // proper group properties mapping takes place when mapping the content type, + // not when mapping the group - because of inherited properties and such + //var result = Mapper.Map(basic); + var result = Mapper.Map(contentType).PropertyGroups[0]; Assert.AreEqual(basic.Name, result.Name); Assert.AreEqual(basic.Id, result.Id); diff --git a/src/Umbraco.Tests/Models/PropertyGroupTests.cs b/src/Umbraco.Tests/Models/PropertyGroupTests.cs index c5ae39be07..ec345051ec 100644 --- a/src/Umbraco.Tests/Models/PropertyGroupTests.cs +++ b/src/Umbraco.Tests/Models/PropertyGroupTests.cs @@ -54,8 +54,7 @@ namespace Umbraco.Tests.Models Key = Guid.NewGuid(), Name = "Group1", SortOrder = 555, - UpdateDate = DateTime.Now, - ParentId = 9 + UpdateDate = DateTime.Now }; var clone = (PropertyGroup)pg.DeepClone(); @@ -68,7 +67,6 @@ namespace Umbraco.Tests.Models Assert.AreEqual(clone.Name, pg.Name); Assert.AreEqual(clone.SortOrder, pg.SortOrder); Assert.AreEqual(clone.UpdateDate, pg.UpdateDate); - Assert.AreEqual(clone.ParentId, pg.ParentId); Assert.AreNotSame(clone.PropertyTypes, pg.PropertyTypes); Assert.AreEqual(clone.PropertyTypes, pg.PropertyTypes); Assert.AreEqual(clone.PropertyTypes.Count, pg.PropertyTypes.Count); @@ -133,8 +131,7 @@ namespace Umbraco.Tests.Models Key = Guid.NewGuid(), Name = "Group1", SortOrder = 555, - UpdateDate = DateTime.Now, - ParentId = 9 + UpdateDate = DateTime.Now }; var result = ss.ToStream(pg); diff --git a/src/Umbraco.Tests/Mvc/RenderIndexActionSelectorAttributeTests.cs b/src/Umbraco.Tests/Mvc/RenderIndexActionSelectorAttributeTests.cs new file mode 100644 index 0000000000..3f327d3155 --- /dev/null +++ b/src/Umbraco.Tests/Mvc/RenderIndexActionSelectorAttributeTests.cs @@ -0,0 +1,173 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; +using System.Web.Routing; +using Moq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Logging; +using Umbraco.Core.Profiling; +using Umbraco.Web; +using Umbraco.Web.Models; +using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; +using Umbraco.Web.Security; + +namespace Umbraco.Tests.Mvc +{ + [TestFixture] + public class RenderIndexActionSelectorAttributeTests + { + private MethodInfo GetRenderMvcControllerIndexMethodFromCurrentType(Type currType) + { + return currType.GetMethods().Single(x => + { + if (x.Name != "Index") return false; + if (x.ReturnParameter == null || x.ReturnParameter.ParameterType != typeof (ActionResult)) return false; + var p = x.GetParameters(); + if (p.Length != 1) return false; + if (p[0].ParameterType != typeof (RenderModel)) return false; + return true; + }); + } + + [Test] + public void Matches_Default_Index() + { + var attr = new RenderIndexActionSelectorAttribute(); + var req = new RequestContext(); + var appCtx = new ApplicationContext( + CacheHelper.CreateDisabledCacheHelper(), + new ProfilingLogger(Mock.Of(), Mock.Of())); + var umbCtx = UmbracoContext.EnsureContext( + Mock.Of(), + appCtx, + new Mock(null, null).Object, + Mock.Of(), + Enumerable.Empty(), + true); + var ctrl = new MatchesDefaultIndexController(umbCtx); + var controllerCtx = new ControllerContext(req, ctrl); + var result = attr.IsValidForRequest(controllerCtx, + GetRenderMvcControllerIndexMethodFromCurrentType(ctrl.GetType())); + + Assert.IsTrue(result); + } + + [Test] + public void Matches_Overriden_Index() + { + var attr = new RenderIndexActionSelectorAttribute(); + var req = new RequestContext(); + var appCtx = new ApplicationContext( + CacheHelper.CreateDisabledCacheHelper(), + new ProfilingLogger(Mock.Of(), Mock.Of())); + var umbCtx = UmbracoContext.EnsureContext( + Mock.Of(), + appCtx, + new Mock(null, null).Object, + Mock.Of(), + Enumerable.Empty(), + true); + var ctrl = new MatchesOverriddenIndexController(umbCtx); + var controllerCtx = new ControllerContext(req, ctrl); + var result = attr.IsValidForRequest(controllerCtx, + GetRenderMvcControllerIndexMethodFromCurrentType(ctrl.GetType())); + + Assert.IsTrue(result); + } + + [Test] + public void Matches_Custom_Index() + { + var attr = new RenderIndexActionSelectorAttribute(); + var req = new RequestContext(); + var appCtx = new ApplicationContext( + CacheHelper.CreateDisabledCacheHelper(), + new ProfilingLogger(Mock.Of(), Mock.Of())); + var umbCtx = UmbracoContext.EnsureContext( + Mock.Of(), + appCtx, + new Mock(null, null).Object, + Mock.Of(), + Enumerable.Empty(), + true); + var ctrl = new MatchesCustomIndexController(umbCtx); + var controllerCtx = new ControllerContext(req, ctrl); + var result = attr.IsValidForRequest(controllerCtx, + GetRenderMvcControllerIndexMethodFromCurrentType(ctrl.GetType())); + + Assert.IsFalse(result); + } + + [Test] + public void Matches_Async_Index_Same_Signature() + { + var attr = new RenderIndexActionSelectorAttribute(); + var req = new RequestContext(); + var appCtx = new ApplicationContext( + CacheHelper.CreateDisabledCacheHelper(), + new ProfilingLogger(Mock.Of(), Mock.Of())); + var umbCtx = UmbracoContext.EnsureContext( + Mock.Of(), + appCtx, + new Mock(null, null).Object, + Mock.Of(), + Enumerable.Empty(), + true); + var ctrl = new MatchesAsyncIndexController(umbCtx); + var controllerCtx = new ControllerContext(req, ctrl); + var result = attr.IsValidForRequest(controllerCtx, + GetRenderMvcControllerIndexMethodFromCurrentType(ctrl.GetType())); + + Assert.IsFalse(result); + } + + public class MatchesDefaultIndexController : RenderMvcController + { + public MatchesDefaultIndexController(UmbracoContext umbracoContext) : base(umbracoContext) + { + } + } + + public class MatchesOverriddenIndexController : RenderMvcController + { + public MatchesOverriddenIndexController(UmbracoContext umbracoContext) : base(umbracoContext) + { + } + + public override ActionResult Index(RenderModel model) + { + return base.Index(model); + } + } + + public class MatchesCustomIndexController : RenderMvcController + { + public MatchesCustomIndexController(UmbracoContext umbracoContext) : base(umbracoContext) + { + } + + public ActionResult Index(RenderModel model, int page) + { + return base.Index(model); + } + } + + public class MatchesAsyncIndexController : RenderMvcController + { + public MatchesAsyncIndexController(UmbracoContext umbracoContext) : base(umbracoContext) + { + } + + public new async Task Index(RenderModel model) + { + return await Task.FromResult(base.Index(model)); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs b/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs index c13818a03c..4c06d30796 100644 --- a/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs +++ b/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using System.Text; using System.Web.Mvc; using NUnit.Framework; diff --git a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs index 1da8237582..5aa2649caf 100644 --- a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs +++ b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs @@ -305,7 +305,7 @@ namespace Umbraco.Tests.Persistence DatabaseSchemaHelper.CreateTable(); DatabaseSchemaHelper.CreateTable(); DatabaseSchemaHelper.CreateTable(); - DatabaseSchemaHelper.CreateTable(); + DatabaseSchemaHelper.CreateTable(); //transaction.Complete(); } diff --git a/src/Umbraco.Tests/Persistence/Mappers/PropertyGroupMapperTest.cs b/src/Umbraco.Tests/Persistence/Mappers/PropertyGroupMapperTest.cs index a8101cad79..490cc8db4a 100644 --- a/src/Umbraco.Tests/Persistence/Mappers/PropertyGroupMapperTest.cs +++ b/src/Umbraco.Tests/Persistence/Mappers/PropertyGroupMapperTest.cs @@ -20,19 +20,6 @@ namespace Umbraco.Tests.Persistence.Mappers Assert.That(column, Is.EqualTo("[cmsPropertyTypeGroup].[id]")); } - [Test] - public void Can_Map_ParentId_Property() - { - // Arrange - SqlSyntaxContext.SqlSyntaxProvider = new SqlCeSyntaxProvider(); - - // Act - string column = new PropertyGroupMapper().Map("ParentId"); - - // Assert - Assert.That(column, Is.EqualTo("[cmsPropertyTypeGroup].[parentGroupId]")); - } - [Test] public void Can_Map_SortOrder_Property() { diff --git a/src/Umbraco.Tests/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs b/src/Umbraco.Tests/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs index e25b95383e..1edda026fc 100644 --- a/src/Umbraco.Tests/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs +++ b/src/Umbraco.Tests/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs @@ -27,13 +27,13 @@ namespace Umbraco.Tests.Persistence.Querying var sql = new Sql(); sql.Select("*") - .From() + .From() .RightJoin() - .On(left => left.NodeId, right => right.ContentTypeNodeId) + .On(left => left.NodeId, right => right.ContentTypeNodeId) .InnerJoin() .On(left => left.NodeId, right => right.NodeId) .Where(x => x.NodeObjectType == NodeObjectType) - .Where(x => x.IsDefault == true); + .Where(x => x.IsDefault == true); Assert.That(sql.SQL, Is.EqualTo(expected.SQL)); @@ -64,13 +64,13 @@ namespace Umbraco.Tests.Persistence.Querying var sql = new Sql(); sql.Select("*") - .From() + .From() .RightJoin() - .On(left => left.NodeId, right => right.ContentTypeNodeId) + .On(left => left.NodeId, right => right.ContentTypeNodeId) .InnerJoin() .On(left => left.NodeId, right => right.NodeId) .Where(x => x.NodeObjectType == NodeObjectType) - .Where(x => x.IsDefault) + .Where(x => x.IsDefault) .Where(x => x.NodeId == 1050); Assert.That(sql.SQL, Is.EqualTo(expected.SQL)); diff --git a/src/Umbraco.Tests/Persistence/Querying/ContentTypeSqlMappingTests.cs b/src/Umbraco.Tests/Persistence/Querying/ContentTypeSqlMappingTests.cs index f63eea7c88..916b6d3333 100644 --- a/src/Umbraco.Tests/Persistence/Querying/ContentTypeSqlMappingTests.cs +++ b/src/Umbraco.Tests/Persistence/Querying/ContentTypeSqlMappingTests.cs @@ -43,8 +43,8 @@ namespace Umbraco.Tests.Persistence.Querying DatabaseContext.Database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 88889, NodeId = 99999, Alias = "TestContentType3", Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true }); DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsContentType")))); - DatabaseContext.Database.Insert(new DocumentTypeDto { ContentTypeNodeId = 99997, IsDefault = true, TemplateNodeId = 55555 }); - DatabaseContext.Database.Insert(new DocumentTypeDto { ContentTypeNodeId = 99997, IsDefault = false, TemplateNodeId = 55554 }); + DatabaseContext.Database.Insert(new ContentTypeTemplateDto { ContentTypeNodeId = 99997, IsDefault = true, TemplateNodeId = 55555 }); + DatabaseContext.Database.Insert(new ContentTypeTemplateDto { ContentTypeNodeId = 99997, IsDefault = false, TemplateNodeId = 55554 }); DatabaseContext.Database.Insert(new ContentTypeAllowedContentTypeDto { AllowedId = 99998, Id = 99997, SortOrder = 1 }); DatabaseContext.Database.Insert(new ContentTypeAllowedContentTypeDto { AllowedId = 99999, Id = 99997, SortOrder = 2}); diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index d5870810f8..35a25a0084 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -56,6 +56,17 @@ namespace Umbraco.Tests.Persistence.Repositories return contentTypeRepository; } + private MediaTypeRepository CreateMediaTypeRepository(IDatabaseUnitOfWork unitOfWork) + { + var contentTypeRepository = new MediaTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); + return contentTypeRepository; + } + + private EntityContainerRepository CreateContainerRepository(IDatabaseUnitOfWork unitOfWork) + { + return new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); + } + //TODO Add test to verify SetDefaultTemplates updates both AllowedTemplates and DefaultTemplate(id). @@ -100,12 +111,15 @@ namespace Umbraco.Tests.Persistence.Repositories { var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); + using (var containerRepository = CreateContainerRepository(unitOfWork)) using (var repository = CreateRepository(unitOfWork)) { - var container = repository.CreateContainer(-1, "blah", 0); + var container1 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) { Name = "blah1" }; + containerRepository.AddOrUpdate(container1); unitOfWork.Commit(); - var container2 = repository.CreateContainer(container.Id, "blah2", 0); + var container2 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) { Name = "blah2", ParentId = container1.Id }; + containerRepository.AddOrUpdate(container2); unitOfWork.Commit(); var contentType = (IContentType) MockedContentTypes.CreateBasicContentType("asdfasdf"); @@ -122,7 +136,7 @@ namespace Umbraco.Tests.Persistence.Repositories repository.AddOrUpdate(contentType2); unitOfWork.Commit(); - var result = repository.Move(contentType, container.Id).ToArray(); + var result = repository.Move(contentType, container1).ToArray(); unitOfWork.Commit(); Assert.AreEqual(2, result.Count()); @@ -131,7 +145,7 @@ namespace Umbraco.Tests.Persistence.Repositories contentType = repository.Get(contentType.Id); contentType2 = repository.Get(contentType2.Id); - Assert.AreEqual(container.Id, contentType.ParentId); + Assert.AreEqual(container1.Id, contentType.ParentId); Assert.AreNotEqual(result.Single(x => x.Entity.Id == contentType.Id).OriginalPath, contentType.Path); Assert.AreNotEqual(result.Single(x => x.Entity.Id == contentType2.Id).OriginalPath, contentType2.Path); } @@ -144,16 +158,16 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); EntityContainer container; - using (var repository = CreateRepository(unitOfWork)) + using (var containerRepository = CreateContainerRepository(unitOfWork)) { - container = repository.CreateContainer(-1, "blah", 0); + container = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) { Name = "blah" }; + containerRepository.AddOrUpdate(container); unitOfWork.Commit(); Assert.That(container.Id, Is.GreaterThan(0)); } - using (var entityRepo = new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, - new Guid(Constants.ObjectTypes.DocumentTypeContainer), new Guid(Constants.ObjectTypes.DocumentType))) + using (var containerRepository = CreateContainerRepository(unitOfWork)) { - var found = entityRepo.Get(container.Id); + var found = containerRepository.Get(container.Id); Assert.IsNotNull(found); } } @@ -164,23 +178,22 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); EntityContainer container; - using (var repository = CreateRepository(unitOfWork)) + using (var containerRepository = CreateContainerRepository(unitOfWork)) { - container = repository.CreateContainer(-1, "blah", 0); + container = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) { Name = "blah" }; + containerRepository.AddOrUpdate(container); unitOfWork.Commit(); } - using (var repository = CreateRepository(unitOfWork)) + using (var containerRepository = CreateContainerRepository(unitOfWork)) { // Act - repository.DeleteContainer(container.Id); + containerRepository.Delete(container); unitOfWork.Commit(); - - using (var entityRepo = new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, - new Guid(Constants.ObjectTypes.DocumentTypeContainer), new Guid(Constants.ObjectTypes.DocumentType))) - { - var found = entityRepo.Get(container.Id); - Assert.IsNull(found); - } + } + using (var containerRepository = CreateContainerRepository(unitOfWork)) + { + var found = containerRepository.Get(container.Id); + Assert.IsNull(found); } } @@ -189,10 +202,11 @@ namespace Umbraco.Tests.Persistence.Repositories { var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); - EntityContainer container; + using (var containerRepository = CreateContainerRepository(unitOfWork)) using (var repository = CreateRepository(unitOfWork)) { - container = repository.CreateContainer(-1, "blah", 0); + var container = new EntityContainer(Constants.ObjectTypes.MediaTypeGuid) { Name = "blah" }; + containerRepository.AddOrUpdate(container); unitOfWork.Commit(); var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test", propertyGroupName: "testGroup"); @@ -210,29 +224,28 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); EntityContainer container; - IContentType contentType; - using (var repository = CreateRepository(unitOfWork)) + IMediaType contentType; + using (var containerRepository = CreateContainerRepository(unitOfWork)) + using (var repository = CreateMediaTypeRepository(unitOfWork)) { - container = repository.CreateContainer(-1, "blah", 0); + container = new EntityContainer(Constants.ObjectTypes.MediaTypeGuid) { Name = "blah" }; + containerRepository.AddOrUpdate(container); unitOfWork.Commit(); - contentType = MockedContentTypes.CreateSimpleContentType("test", "Test", propertyGroupName: "testGroup"); + contentType = MockedContentTypes.CreateSimpleMediaType("test", "Test", propertyGroupName: "testGroup"); contentType.ParentId = container.Id; repository.AddOrUpdate(contentType); unitOfWork.Commit(); } - using (var repository = CreateRepository(unitOfWork)) + using (var containerRepository = CreateContainerRepository(unitOfWork)) + using (var repository = CreateMediaTypeRepository(unitOfWork)) { // Act - repository.DeleteContainer(container.Id); + containerRepository.Delete(container); unitOfWork.Commit(); - using (var entityRepo = new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, - new Guid(Constants.ObjectTypes.DocumentTypeContainer), new Guid(Constants.ObjectTypes.DocumentType))) - { - var found = entityRepo.Get(container.Id); - Assert.IsNull(found); - } + var found = containerRepository.Get(container.Id); + Assert.IsNull(found); contentType = repository.Get(contentType.Id); Assert.IsNotNull(contentType); diff --git a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs index 850a25fd32..aa0b8a8ffe 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs @@ -42,6 +42,11 @@ namespace Umbraco.Tests.Persistence.Repositories return dataTypeDefinitionRepository; } + private EntityContainerRepository CreateContainerRepository(IDatabaseUnitOfWork unitOfWork) + { + return new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); + } + [TestCase("UmbracoPreVal87-21,3,48", 3, true)] [TestCase("UmbracoPreVal87-21,33,48", 3, false)] [TestCase("UmbracoPreVal87-21,33,48", 33, true)] @@ -64,12 +69,15 @@ namespace Umbraco.Tests.Persistence.Repositories { var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); + using (var containerRepository = CreateContainerRepository(unitOfWork)) using (var repository = CreateRepository(unitOfWork)) { - var container = repository.CreateContainer(-1, "blah", 0); + var container1 = new EntityContainer(Constants.ObjectTypes.DataTypeGuid) { Name = "blah1" }; + containerRepository.AddOrUpdate(container1); unitOfWork.Commit(); - var container2 = repository.CreateContainer(container.Id, "blah2", 0); + var container2 = new EntityContainer(Constants.ObjectTypes.DataTypeGuid) { Name = "blah2", ParentId = container1.Id }; + containerRepository.AddOrUpdate(container2); unitOfWork.Commit(); var dataType = (IDataTypeDefinition) new DataTypeDefinition(container2.Id, Constants.PropertyEditors.RadioButtonListAlias) @@ -87,7 +95,7 @@ namespace Umbraco.Tests.Persistence.Repositories repository.AddOrUpdate(dataType2); unitOfWork.Commit(); - var result = repository.Move(dataType, container.Id).ToArray(); + var result = repository.Move(dataType, container1).ToArray(); unitOfWork.Commit(); Assert.AreEqual(2, result.Count()); @@ -96,7 +104,7 @@ namespace Umbraco.Tests.Persistence.Repositories dataType = repository.Get(dataType.Id); dataType2 = repository.Get(dataType2.Id); - Assert.AreEqual(container.Id, dataType.ParentId); + Assert.AreEqual(container1.Id, dataType.ParentId); Assert.AreNotEqual(result.Single(x => x.Entity.Id == dataType.Id).OriginalPath, dataType.Path); Assert.AreNotEqual(result.Single(x => x.Entity.Id == dataType2.Id).OriginalPath, dataType2.Path); } @@ -109,16 +117,16 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); EntityContainer container; - using (var repository = CreateRepository(unitOfWork)) + using (var containerRepository = CreateContainerRepository(unitOfWork)) { - container = repository.CreateContainer(-1, "blah", 0); + container = new EntityContainer(Constants.ObjectTypes.DataTypeGuid) { Name = "blah" }; + containerRepository.AddOrUpdate(container); unitOfWork.Commit(); Assert.That(container.Id, Is.GreaterThan(0)); } - using (var entityRepo = new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, - new Guid(Constants.ObjectTypes.DataTypeContainer), new Guid(Constants.ObjectTypes.DataType))) + using (var containerRepository = CreateContainerRepository(unitOfWork)) { - var found = entityRepo.Get(container.Id); + var found = containerRepository.Get(container.Id); Assert.IsNotNull(found); } } @@ -129,23 +137,22 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); EntityContainer container; - using (var repository = CreateRepository(unitOfWork)) + using (var containerRepository = CreateContainerRepository(unitOfWork)) { - container = repository.CreateContainer(-1, "blah", 0); + container = new EntityContainer(Constants.ObjectTypes.DataTypeGuid) { Name = "blah" }; + containerRepository.AddOrUpdate(container); unitOfWork.Commit(); } - using (var repository = CreateRepository(unitOfWork)) + using (var containerRepository = CreateContainerRepository(unitOfWork)) { // Act - repository.DeleteContainer(container.Id); + containerRepository.Delete(container); unitOfWork.Commit(); - - using (var entityRepo = new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, - new Guid(Constants.ObjectTypes.DataTypeContainer), new Guid(Constants.ObjectTypes.DataType))) - { - var found = entityRepo.Get(container.Id); - Assert.IsNull(found); - } + } + using (var containerRepository = CreateContainerRepository(unitOfWork)) + { + var found = containerRepository.Get(container.Id); + Assert.IsNull(found); } } @@ -154,10 +161,11 @@ namespace Umbraco.Tests.Persistence.Repositories { var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); - EntityContainer container; + using (var containerRepository = CreateContainerRepository(unitOfWork)) using (var repository = CreateRepository(unitOfWork)) { - container = repository.CreateContainer(-1, "blah", 0); + var container = new EntityContainer(Constants.ObjectTypes.DataTypeGuid) { Name = "blah" }; + containerRepository.AddOrUpdate(container); unitOfWork.Commit(); var dataTypeDefinition = new DataTypeDefinition(container.Id, Constants.PropertyEditors.RadioButtonListAlias) { Name = "test" }; @@ -175,27 +183,26 @@ namespace Umbraco.Tests.Persistence.Repositories var unitOfWork = provider.GetUnitOfWork(); EntityContainer container; IDataTypeDefinition dataType; + using (var containerRepository = CreateContainerRepository(unitOfWork)) using (var repository = CreateRepository(unitOfWork)) { - container = repository.CreateContainer(-1, "blah", 0); + container = new EntityContainer(Constants.ObjectTypes.DataTypeGuid) { Name = "blah" }; + containerRepository.AddOrUpdate(container); unitOfWork.Commit(); dataType = new DataTypeDefinition(container.Id, Constants.PropertyEditors.RadioButtonListAlias) { Name = "test" }; repository.AddOrUpdate(dataType); unitOfWork.Commit(); } + using (var containerRepository = CreateContainerRepository(unitOfWork)) using (var repository = CreateRepository(unitOfWork)) { // Act - repository.DeleteContainer(container.Id); + containerRepository.Delete(container); unitOfWork.Commit(); - using (var entityRepo = new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, - new Guid(Constants.ObjectTypes.DataTypeContainer), new Guid(Constants.ObjectTypes.DataType))) - { - var found = entityRepo.Get(container.Id); - Assert.IsNull(found); - } + var found = containerRepository.Get(container.Id); + Assert.IsNull(found); dataType = repository.Get(dataType.Id); Assert.IsNotNull(dataType); diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs index 82a6578ebb..55d4c92722 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs @@ -31,17 +31,25 @@ namespace Umbraco.Tests.Persistence.Repositories return new MediaTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); } + private EntityContainerRepository CreateContainerRepository(IDatabaseUnitOfWork unitOfWork) + { + return new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); + } + [Test] public void Can_Move() { var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); + using (var containerRepository = CreateContainerRepository(unitOfWork)) using (var repository = CreateRepository(unitOfWork)) { - var container = repository.CreateContainer(-1, "blah", 0); + var container1 = new EntityContainer(Constants.ObjectTypes.MediaTypeGuid) { Name = "blah1" }; + containerRepository.AddOrUpdate(container1); unitOfWork.Commit(); - var container2 = repository.CreateContainer(container.Id, "blah2", 0); + var container2 = new EntityContainer(Constants.ObjectTypes.MediaTypeGuid) { Name = "blah2", ParentId = container1.Id }; + containerRepository.AddOrUpdate(container2); unitOfWork.Commit(); var contentType = (IMediaType)MockedContentTypes.CreateVideoMediaType(); @@ -58,7 +66,7 @@ namespace Umbraco.Tests.Persistence.Repositories repository.AddOrUpdate(contentType2); unitOfWork.Commit(); - var result = repository.Move(contentType, container.Id).ToArray(); + var result = repository.Move(contentType, container1).ToArray(); unitOfWork.Commit(); Assert.AreEqual(2, result.Count()); @@ -67,7 +75,7 @@ namespace Umbraco.Tests.Persistence.Repositories contentType = repository.Get(contentType.Id); contentType2 = repository.Get(contentType2.Id); - Assert.AreEqual(container.Id, contentType.ParentId); + Assert.AreEqual(container1.Id, contentType.ParentId); Assert.AreNotEqual(result.Single(x => x.Entity.Id == contentType.Id).OriginalPath, contentType.Path); Assert.AreNotEqual(result.Single(x => x.Entity.Id == contentType2.Id).OriginalPath, contentType2.Path); } @@ -80,16 +88,16 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); EntityContainer container; - using (var repository = CreateRepository(unitOfWork)) + using (var containerRepository = CreateContainerRepository(unitOfWork)) { - container = repository.CreateContainer(-1, "blah", 0); + container = new EntityContainer(Constants.ObjectTypes.DataTypeGuid) { Name = "blah" }; + containerRepository.AddOrUpdate(container); unitOfWork.Commit(); Assert.That(container.Id, Is.GreaterThan(0)); } - using (var entityRepo = new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, - new Guid(Constants.ObjectTypes.MediaTypeContainer), new Guid(Constants.ObjectTypes.MediaType))) + using (var containerRepository = CreateContainerRepository(unitOfWork)) { - var found = entityRepo.Get(container.Id); + var found = containerRepository.Get(container.Id); Assert.IsNotNull(found); } } @@ -100,23 +108,22 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); EntityContainer container; - using (var repository = CreateRepository(unitOfWork)) + using (var containerRepository = CreateContainerRepository(unitOfWork)) { - container = repository.CreateContainer(-1, "blah", 0); + container = new EntityContainer(Constants.ObjectTypes.DataTypeGuid) { Name = "blah" }; + containerRepository.AddOrUpdate(container); unitOfWork.Commit(); } - using (var repository = CreateRepository(unitOfWork)) + using (var containerRepository = CreateContainerRepository(unitOfWork)) { // Act - repository.DeleteContainer(container.Id); + containerRepository.Delete(container); unitOfWork.Commit(); - - using (var entityRepo = new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, - new Guid(Constants.ObjectTypes.MediaTypeContainer), new Guid(Constants.ObjectTypes.MediaType))) - { - var found = entityRepo.Get(container.Id); - Assert.IsNull(found); - } + } + using (var containerRepository = CreateContainerRepository(unitOfWork)) + { + var found = containerRepository.Get(container.Id); + Assert.IsNull(found); } } @@ -125,10 +132,11 @@ namespace Umbraco.Tests.Persistence.Repositories { var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); - EntityContainer container; + using (var containerRepository = CreateContainerRepository(unitOfWork)) using (var repository = CreateRepository(unitOfWork)) { - container = repository.CreateContainer(-1, "blah", 0); + var container = new EntityContainer(Constants.ObjectTypes.DataTypeGuid) { Name = "blah" }; + containerRepository.AddOrUpdate(container); unitOfWork.Commit(); var contentType = MockedContentTypes.CreateVideoMediaType(); @@ -147,9 +155,11 @@ namespace Umbraco.Tests.Persistence.Repositories var unitOfWork = provider.GetUnitOfWork(); EntityContainer container; IMediaType contentType; + using (var containerRepository = CreateContainerRepository(unitOfWork)) using (var repository = CreateRepository(unitOfWork)) { - container = repository.CreateContainer(-1, "blah", 0); + container = new EntityContainer(Constants.ObjectTypes.MediaTypeGuid) { Name = "blah" }; + containerRepository.AddOrUpdate(container); unitOfWork.Commit(); contentType = MockedContentTypes.CreateVideoMediaType(); @@ -157,18 +167,15 @@ namespace Umbraco.Tests.Persistence.Repositories repository.AddOrUpdate(contentType); unitOfWork.Commit(); } + using (var containerRepository = CreateContainerRepository(unitOfWork)) using (var repository = CreateRepository(unitOfWork)) { // Act - repository.DeleteContainer(container.Id); + containerRepository.Delete(container); unitOfWork.Commit(); - using (var entityRepo = new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, - new Guid(Constants.ObjectTypes.MediaTypeContainer), new Guid(Constants.ObjectTypes.MediaType))) - { - var found = entityRepo.Get(container.Id); - Assert.IsNull(found); - } + var found = containerRepository.Get(container.Id); + Assert.IsNull(found); contentType = repository.Get(contentType.Id); Assert.IsNotNull(contentType); diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index 2ab6a3fe06..742205904f 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -986,7 +986,9 @@ namespace Umbraco.Tests.Services Assert.That(contentType, Is.Not.Null); var compositionPropertyGroups = contentType.CompositionPropertyGroups; - Assert.That(compositionPropertyGroups.Count(x => x.Name.Equals("Content_")), Is.EqualTo(0)); + + // now it is still 1, because we don't propagate renames anymore + Assert.That(compositionPropertyGroups.Count(x => x.Name.Equals("Content_")), Is.EqualTo(1)); var propertyTypeCount = contentType.PropertyTypes.Count(); var compPropertyTypeCount = contentType.CompositionPropertyTypes.Count(); @@ -1059,7 +1061,9 @@ namespace Umbraco.Tests.Services var advancedPageReloaded = service.GetContentType("advancedPage"); var contentUnderscoreTabExists = advancedPageReloaded.CompositionPropertyGroups.Any(x => x.Name.Equals("Content_")); - Assert.That(contentUnderscoreTabExists, Is.False); + + // now is true, because we don't propagate renames anymore + Assert.That(contentUnderscoreTabExists, Is.True); var numberOfContentTabs = advancedPageReloaded.CompositionPropertyGroups.Count(x => x.Name.Equals("Content")); Assert.That(numberOfContentTabs, Is.EqualTo(4)); @@ -1162,7 +1166,6 @@ namespace Umbraco.Tests.Services var contentType = service.GetContentType("contentPage"); var propertyGroup = contentType.PropertyGroups["Content"]; - Assert.That(propertyGroup.ParentId.HasValue, Is.False); } [Test] @@ -1259,7 +1262,6 @@ namespace Umbraco.Tests.Services var contentType = service.GetContentType("contentPage"); var propertyGroup = contentType.PropertyGroups["Content"]; - Assert.That(propertyGroup.ParentId.HasValue, Is.False); var numberOfContentTabs = contentType.CompositionPropertyGroups.Count(x => x.Name.Equals("Content")); Assert.That(numberOfContentTabs, Is.EqualTo(3)); @@ -1278,7 +1280,6 @@ namespace Umbraco.Tests.Services var propertyGroupReloaded = contentPageReloaded.PropertyGroups["Content"]; var hasDescriptionPropertyType = propertyGroupReloaded.PropertyTypes.Contains("description"); Assert.That(hasDescriptionPropertyType, Is.True); - Assert.That(propertyGroupReloaded.ParentId.HasValue, Is.False); var descriptionPropertyTypeReloaded = propertyGroupReloaded.PropertyTypes["description"]; Assert.That(descriptionPropertyTypeReloaded.PropertyGroupId.IsValueCreated, Is.False); diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index b97f069b12..3eb0df882a 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -185,24 +185,41 @@ namespace Umbraco.Tests.TestHelpers.Entities var pg = new PropertyGroup(contentCollection) {Name = propertyGroupName, SortOrder = 1}; contentType.PropertyGroups.Add(pg); - if (parent != null) - { - var foundPg = parent.PropertyGroups.FirstOrDefault(x => x.Name == propertyGroupName); - if (foundPg != null) - { - //this exists on the parent, so set the parent id - pg.SetLazyParentId(new Lazy(() => foundPg.Id)); - } - } - //ensure that nothing is marked as dirty contentType.ResetDirtyProperties(false); contentType.SetDefaultTemplate(new Template("Textpage", "textpage")); return contentType; - } + } + public static MediaType CreateSimpleMediaType(string alias, string name, IMediaType parent = null, bool randomizeAliases = false, string propertyGroupName = "Content") + { + var contentType = parent == null ? new MediaType(-1) : new MediaType(parent, alias); + + contentType.Alias = alias; + contentType.Name = name; + contentType.Description = "ContentType used for simple text pages"; + contentType.Icon = ".sprTreeDoc3"; + contentType.Thumbnail = "doc2.png"; + contentType.SortOrder = 1; + contentType.CreatorId = 0; + contentType.Trashed = false; + + var contentCollection = new PropertyTypeCollection(); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = RandomAlias("title", randomizeAliases), Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.TinyMCEAlias, DataTypeDatabaseType.Ntext) { Alias = RandomAlias("bodyText", randomizeAliases), Name = "Body Text", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = RandomAlias("author", randomizeAliases), Name = "Author", Description = "Name of the author", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88 }); + + var pg = new PropertyGroup(contentCollection) { Name = propertyGroupName, SortOrder = 1 }; + contentType.PropertyGroups.Add(pg); + + //ensure that nothing is marked as dirty + contentType.ResetDirtyProperties(false); + + return contentType; + } + public static ContentType CreateSimpleContentType(string alias, string name, bool mandatory) { var contentType = new ContentType(-1) diff --git a/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs b/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs index 31bdb616b0..7f38bf61a1 100644 --- a/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs @@ -8,6 +8,7 @@ using System.Web; using System.Web.Routing; using Moq; using Umbraco.Core; +using Umbraco.Core.Configuration; namespace Umbraco.Tests.TestHelpers { @@ -60,7 +61,7 @@ namespace Umbraco.Tests.TestHelpers //Cookie collection var cookieCollection = new HttpCookieCollection(); - cookieCollection.Add(new HttpCookie(Constants.Web.AuthCookieName, "FBA996E7-D6BE-489B-B199-2B0F3D2DD826")); + cookieCollection.Add(new HttpCookie("UMB_UCONTEXT", "FBA996E7-D6BE-489B-B199-2B0F3D2DD826")); //Request var requestMock = new Mock(); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index ef142d0d52..f3ed7d225b 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -181,6 +181,7 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umblayoutselector.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umblayoutselector.directive.js index ea3eedd968..48291bfa87 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umblayoutselector.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umblayoutselector.directive.js @@ -46,32 +46,19 @@ function setActiveLayout(layouts) { for (var i = 0; layouts.length > i; i++) { - var layout = layouts[i]; - - if(layout.name === scope.activeLayout.name && layout.path === scope.activeLayout.path) { + if(layout.path === scope.activeLayout.path) { layout.active = true; } - } } scope.pickLayout = function(selectedLayout) { - - for (var i = 0; scope.layouts.length > i; i++) { - - var layout = scope.layouts[i]; - - layout.active = false; - } - - selectedLayout.active = true; - - scope.activeLayout = selectedLayout; - - scope.layoutDropDownIsOpen = false; - + if(scope.onLayoutSelect) { + scope.onLayoutSelect(selectedLayout); + scope.layoutDropDownIsOpen = false; + } }; scope.toggleLayoutDropdown = function() { @@ -92,7 +79,8 @@ templateUrl: 'views/components/umb-layout-selector.html', scope: { layouts: '=', - activeLayout: '=' + activeLayout: '=', + onLayoutSelect: "=" }, link: link }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js index fa639b6225..f5108070b1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js @@ -25,10 +25,7 @@ } function setItemData(item) { - item.isFolder = !mediaHelper.hasFilePropertyType(item); - item.hidden = item.isFolder; - if(!item.isFolder){ item.thumbnail = mediaHelper.resolveFile(item, true); item.image = mediaHelper.resolveFile(item, false); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfiledropzone.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfiledropzone.directive.js index 724940bd9d..d33a8edb48 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfiledropzone.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfiledropzone.directive.js @@ -144,33 +144,60 @@ angular.module("umbraco.directives") }).success(function (data, status, headers, config) { - // set done status on file - file.uploadStatus = "done"; + if(data.notifications && data.notifications.length > 0) { - // set date/time for when done - used for sorting - file.doneDate = new Date(); + // set error status on file + file.uploadStatus = "error"; + + // Throw message back to user with the cause of the error + file.serverErrorMessage = data.notifications[0].message; + + // Put the file in the rejected pool + scope.rejected.push(file); + + } else { + + // set done status on file + file.uploadStatus = "done"; + + // set date/time for when done - used for sorting + file.doneDate = new Date(); + + // Put the file in the done pool + scope.done.push(file); + + } - scope.done.push(file); scope.currentFile = undefined; //after processing, test if everthing is done _processQueueItem(); }).error( function (evt, status, headers, config) { + + // set status done file.uploadStatus = "error"; //if the service returns a detailed error - if(evt.InnerException){ - file.errorMessage = evt.InnerException.ExceptionMessage; + if (evt.InnerException) { + file.serverErrorMessage = evt.InnerException.ExceptionMessage; - //Check if its the common "too large file" exception - if(evt.InnerException.StackTrace && evt.InnerException.StackTrace.indexOf("ValidateRequestEntityLength") > 0){ - file.errorMessage = "File too large to upload"; - } + //Check if its the common "too large file" exception + if (evt.InnerException.StackTrace && evt.InnerException.StackTrace.indexOf("ValidateRequestEntityLength") > 0) { + file.serverErrorMessage = "File too large to upload"; + } + + } else if (evt.Message) { + file.serverErrorMessage = evt.Message; + } + + // If file not found, server will return a 404 and display this message + if(status === 404 ) { + file.serverErrorMessage = "File not found"; } //after processing, test if everthing is done - scope.done.push(file); + scope.rejected.push(file); scope.currentFile = undefined; _processQueueItem(); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/macro.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/macro.resource.js index 9ba64838c9..06b5f9496c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/macro.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/macro.resource.js @@ -36,7 +36,7 @@ function macroResource($q, $http, umbRequestHelper) { * @methodOf umbraco.resources.macroResource * * @description - * Gets the result of a macro as html to display in the rich text editor + * Gets the result of a macro as html to display in the rich text editor or in the Grid * * @param {int} macroId The macro id to get parameters for * @param {int} pageId The current page id @@ -45,39 +45,17 @@ function macroResource($q, $http, umbRequestHelper) { */ getMacroResultAsHtmlForEditor: function (macroAlias, pageId, macroParamDictionary) { - //need to format the query string for the custom dictionary - var query = "macroAlias=" + macroAlias + "&pageId=" + pageId; - if (macroParamDictionary) { - var counter = 0; - _.each(macroParamDictionary, function (val, key) { - //check for null - val = val ? val : ""; - //need to detect if the val is a string or an object - if (!angular.isString(val)) { - //if it's not a string we'll send it through the json serializer - var json = angular.toJson(val); - //then we need to url encode it so that it's safe - val = encodeURIComponent(json); - } - else { - //we still need to encode the string, it could contain line breaks, etc... - val = encodeURIComponent(val); - } - - query += "¯oParams[" + counter + "].key=" + key + "¯oParams[" + counter + "].value=" + val; - counter++; - }); - } - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "macroApiBaseUrl", - "GetMacroResultAsHtmlForEditor", - query)), - 'Failed to retrieve macro result for macro with alias ' + macroAlias); + $http.post( + umbRequestHelper.getApiUrl( + "macroApiBaseUrl", + "GetMacroResultAsHtmlForEditor"), { + macroAlias: macroAlias, + pageId: pageId, + macroParams: macroParamDictionary + }), + 'Failed to retrieve macro result for macro with alias ' + macroAlias); } - }; } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js index ea85e709e9..e7bfe59d42 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js @@ -1,10 +1,104 @@ (function() { 'use strict'; - function listViewHelper() { + function listViewHelper($cookieStore) { var firstSelectedIndex = 0; + function getLayout(nodeId, availableLayouts) { + + var storedLayouts = []; + + if ($cookieStore.get("umblistViewLayout")) { + storedLayouts = $cookieStore.get("umblistViewLayout"); + } + + if (storedLayouts && storedLayouts.length > 0) { + for (var i = 0; storedLayouts.length > i; i++) { + var layout = storedLayouts[i]; + if (layout.nodeId === nodeId) { + return setLayout(nodeId, layout, availableLayouts); + } + } + + } + + return getFirstAllowedLayout(availableLayouts); + + } + + function setLayout(nodeId, selectedLayout, availableLayouts) { + + var activeLayout = {}; + var layoutFound = false; + + for (var i = 0; availableLayouts.length > i; i++) { + var layout = availableLayouts[i]; + if (layout.path === selectedLayout.path) { + activeLayout = layout; + layout.active = true; + layoutFound = true; + } else { + layout.active = false; + } + } + + if(!layoutFound) { + activeLayout = getFirstAllowedLayout(availableLayouts); + } + + setLayoutCookie(nodeId, activeLayout); + + return activeLayout; + + } + + function setLayoutCookie(nodeId, selectedLayout) { + + var layoutFound = false; + var storedLayouts = []; + + if($cookieStore.get("umblistViewLayout")) { + storedLayouts = $cookieStore.get("umblistViewLayout"); + } + + if(storedLayouts.length > 0) { + for(var i = 0; storedLayouts.length > i; i++) { + var layout = storedLayouts[i]; + if(layout.nodeId === nodeId) { + layout.path = selectedLayout.path; + layoutFound = true; + } + } + } + + if(!layoutFound) { + var cookieObject = { + "nodeId": nodeId, + "path": selectedLayout.path + }; + storedLayouts.push(cookieObject); + } + + document.cookie="umblistViewLayout=" + JSON.stringify(storedLayouts); + + } + + function getFirstAllowedLayout(layouts) { + + var firstAllowedLayout = {}; + + for (var i = 0; layouts.length > i; i++) { + var layout = layouts[i]; + if (layout.selected === true) { + firstAllowedLayout = layout; + break; + } + } + + return firstAllowedLayout; + } + function selectHandler(selectedItem, selectedIndex, items, selection, $event) { var start = 0; @@ -57,7 +151,7 @@ isSelected = true; } } - if(!isSelected && !item.hidden) { + if(!isSelected) { selection.push({id: item.id}); item.selected = true; } @@ -169,6 +263,10 @@ var service = { + getLayout: getLayout, + getFirstAllowedLayout: getFirstAllowedLayout, + setLayout: setLayout, + setLayoutCookie: setLayoutCookie, selectHandler: selectHandler, selectItem: selectItem, deselectItem: deselectItem, diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less index 56e77342b0..f2a5082231 100644 --- a/src/Umbraco.Web.UI.Client/src/less/main.less +++ b/src/Umbraco.Web.UI.Client/src/less/main.less @@ -521,3 +521,58 @@ height:1px; .umb-loader-wrapper.-bottom { bottom: 0; } + +// Helpers + +.strong { + font-weight: bold; +} + +.inline { + display: inline; +} + + +// Input label styles +// @Simon: not sure where to put this part yet +// --- TODO Needs to be divided into the right .less directories + + +// Titles for input fields +.input-label--title { + font-weight: bold; + color: @black; + + margin-bottom: 3px; +} + + +// Used for input checkmark fields +.input-label--small { + display: inline; + + font-size: 12px; + font-weight: bold; + color: fade(@black, 70); + + &:hover { + color: @black; + } +} + +input[type=checkbox]:checked + .input-label--small { + color: @blue; +} + + +// Use this for headers in the panels +.panel-dialog--header { + border-bottom: 1px solid @gray; + + margin: 10px 0; + padding-bottom: 10px; + + font-size: @fontSizeLarge; + font-weight: bold; + line-height: 20px; +} diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index f3025074cb..36c2812cc7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -42,6 +42,23 @@ bottom: 90px; } +.umb-mediapicker-upload { + display: -ms-flexbox; + display: -webkit-box; + display: -webkit-flex; + display: flex; + + .form-search { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + } + + .upload-button { + margin-left: 16px; + } +} + .umb-panel.editor-breadcrumb .umb-panel-body, .umb-panel.editor-breadcrumb .umb-bottom-bar { bottom: 31px !important; } diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index acb9644a97..082e8d079d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -40,8 +40,16 @@ padding: 10px; } -.umb-contentpicker small a { +.umb-contentpicker small { + + &:not(:last-child) { + padding-right: 3px; + border-right: 1px solid @grayMed; + } + + a { color: @gray; + } } /* CODEMIRROR DATATYPE */ diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index ec73e62c0c..f002566eed 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -14,6 +14,7 @@ @grayDarker: #222; @grayDark: #343434; @gray: #555; +@grayMed: #999; @grayLight: #d9d9d9; @grayLighter: #f8f8f8; @white: #fff; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.html index cf9b5aba2d..540a19d64a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.html @@ -1,5 +1,7 @@ -
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js index ba42d6c076..309fa0957d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js @@ -12,6 +12,7 @@ angular.module("umbraco") $scope.cropSize = dialogOptions.cropSize; $scope.lastOpenedNode = $cookieStore.get("umbLastOpenedMediaNodeId"); $scope.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes); + $scope.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB"; $scope.model.selectedImages = []; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html index 1e624b808f..95e48cbd42 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html @@ -47,7 +47,8 @@ parent-id="{{currentFolder.id}}" files-uploaded="onUploadComplete" files-queued="onFilesQueue" - accept="{{acceptedFileTypes}}"> + accept="{{acceptedFileTypes}}" + max-file-size="{{maxFileSize}}"> - +
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-file-dropzone.html b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-file-dropzone.html index 04d6c0cffe..dc05556bab 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-file-dropzone.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-file-dropzone.html @@ -79,7 +79,11 @@ (Only allowed file types are: "{{ accept }}") - (Max file size is " {{ maxFileSize }} ") + (Max file size is "{{maxFileSize}}") + + + + ({{file.serverErrorMessage}})
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js index 9957b49ec3..e33b592e76 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js @@ -154,8 +154,39 @@ saveMethod: contentTypeResource.save, scope: $scope, content: vm.contentType, - //no-op for rebind callback... we don't really need to rebind for content types - rebindCallback: angular.noop + // we need to rebind... the IDs that have been created! + rebindCallback: function (origContentType, savedContentType) { + vm.contentType.id = savedContentType.id; + vm.contentType.groups.forEach(function(group) { + if (!group.name) return; + + var k = 0; + while (k < savedContentType.groups.length && savedContentType.groups[k].name != group.name) + k++; + if (k == savedContentType.groups.length) { + group.id = 0; + return; + } + + var savedGroup = savedContentType.groups[k]; + if (!group.id) group.id = savedGroup.id; + + group.properties.forEach(function (property) { + if (property.id || !property.alias) return; + + k = 0; + while (k < savedGroup.properties.length && savedGroup.properties[k].alias != property.alias) + k++; + if (k == savedGroup.properties.length) { + property.id = 0; + return; + } + + var savedProperty = savedGroup.properties[k]; + property.id = savedProperty.id; + }); + }); + } }).then(function (data) { //success syncTreeNode(vm.contentType, data.path); diff --git a/src/Umbraco.Web.UI.Client/src/views/members/create.html b/src/Umbraco.Web.UI.Client/src/views/member/create.html similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/members/create.html rename to src/Umbraco.Web.UI.Client/src/views/member/create.html diff --git a/src/Umbraco.Web.UI.Client/src/views/members/delete.html b/src/Umbraco.Web.UI.Client/src/views/member/delete.html similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/members/delete.html rename to src/Umbraco.Web.UI.Client/src/views/member/delete.html diff --git a/src/Umbraco.Web.UI.Client/src/views/members/edit.html b/src/Umbraco.Web.UI.Client/src/views/member/edit.html similarity index 94% rename from src/Umbraco.Web.UI.Client/src/views/members/edit.html rename to src/Umbraco.Web.UI.Client/src/views/member/edit.html index 3492275c08..80a9d39756 100644 --- a/src/Umbraco.Web.UI.Client/src/views/members/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/member/edit.html @@ -11,7 +11,6 @@ -
+ diff --git a/src/Umbraco.Web.UI.Client/src/views/members/member.create.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/member.create.controller.js similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/members/member.create.controller.js rename to src/Umbraco.Web.UI.Client/src/views/member/member.create.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/views/members/member.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/member.delete.controller.js similarity index 86% rename from src/Umbraco.Web.UI.Client/src/views/members/member.delete.controller.js rename to src/Umbraco.Web.UI.Client/src/views/member/member.delete.controller.js index f82fad5c9a..3b2730a976 100644 --- a/src/Umbraco.Web.UI.Client/src/views/members/member.delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/member/member.delete.controller.js @@ -20,7 +20,7 @@ function MemberDeleteController($scope, memberResource, treeService, navigationS //if the current edited item is the same one as we're deleting, we need to navigate elsewhere if (editorState.current && editorState.current.key == $scope.currentNode.id) { - $location.path("/member/members/list/" + ($routeParams.listName ? $routeParams.listName : 'all-members')); + $location.path("/member/member/list/" + ($routeParams.listName ? $routeParams.listName : 'all-members')); } navigationService.hideMenu(); diff --git a/src/Umbraco.Web.UI.Client/src/views/members/member.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js similarity index 89% rename from src/Umbraco.Web.UI.Client/src/views/members/member.edit.controller.js rename to src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js index 9e5db32983..215cd7777c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/members/member.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js @@ -10,16 +10,17 @@ function MemberEditController($scope, $routeParams, $location, $q, $window, appS //setup scope vars $scope.page = {}; - $scope.page.loading = false; + $scope.page.loading = true; $scope.page.menu = {}; $scope.page.menu.currentSection = appState.getSectionState("currentSection"); $scope.page.menu.currentNode = null; //the editors affiliated node $scope.page.nameLocked = false; $scope.page.listViewPath = null; $scope.page.saveButtonState = "init"; + $scope.busy = false; $scope.page.listViewPath = ($routeParams.page && $routeParams.listName) - ? "/member/members/list/" + $routeParams.listName + "?page=" + $routeParams.page + ? "/member/member/list/" + $routeParams.listName + "?page=" + $routeParams.page : null; //build a path to sync the tree with @@ -33,8 +34,6 @@ function MemberEditController($scope, $routeParams, $location, $q, $window, appS // we are not using the umbraco membership provider if ($routeParams.doctype) { - $scope.page.loading = true; - //we are creating so get an empty member item memberResource.getScaffold($routeParams.doctype) .then(function(data) { @@ -51,8 +50,6 @@ function MemberEditController($scope, $routeParams, $location, $q, $window, appS } else { - $scope.page.loading = true; - memberResource.getScaffold() .then(function (data) { $scope.content = data; @@ -75,14 +72,13 @@ function MemberEditController($scope, $routeParams, $location, $q, $window, appS //TODO: Why is this here - I don't understand why this would ever be an integer? This will not work when we support non-umbraco membership providers. if ($routeParams.id && $routeParams.id.length < 9) { + entityResource.getById($routeParams.id, "Member").then(function(entity) { - $location.path("/member/members/edit/" + entity.key); + $location.path("/member/member/edit/" + entity.key); }); } else { - $scope.page.loading = true; - //we are editing so get the content item from the server memberResource.getByKey($routeParams.id) .then(function(data) { @@ -96,7 +92,7 @@ function MemberEditController($scope, $routeParams, $location, $q, $window, appS var path = buildTreePath(data); //sync the tree (only for ui purposes) - navigationService.syncTree({ tree: "members", path: path.split(",") }); + navigationService.syncTree({ tree: "member", path: path.split(",") }); //it's the initial load of the editor, we need to get the tree node // from the server so that we can load in the actions menu. @@ -154,7 +150,7 @@ function MemberEditController($scope, $routeParams, $location, $q, $window, appS var path = buildTreePath(data); //sync the tree (only for ui purposes) - navigationService.syncTree({ tree: "members", path: path.split(","), forceReload: true }); + navigationService.syncTree({ tree: "member", path: path.split(","), forceReload: true }); }, function (err) { diff --git a/src/Umbraco.Web.UI.Client/src/views/members/member.list.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/member.list.controller.js similarity index 94% rename from src/Umbraco.Web.UI.Client/src/views/members/member.list.controller.js rename to src/Umbraco.Web.UI.Client/src/views/member/member.list.controller.js index 5d3b5dddce..9fc03349e8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/members/member.list.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/member/member.list.controller.js @@ -31,7 +31,7 @@ function MemberListController($scope, $routeParams, $location, $q, $window, appS editorState.set($scope.content); - navigationService.syncTree({ tree: "members", path: data.path.split(",") }).then(function (syncArgs) { + navigationService.syncTree({ tree: "member", path: data.path.split(",") }).then(function (syncArgs) { $scope.currentNode = syncArgs.node; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 06b790b777..667dfd0f22 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -1,7 +1,7 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it -function contentPickerController($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper) { +function contentPickerController($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location) { function trim(str, chr) { var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g'); @@ -49,6 +49,7 @@ function contentPickerController($scope, dialogService, entityResource, editorSt //the default pre-values var defaultConfig = { multiPicker: false, + showOpenButton: false, showEditButton: false, showPathOnHover: false, startNode: { @@ -65,14 +66,17 @@ function contentPickerController($scope, dialogService, entityResource, editorSt //Umbraco persists boolean for prevalues as "0" or "1" so we need to convert that! $scope.model.config.multiPicker = ($scope.model.config.multiPicker === "1" ? true : false); + $scope.model.config.showOpenButton = ($scope.model.config.showOpenButton === "1" ? true : false); $scope.model.config.showEditButton = ($scope.model.config.showEditButton === "1" ? true : false); $scope.model.config.showPathOnHover = ($scope.model.config.showPathOnHover === "1" ? true : false); - + var entityType = $scope.model.config.startNode.type === "member" ? "Member" : $scope.model.config.startNode.type === "media" ? "Media" : "Document"; + $scope.allowOpenButton = entityType === "Document" || entityType === "Media"; + $scope.allowEditButton = entityType === "Document"; //the dialog options for the picker var dialogOptions = { @@ -167,6 +171,21 @@ function contentPickerController($scope, dialogService, entityResource, editorSt angularHelper.getCurrentForm($scope).$setDirty(); }; + $scope.showNode = function (index) { + var item = $scope.renderModel[index]; + var id = item.id; + var section = $scope.model.config.startNode.type.toLowerCase(); + + entityResource.getPath(id, entityType).then(function (path) { + navigationService.changeSection(section); + navigationService.showTree(section, { + tree: section, path: path, forceReload: false, activate: true + }); + var routePath = section + "/" + section + "/edit/" + id.toString(); + $location.path(routePath).search(""); + }); + } + $scope.add = function (item) { var currIds = _.map($scope.renderModel, function (i) { return i.id; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index f2b741d8dc..9f5f3fb60f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -12,8 +12,10 @@ {{node.name}} -
- Edit + +
+ Open + Edit
@@ -38,7 +40,7 @@
You can only have {{model.config.maxNumber}} items selected
- + diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html index ac5d9a6ea3..70544bb965 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html @@ -1,4 +1,4 @@ -
+
@@ -22,6 +22,7 @@ parent-id="{{vm.nodeId}}" files-uploaded="vm.onUploadComplete" accept="{{vm.acceptedFileTypes}}" + max-file-size="{{vm.maxFileSize}}" hide-dropzone="{{!vm.activeDrag && items.length > 0 }}" compact="{{ items.length > 0 }}" files-queued="vm.onFilesQueue"> @@ -35,7 +36,7 @@ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js index b60caa514e..19a72146d6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js @@ -15,8 +15,10 @@ vm.nodeId = $scope.contentId; vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes); + vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB"; vm.activeDrag = false; vm.mediaDetailsTooltip = {}; + vm.itemsWithoutFolders = []; vm.dragEnter = dragEnter; vm.dragLeave = dragLeave; @@ -27,6 +29,26 @@ vm.selectFolder = selectFolder; vm.clickItem = clickItem; + function activate() { + vm.itemsWithoutFolders = filterOutFolders($scope.items); + } + + function filterOutFolders(items) { + + var newArray = []; + + for (var i = 0; items.length > i; i++) { + var item = items[i]; + var isFolder = !mediaHelper.hasFilePropertyType(item); + + if (!isFolder) { + newArray.push(item); + } + } + + return newArray; + } + function dragEnter(el, event) { vm.activeDrag = true; } @@ -60,7 +82,7 @@ } function selectItem(selectedItem, $event, index) { - listViewHelper.selectHandler(selectedItem, index, $scope.items, $scope.selection, $event); + listViewHelper.selectHandler(selectedItem, index, vm.itemsWithoutFolders, $scope.selection, $event); } function selectFolder(selectedItem, $event, index) { @@ -71,6 +93,8 @@ $location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + item.id); } + activate(); + } angular.module("umbraco").controller("Umbraco.PropertyEditors.ListView.GridLayoutController", ListViewGridLayoutController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.html index 717fee1383..e55af1f348 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.html @@ -10,6 +10,7 @@ parent-id="{{vm.nodeId}}" files-uploaded="vm.onUploadComplete" accept="{{vm.acceptedFileTypes}}" + max-file-size="{{vm.maxFileSize}}" hide-dropzone="{{!vm.activeDrag && items.length > 0 }}" compact="{{ items.length > 0 }}" files-queued="vm.onFilesQueue"> diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js index 73718b2530..3716f9010a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js @@ -8,6 +8,7 @@ vm.nodeId = $scope.contentId; vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes); + vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB"; vm.activeDrag = false; vm.selectItem = selectItem; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index fbbf6b7bdd..ad2ca705af 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -1,4 +1,4 @@ -function listViewController($rootScope, $scope, $routeParams, $injector, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q, mediaResource, listViewHelper) { +function listViewController($rootScope, $scope, $routeParams, $injector, $cookieStore, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q, mediaResource, listViewHelper) { //this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content // that isn't created yet, if we continue this will use the parent id in the route params which isn't what @@ -70,7 +70,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific ], layout: { layouts: $scope.model.config.layouts, - activeLayout: getFirstAllowedLayout($scope.model.config.layouts) + activeLayout: listViewHelper.getLayout($routeParams.id, $scope.model.config.layouts) }, allowBulkPublish: true, allowBulkUnpublish: true, @@ -99,23 +99,9 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific } }); - function getFirstAllowedLayout(layouts) { - - var firstAllowedLayout = {}; - - for (var i = 0; layouts.length > i; i++) { - - var layout = layouts[i]; - - if (layout.selected === true) { - firstAllowedLayout = layout; - break; - } - - } - - return firstAllowedLayout; - } + $scope.selectLayout = function(selectedLayout) { + $scope.options.layout.activeLayout = listViewHelper.setLayout($routeParams.id, selectedLayout, $scope.model.config.layouts); + }; function showNotificationsAndReset(err, reload, successMsg) { @@ -212,11 +198,23 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific $scope.reloadView($scope.contentId); } }); - }, 200)); + }, 1000)); - $scope.enterSearch = function($event) { + $scope.filterResults = function (ev) { + //13: enter + + switch (ev.keyCode) { + case 13: + $scope.options.pageNumber = 1; + $scope.actionInProgress = true; + $scope.reloadView($scope.contentId); + break; + } + }; + + $scope.enterSearch = function ($event) { $($event.target).next().focus(); - } + }; $scope.isAnythingSelected = function() { if ($scope.selection.length === 0) { @@ -461,4 +459,4 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific } -angular.module("umbraco").controller("Umbraco.PropertyEditors.ListViewController", listViewController); \ No newline at end of file +angular.module("umbraco").controller("Umbraco.PropertyEditors.ListViewController", listViewController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index 4eabec7436..ad1144d4f5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -51,18 +51,21 @@ + + active-layout="options.layout.activeLayout" + on-layout-select="selectLayout"> +
- +
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 85ceffbc46..44b9f978ef 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -40,7 +40,8 @@ v4.5 true - + + @@ -1856,6 +1857,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/renderbody.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/renderbody.gif new file mode 100644 index 0000000000..1a84493fe9 Binary files /dev/null and b/src/Umbraco.Web.UI/Umbraco/Images/editor/renderbody.gif differ diff --git a/src/Umbraco.Web.UI/Umbraco/js/install.loader.js b/src/Umbraco.Web.UI/Umbraco/js/install.loader.js deleted file mode 100644 index 869521ec7d..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/js/install.loader.js +++ /dev/null @@ -1,17 +0,0 @@ -LazyLoad.js( [ - 'lib/jquery/jquery.min.js', - /* 1.1.5 */ - 'lib/angular/1.1.5/angular.min.js', - 'lib/angular/1.1.5/angular-cookies.min.js', - 'lib/angular/1.1.5/angular-mobile.min.js', - 'lib/angular/1.1.5/angular-mocks.js', - 'lib/angular/1.1.5/angular-sanitize.min.js', - 'lib/underscore/underscore-min.js', - 'js/umbraco.installer.js', - 'js/umbraco.directives.js' - ], function () { - jQuery(document).ready(function () { - angular.bootstrap(document, ['ngSanitize', 'umbraco.install', 'umbraco.directives.validation']); - }); - } -); \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/ClientDependency.config b/src/Umbraco.Web.UI/config/ClientDependency.config index 2f0bc30fad..a0c0655dbe 100644 --- a/src/Umbraco.Web.UI/config/ClientDependency.config +++ b/src/Umbraco.Web.UI/config/ClientDependency.config @@ -10,7 +10,7 @@ NOTES: * Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config * A new version will invalidate both client and server cache and create new persisted files --> - + - - + - - - + + + + - - - + + diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index a89239144c..261617fd0c 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -9,16 +9,15 @@ - - + - - - + + + + - - - + + diff --git a/src/Umbraco.Web.UI/umbraco/Install/Views/Index.cshtml b/src/Umbraco.Web.UI/umbraco/Install/Views/Index.cshtml index 8171aad9e2..666f6a5b5a 100644 --- a/src/Umbraco.Web.UI/umbraco/Install/Views/Index.cshtml +++ b/src/Umbraco.Web.UI/umbraco/Install/Views/Index.cshtml @@ -45,7 +45,20 @@

-

{{installer.feedback}}

+

{{installer.feedback}}

+ + +