diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000000..c5560c3ce1 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,9 @@ +# The MIT License (MIT) # + +Copyright (c) 2013 Umbraco + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 32dab6f4a3..996f05936a 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -19,22 +19,21 @@ - + - + + + - - - diff --git a/build/NuSpecs/build/net40/UmbracoCms.props b/build/NuSpecs/build/net40/UmbracoCms.props index 50c21a7c0d..5e11945a9d 100644 --- a/build/NuSpecs/build/net40/UmbracoCms.props +++ b/build/NuSpecs/build/net40/UmbracoCms.props @@ -5,5 +5,10 @@ AddUmbracoFilesToOutput; $(CopyAllFilesToSingleFolderForPackageDependsOn); + + + AddUmbracoFilesToOutput; + $(CopyAllFilesToSingleFolderForPackageDependsOn); + \ No newline at end of file diff --git a/build/NuSpecs/build/net45/UmbracoCms.props b/build/NuSpecs/build/net45/UmbracoCms.props index 50c21a7c0d..5e11945a9d 100644 --- a/build/NuSpecs/build/net45/UmbracoCms.props +++ b/build/NuSpecs/build/net45/UmbracoCms.props @@ -5,5 +5,10 @@ AddUmbracoFilesToOutput; $(CopyAllFilesToSingleFolderForPackageDependsOn); + + + AddUmbracoFilesToOutput; + $(CopyAllFilesToSingleFolderForPackageDependsOn); + \ No newline at end of file diff --git a/build/NuSpecs/build/net451/UmbracoCms.props b/build/NuSpecs/build/net451/UmbracoCms.props index 50c21a7c0d..5e11945a9d 100644 --- a/build/NuSpecs/build/net451/UmbracoCms.props +++ b/build/NuSpecs/build/net451/UmbracoCms.props @@ -5,5 +5,10 @@ AddUmbracoFilesToOutput; $(CopyAllFilesToSingleFolderForPackageDependsOn); + + + AddUmbracoFilesToOutput; + $(CopyAllFilesToSingleFolderForPackageDependsOn); + \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/CacheKeys.cs b/src/Umbraco.Core/Cache/CacheKeys.cs index 1c89d7662f..54f9ab4832 100644 --- a/src/Umbraco.Core/Cache/CacheKeys.cs +++ b/src/Umbraco.Core/Cache/CacheKeys.cs @@ -31,6 +31,7 @@ namespace Umbraco.Core.Cache public const string UserContextCacheKey = "UmbracoUserContext"; public const string UserContextTimeoutCacheKey = "UmbracoUserContextTimeout"; public const string UserCacheKey = "UmbracoUser"; + public const string UserPermissionsCacheKey = "UmbracoUserPermissions"; public const string ContentTypeCacheKey = "UmbracoContentType"; diff --git a/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs b/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs index 9dddb4c576..d88a3922bb 100644 --- a/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs +++ b/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Cache /// An abstract class for implementing a runtime cache provider /// /// - /// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, ETC... + /// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, REQUEST CACHE, ETC... /// internal abstract class RuntimeCacheProviderBase : CacheProviderBase { diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 41ceaf9b90..0e5c3340fa 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core.Models private DateTime? _expireDate; private int _writer; private string _nodeName;//NOTE Once localization is introduced this will be the non-localized Node Name. - + private bool _permissionsChanged; /// /// Constructor for creating a Content object /// @@ -82,6 +82,7 @@ namespace Umbraco.Core.Models private static readonly PropertyInfo ExpireDateSelector = ExpressionHelper.GetPropertyInfo(x => x.ExpireDate); private static readonly PropertyInfo WriterSelector = ExpressionHelper.GetPropertyInfo(x => x.WriterId); private static readonly PropertyInfo NodeNameSelector = ExpressionHelper.GetPropertyInfo(x => x.NodeName); + private static readonly PropertyInfo PermissionsChangedSelector = ExpressionHelper.GetPropertyInfo(x => x.PermissionsChanged); /// /// Gets or sets the template used by the Content. @@ -243,6 +244,22 @@ namespace Umbraco.Core.Models } } + /// + /// Used internally to track if permissions have been changed during the saving process for this entity + /// + internal bool PermissionsChanged + { + get { return _permissionsChanged; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _permissionsChanged = value; + return _permissionsChanged; + }, _permissionsChanged, PermissionsChangedSelector); + } + } + /// /// Gets the ContentType used by this content object /// diff --git a/src/Umbraco.Core/Models/Membership/IUser.cs b/src/Umbraco.Core/Models/Membership/IUser.cs index 4960b1f2b9..268ed04219 100644 --- a/src/Umbraco.Core/Models/Membership/IUser.cs +++ b/src/Umbraco.Core/Models/Membership/IUser.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Models.Membership bool NoConsole { get; set; } IUserType UserType { get; } - string Permissions { get; set; } + string DefaultPermissions { get; set; } } internal interface IUserProfile : IProfile diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs index d6b71135ef..ddcc28cccb 100644 --- a/src/Umbraco.Core/Models/Membership/User.cs +++ b/src/Umbraco.Core/Models/Membership/User.cs @@ -91,7 +91,7 @@ namespace Umbraco.Core.Models.Membership [DataMember] public string Language { get; set; } [DataMember] - public string Permissions { get; set; } + public string DefaultPermissions { get; set; } [DataMember] public bool DefaultToLiveEditing { get; set; } diff --git a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs index 6d38db9739..0a046ca7a0 100644 --- a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs @@ -32,7 +32,7 @@ namespace Umbraco.Core.Persistence.Factories Language = dto.UserLanguage, DefaultToLiveEditing = dto.DefaultToLiveEditing, NoConsole = dto.NoConsole, - Permissions = dto.DefaultPermissions + DefaultPermissions = dto.DefaultPermissions }; foreach (var app in dto.User2AppDtos) @@ -62,7 +62,7 @@ namespace Umbraco.Core.Persistence.Factories UserLanguage = entity.Language, UserName = entity.Name, Type = short.Parse(entity.UserType.Id.ToString()), - DefaultPermissions = entity.Permissions, + DefaultPermissions = entity.DefaultPermissions, User2AppDtos = new List() }; diff --git a/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs b/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs index 1a7add097a..5e2947c3a7 100644 --- a/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs @@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.Username, dto => dto.Login); CacheMap(src => src.Password, dto => dto.Password); CacheMap(src => src.Name, dto => dto.UserName); - CacheMap(src => src.Permissions, dto => dto.DefaultPermissions); + CacheMap(src => src.DefaultPermissions, dto => dto.DefaultPermissions); CacheMap(src => src.StartMediaId, dto => dto.MediaStartId); CacheMap(src => src.StartContentId, dto => dto.ContentStartId); CacheMap(src => src.DefaultToLiveEditing, dto => dto.DefaultToLiveEditing); diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs index 269fea5f9a..d8507ac681 100644 --- a/src/Umbraco.Core/Persistence/PetaPoco.cs +++ b/src/Umbraco.Core/Persistence/PetaPoco.cs @@ -384,7 +384,7 @@ namespace Umbraco.Core.Persistence } // Add a parameter to a DB command - void AddParam(IDbCommand cmd, object item, string ParameterPrefix) + internal void AddParam(IDbCommand cmd, object item, string ParameterPrefix) { // Convert value to from poco type to db type if (Database.Mapper != null && item!=null) diff --git a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs index 85ab884857..6faf4d637a 100644 --- a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs +++ b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Data; using System.Linq; using Umbraco.Core.Logging; using Umbraco.Core.Models.Rdbms; @@ -28,6 +30,76 @@ namespace Umbraco.Core.Persistence CreateTable(db, overwrite, tableType); } + public static void BulkInsertRecords(this Database db, IEnumerable collection) + { + using (var tr = db.GetTransaction()) + { + try + { + if (SqlSyntaxContext.SqlSyntaxProvider is SqlCeSyntaxProvider) + { + //SqlCe doesn't support bulk insert statements! + + foreach (var poco in collection) + { + db.Insert(poco); + } + + } + else + { + string sql; + using (var cmd = db.GenerateBulkInsertCommand(collection, db.Connection, out sql)) + { + cmd.CommandText = sql; + cmd.ExecuteNonQuery(); + } + } + + tr.Complete(); + } + catch + { + tr.Dispose(); + throw; + } + } + } + + internal static IDbCommand GenerateBulkInsertCommand(this Database db, IEnumerable collection, IDbConnection connection, out string sql) + { + var pd = Database.PocoData.ForType(typeof(T)); + var tableName = db.EscapeTableName(pd.TableInfo.TableName); + + //get all columns but not the primary key if it is auto-incremental + var cols = string.Join(", ", ( + from c in pd.Columns + where c.Value.ResultColumn == false + select tableName + "." + db.EscapeSqlIdentifier(c.Key)) + .ToArray()); + + var cmd = db.CreateCommand(connection, ""); + + var pocoValues = new List(); + var index = 0; + foreach (var poco in collection) + { + var values = new List(); + foreach (var i in pd.Columns) + { + //if (pd.TableInfo.AutoIncrement && i.Key == pd.TableInfo.PrimaryKey) + //{ + // continue; + //} + values.Add(string.Format("{0}{1}", "@", index++)); + db.AddParam(cmd, i.Value.GetValue(poco), "@"); + } + pocoValues.Add("(" + string.Join(",", values.ToArray()) + ")"); + } + sql = string.Format("INSERT INTO {0} ({1}) VALUES {2}", tableName, cols, string.Join(", ", pocoValues)); + return cmd; + } + public static void CreateTable(this Database db, bool overwrite, Type modelType) { var tableDefinition = DefinitionFactory.GetTableDefinition(modelType); @@ -67,7 +139,7 @@ namespace Umbraco.Core.Persistence //Turn on identity insert if db provider is not mysql if (SqlSyntaxContext.SqlSyntaxProvider.SupportsIdentityInsert() && tableDefinition.Columns.Any(x => x.IsIdentity)) db.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(tableName)))); - + //Call the NewTable-event to trigger the insert of base/default data NewTable(tableName, db, e); @@ -106,7 +178,7 @@ namespace Umbraco.Core.Persistence public static void DropTable(this Database db) where T : new() { - Type type = typeof (T); + Type type = typeof(T); var tableNameAttribute = type.FirstAttribute(); if (tableNameAttribute == null) throw new Exception( @@ -184,5 +256,5 @@ namespace Umbraco.Core.Persistence } } - internal class TableCreationEventArgs : System.ComponentModel.CancelEventArgs{} + internal class TableCreationEventArgs : System.ComponentModel.CancelEventArgs { } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 2c92517b15..755fc5f04a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -206,7 +206,7 @@ namespace Umbraco.Core.Persistence.Repositories nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture)); nodeDto.SortOrder = sortOrder; var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto); - + //Update with new correct path nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId); Database.Update(nodeDto); @@ -217,6 +217,24 @@ namespace Umbraco.Core.Persistence.Repositories entity.SortOrder = sortOrder; entity.Level = level; + + //Assign the same permissions to it as the parent node + // http://issues.umbraco.org/issue/U4-2161 + var parentPermissions = GetPermissionsForEntity(entity.ParentId).ToArray(); + //if there are parent permissions then assign them, otherwise leave null and permissions will become the + // user's default permissions. + if (parentPermissions.Any()) + { + var userPermissions = parentPermissions.Select( + permissionDto => new KeyValuePair( + permissionDto.UserId, + permissionDto.Permission)); + AssignEntityPermissions(entity, userPermissions); + //flag the entity's permissions changed flag so we can track those changes. + //Currently only used for the cache refreshers to detect if we should refresh all user permissions cache. + ((Content) entity).PermissionsChanged = true; + } + //Create the Content specific data - cmsContent var contentDto = dto.ContentVersionDto.ContentDto; contentDto.NodeId = nodeDto.NodeId; @@ -283,6 +301,10 @@ namespace Umbraco.Core.Persistence.Repositories "SELECT coalesce(max(sortOrder),0) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType", new {ParentId = entity.ParentId, NodeObjectType = NodeObjectTypeId}); entity.SortOrder = maxSortOrder + 1; + + //Question: If we move a node, should we update permissions to inherit from the new parent if the parent has permissions assigned? + // if we do that, then we'd need to propogate permissions all the way downward which might not be ideal for many people. + // Gonna just leave it as is for now, and not re-propogate permissions. } var factory = new ContentFactory(NodeObjectTypeId, entity.Id); @@ -510,7 +532,7 @@ namespace Umbraco.Core.Persistence.Repositories var sql = new Sql(); sql.Select("*") .From() - .Where(x => x.ParentId == parentId && x.Text.StartsWith(nodeName)); + .Where(x => x.NodeObjectType == NodeObjectTypeId && x.ParentId == parentId && x.Text.StartsWith(nodeName)); int uniqueNumber = 1; var currentName = nodeName; @@ -533,45 +555,5 @@ namespace Umbraco.Core.Persistence.Repositories return currentName; } - - /// - /// Comparer that takes into account the duplicate index of a node name - /// This is needed as a normal alphabetic sort would go Page (1), Page (10), Page (2) etc. - /// - private class SimilarNodeNameComparer : IComparer - { - public int Compare(string x, string y) - { - if (x.LastIndexOf(')') == x.Length - 1 && y.LastIndexOf(')') == y.Length - 1) - { - if (x.ToLower().Substring(0, x.LastIndexOf('(')) == y.ToLower().Substring(0, y.LastIndexOf('('))) - { - int xDuplicateIndex = ExtractDuplicateIndex(x); - int yDuplicateIndex = ExtractDuplicateIndex(y); - - if (xDuplicateIndex != 0 && yDuplicateIndex != 0) - { - return xDuplicateIndex.CompareTo(yDuplicateIndex); - } - } - } - return String.Compare(x.ToLower(), y.ToLower(), StringComparison.Ordinal); - } - - private int ExtractDuplicateIndex(string text) - { - int index = 0; - - if (text.LastIndexOf('(') != -1 && text.LastIndexOf('(') < text.Length - 2) - { - int startPos = text.LastIndexOf('(') + 1; - int length = text.Length - 1 - startPos; - - int.TryParse(text.Substring(startPos, length), out index); - } - - return index; - } - } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 81ec056f0a..e27fac1ba3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -25,14 +25,20 @@ namespace Umbraco.Core.Persistence.Repositories : base(work) { _mediaTypeRepository = mediaTypeRepository; + + EnsureUniqueNaming = true; } public MediaRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IMediaTypeRepository mediaTypeRepository) : base(work, cache) { _mediaTypeRepository = mediaTypeRepository; + + EnsureUniqueNaming = true; } + public bool EnsureUniqueNaming { get; set; } + #region Overrides of RepositoryBase protected override IMedia PerformGet(int id) @@ -182,6 +188,9 @@ namespace Umbraco.Core.Persistence.Repositories { ((Models.Media)entity).AddingEntity(); + //Ensure unique name on the same level + entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name); + var factory = new MediaFactory(NodeObjectTypeId, entity.Id); var dto = factory.BuildDto(entity); @@ -246,6 +255,9 @@ namespace Umbraco.Core.Persistence.Repositories //Updates Modified date ((Models.Media)entity).UpdatingEntity(); + //Ensure unique name on the same level + entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name, entity.Id); + //Look up parent to get and set the correct Path and update SortOrder if ParentId has changed if (((ICanBeDirty)entity).IsPropertyDirty("ParentId")) { @@ -370,5 +382,37 @@ namespace Umbraco.Core.Persistence.Repositories return new PropertyCollection(properties); } + + private string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0) + { + if (EnsureUniqueNaming == false) + return nodeName; + + var sql = new Sql(); + sql.Select("*") + .From() + .Where(x => x.NodeObjectType == NodeObjectTypeId && x.ParentId == parentId && x.Text.StartsWith(nodeName)); + + int uniqueNumber = 1; + var currentName = nodeName; + + var dtos = Database.Fetch(sql); + if (dtos.Any()) + { + var results = dtos.OrderBy(x => x.Text, new SimilarNodeNameComparer()); + foreach (var dto in results) + { + if (id != 0 && id == dto.NodeId) continue; + + if (dto.Text.ToLowerInvariant().Equals(currentName.ToLowerInvariant())) + { + currentName = nodeName + string.Format(" ({0})", uniqueNumber); + uniqueNumber++; + } + } + } + + return currentName; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs new file mode 100644 index 0000000000..0bf6a6eeb8 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Text; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Persistence.Repositories +{ + /// + /// A repository that exposes functionality to modify assigned permissions to a node + /// + /// + /// + internal abstract class PermissionRepository : PetaPocoRepositoryBase + where TEntity : class, IAggregateRoot + { + protected PermissionRepository(IDatabaseUnitOfWork work) + : base(work) + { + } + + protected PermissionRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) + : base(work, cache) + { + } + + protected internal IEnumerable GetPermissionsForEntity(int entityId) + { + var sql = new Sql(); + sql.Select("*") + .From() + .Where(dto => dto.NodeId == entityId); + return Database.Fetch(sql); + } + + /// + /// Assigns permissions to an entity for multiple users + /// + /// + /// + /// + protected internal void AssignEntityPermissions(TEntity entity, string permissions, IEnumerable userIds) + { + var actions = userIds.Select(id => new User2NodePermissionDto + { + NodeId = entity.Id, + Permission = permissions, + UserId = (int)id + }); + + Database.BulkInsertRecords(actions); + } + + /// + /// Assigns permissions to an entity for multiple users/permission entries + /// + /// + /// + /// A key/value pair list containing a userId and a permission to assign + /// + protected internal void AssignEntityPermissions(TEntity entity, IEnumerable> userPermissions) + { + var actions = userPermissions.Select(p => new User2NodePermissionDto + { + NodeId = entity.Id, + Permission = p.Value, + UserId = (int)p.Key + }); + + Database.BulkInsertRecords(actions); + } + + /// + /// Replace permissions for an entity for multiple users + /// + /// + /// + /// + protected internal void ReplaceEntityPermissions(TEntity entity, string permissions, IEnumerable userIds) + { + Database.Update( + GenerateReplaceEntityPermissionsSql(entity.Id, permissions, userIds.ToArray())); + } + + /// + /// An overload to replace entity permissions and all replace all descendant permissions + /// + /// + /// + /// + /// A callback to get the descendant Ids of the current entity + /// + /// + protected internal void ReplaceEntityPermissions(TEntity entity, string permissions, Func> getDescendantIds, IEnumerable userIds) + { + Database.Update( + GenerateReplaceEntityPermissionsSql( + new[] {entity.Id}.Concat(getDescendantIds(entity)).ToArray(), + permissions, + userIds.ToArray())); + } + + internal static string GenerateReplaceEntityPermissionsSql(int entityId, string permissions, object[] userIds) + { + return GenerateReplaceEntityPermissionsSql(new[] {entityId}, permissions, userIds); + } + + internal static string GenerateReplaceEntityPermissionsSql(int[] entityIds, string permissions, object[] userIds) + { + //create the "SET" clause of the update statement + var sqlSet = string.Format("SET {0}={1}", + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("permission"), + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedValue(permissions)); + + //build the nodeIds part of the where clause + var sqlNodeWhere = BuildOrClause(entityIds, "nodeId"); + + //build up the userIds part of the where clause + var userWhereBuilder = BuildOrClause(userIds, "userId"); + + var sqlWhere = new Sql(); + sqlWhere.Where(string.Format("{0} AND {1}", sqlNodeWhere, userWhereBuilder)); + + return string.Format("{0} {1}", sqlSet, sqlWhere.SQL); + } + + private static string BuildOrClause(IEnumerable ids, string colName) + { + var asArray = ids.ToArray(); + var userWhereBuilder = new StringBuilder(); + userWhereBuilder.Append("("); + for (var index = 0; index < asArray.Length; index++) + { + var userId = asArray[index]; + userWhereBuilder.Append(SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(colName)); + userWhereBuilder.Append("="); + userWhereBuilder.Append(userId); + if (index < asArray.Length - 1) + { + userWhereBuilder.Append(" OR "); + } + } + userWhereBuilder.Append(")"); + return userWhereBuilder.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/SimilarNodeNameComparer.cs b/src/Umbraco.Core/Persistence/Repositories/SimilarNodeNameComparer.cs new file mode 100644 index 0000000000..734eecf1f9 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/SimilarNodeNameComparer.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Persistence.Repositories +{ + /// + /// Comparer that takes into account the duplicate index of a node name + /// This is needed as a normal alphabetic sort would go Page (1), Page (10), Page (2) etc. + /// + internal class SimilarNodeNameComparer : IComparer + { + public int Compare(string x, string y) + { + if (x.LastIndexOf(')') == x.Length - 1 && y.LastIndexOf(')') == y.Length - 1) + { + if (x.ToLower().Substring(0, x.LastIndexOf('(')) == y.ToLower().Substring(0, y.LastIndexOf('('))) + { + int xDuplicateIndex = ExtractDuplicateIndex(x); + int yDuplicateIndex = ExtractDuplicateIndex(y); + + if (xDuplicateIndex != 0 && yDuplicateIndex != 0) + { + return xDuplicateIndex.CompareTo(yDuplicateIndex); + } + } + } + return String.Compare(x.ToLower(), y.ToLower(), StringComparison.Ordinal); + } + + private int ExtractDuplicateIndex(string text) + { + int index = 0; + + if (text.LastIndexOf('(') != -1 && text.LastIndexOf('(') < text.Length - 2) + { + int startPos = text.LastIndexOf('(') + 1; + int length = text.Length - 1 - startPos; + + int.TryParse(text.Substring(startPos, length), out index); + } + + return index; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 6ecce76f73..6b11bd3a2c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// Represents the UserRepository for doing CRUD operations for /// - internal class UserRepository : PetaPocoRepositoryBase, IUserRepository + internal class UserRepository : PermissionRepository, IUserRepository { private readonly IUserTypeRepository _userTypeRepository; @@ -122,9 +122,12 @@ namespace Umbraco.Core.Persistence.Repositories } protected override IEnumerable GetDeleteClauses() - { + { var list = new List { + "DELETE FROM umbracoUser2NodePermission WHERE userId = @Id", + "DELETE FROM umbracoUser2NodeNotify WHERE userId = @Id", + "DELETE FROM umbracoUserLogins WHERE userId = @Id", "DELETE FROM umbracoUser2app WHERE " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("user") + "=@Id", "DELETE FROM umbracoUser WHERE id = @Id" }; diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index 3a9b5041eb..0ed84582fa 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { - internal abstract class VersionableRepositoryBase : PetaPocoRepositoryBase + internal abstract class VersionableRepositoryBase : PermissionRepository where TEntity : class, IAggregateRoot { protected VersionableRepositoryBase(IDatabaseUnitOfWork work) : base(work) diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index 38189576a7..73c7b290f9 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -81,7 +81,7 @@ namespace Umbraco.Core.Persistence return new MediaRepository( uow, RuntimeCacheProvider.Current, - CreateMediaTypeRepository(uow)); + CreateMediaTypeRepository(uow)) { EnsureUniqueNaming = Umbraco.Core.Configuration.UmbracoSettings.EnsureUniqueNaming }; } public virtual IMediaTypeRepository CreateMediaTypeRepository(IDatabaseUnitOfWork uow) diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs index e69ef4cca2..522e0b4173 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Data; using System.Data.Common; +using System.Linq; using StackExchange.Profiling; using Umbraco.Core.Logging; @@ -46,8 +48,7 @@ namespace Umbraco.Core.Persistence // wrap the connection with a profiling connection that tracks timings return new StackExchange.Profiling.Data.ProfiledDbConnection(connection as DbConnection, MiniProfiler.Current); } - - + public override void OnException(Exception x) { LogHelper.Info(x.StackTrace); diff --git a/src/Umbraco.Core/Profiling/WebProfiler.cs b/src/Umbraco.Core/Profiling/WebProfiler.cs index 56918ff349..3a1974279e 100644 --- a/src/Umbraco.Core/Profiling/WebProfiler.cs +++ b/src/Umbraco.Core/Profiling/WebProfiler.cs @@ -1,6 +1,7 @@ using System; using System.Web; using StackExchange.Profiling; +using StackExchange.Profiling.SqlFormatters; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; @@ -52,13 +53,8 @@ namespace Umbraco.Core.Profiling /// void UmbracoApplicationEndRequest(object sender, EventArgs e) { - if (GlobalSettings.DebugMode == false) return; - var request = TryGetRequest(sender); - if (request.Success == false) return; - if (request.Result.Url.IsClientSideRequest()) return; - if (string.IsNullOrEmpty(request.Result["umbDebug"]) == false) + if (CanPerformProfilingAction(sender)) { - //stop the profiler Stop(); } } @@ -70,17 +66,28 @@ namespace Umbraco.Core.Profiling /// void UmbracoApplicationBeginRequest(object sender, EventArgs e) { - if (GlobalSettings.DebugMode == false) return; - var request = TryGetRequest(sender); - if (request.Success == false) return; - if (request.Result.Url.IsClientSideRequest()) return; - if (string.IsNullOrEmpty(request.Result["umbDebug"]) == false) + if (CanPerformProfilingAction(sender)) { - //start the profiler Start(); } } + private bool CanPerformProfilingAction(object sender) + { + if (GlobalSettings.DebugMode == false) + return false; + + //will not run in medium trust + if (SystemUtilities.GetCurrentTrustLevel() < AspNetHostingPermissionLevel.High) + return false; + + var request = TryGetRequest(sender); + if (request.Success == false || request.Result.Url.IsClientSideRequest() || string.IsNullOrEmpty(request.Result["umbDebug"])) + return false; + + return true; + } + /// /// Render the UI to display the profiler /// @@ -103,9 +110,7 @@ namespace Umbraco.Core.Profiling /// public IDisposable Step(string name) { - if (GlobalSettings.DebugMode == false) return null; - - return MiniProfiler.Current.Step(name); + return GlobalSettings.DebugMode == false ? null : MiniProfiler.Current.Step(name); } /// @@ -113,10 +118,8 @@ namespace Umbraco.Core.Profiling /// public void Start() { - if (GlobalSettings.DebugMode == false) return; - //will not run in medium trust - if (SystemUtilities.GetCurrentTrustLevel() < AspNetHostingPermissionLevel.High) return; - + MiniProfiler.Settings.SqlFormatter = new SqlServerFormatter(); + MiniProfiler.Settings.StackMaxLength = 5000; MiniProfiler.Start(); } @@ -129,10 +132,6 @@ namespace Umbraco.Core.Profiling /// public void Stop(bool discardResults = false) { - if (GlobalSettings.DebugMode == false) return; - //will not run in medium trust - if (SystemUtilities.GetCurrentTrustLevel() < AspNetHostingPermissionLevel.High) return; - MiniProfiler.Stop(discardResults); } @@ -156,6 +155,5 @@ namespace Umbraco.Core.Profiling return new Attempt(ex); } } - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index b773a901c3..41a536fc46 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -3,14 +3,12 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; -using System.Web; using System.Xml.Linq; using Umbraco.Core.Auditing; using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Caching; @@ -88,7 +86,7 @@ namespace Umbraco.Core.Services Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parentId), this); - Audit.Add(AuditTypes.New, "", content.CreatorId, content.Id); + Audit.Add(AuditTypes.New, string.Format("Content '{0}' was created", name), content.CreatorId, content.Id); return content; } @@ -123,7 +121,7 @@ namespace Umbraco.Core.Services Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parent), this); - Audit.Add(AuditTypes.New, "", content.CreatorId, content.Id); + Audit.Add(AuditTypes.New, string.Format("Content '{0}' was created", name), content.CreatorId, content.Id); return content; } @@ -163,7 +161,7 @@ namespace Umbraco.Core.Services Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parentId), this); - Audit.Add(AuditTypes.New, "", content.CreatorId, content.Id); + Audit.Add(AuditTypes.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id); return content; } @@ -203,7 +201,7 @@ namespace Umbraco.Core.Services Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parent), this); - Audit.Add(AuditTypes.New, "", content.CreatorId, content.Id); + Audit.Add(AuditTypes.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id); return content; } diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index 7a09e24b32..0e35802961 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -63,9 +63,9 @@ namespace Umbraco.Core.Services media.CreatorId = userId; - Created.RaiseEvent(new NewEventArgs(media, false, mediaTypeAlias, parentId), this); + Created.RaiseEvent(new NewEventArgs(media, false, mediaTypeAlias, parentId), this); - Audit.Add(AuditTypes.New, "", media.CreatorId, media.Id); + Audit.Add(AuditTypes.New, string.Format("Media '{0}' was created", name), media.CreatorId, media.Id); return media; } @@ -98,7 +98,7 @@ namespace Umbraco.Core.Services Created.RaiseEvent(new NewEventArgs(media, false, mediaTypeAlias, parent), this); - Audit.Add(AuditTypes.New, "", media.CreatorId, media.Id); + Audit.Add(AuditTypes.New, string.Format("Media '{0}' was created", name), media.CreatorId, media.Id); return media; } @@ -142,7 +142,7 @@ namespace Umbraco.Core.Services Created.RaiseEvent(new NewEventArgs(media, false, mediaTypeAlias, parentId), this); - Audit.Add(AuditTypes.New, "", media.CreatorId, media.Id); + Audit.Add(AuditTypes.New, string.Format("Media '{0}' was created with Id {1}", name, media.Id), media.CreatorId, media.Id); return media; } @@ -186,7 +186,7 @@ namespace Umbraco.Core.Services Created.RaiseEvent(new NewEventArgs(media, false, mediaTypeAlias, parent), this); - Audit.Add(AuditTypes.New, "", media.CreatorId, media.Id); + Audit.Add(AuditTypes.New, string.Format("Media '{0}' was created with Id {1}", name, media.Id), media.CreatorId, media.Id); return media; } diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index f7bbe244bb..e451d3aa05 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -164,7 +164,7 @@ namespace Umbraco.Core.Services Language = Umbraco.Core.Configuration.GlobalSettings.DefaultUILanguage, Name = name, Password = password, - Permissions = userType.Permissions, + DefaultPermissions = userType.Permissions, Username = login, StartContentId = -1, StartMediaId = -1, diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index e5a01934c5..c060e218b2 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -77,28 +77,28 @@ True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll True - ..\packages\Microsoft.AspNet.Mvc.4.0.20710.0\lib\net40\System.Web.Mvc.dll + ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll True - ..\packages\Microsoft.AspNet.Razor.2.0.20715.0\lib\net40\System.Web.Razor.dll + ..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll @@ -511,12 +511,14 @@ + + diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config index 2a9de67098..9e1744a360 100644 --- a/src/Umbraco.Core/packages.config +++ b/src/Umbraco.Core/packages.config @@ -2,9 +2,9 @@ - - - + + + diff --git a/src/Umbraco.Tests/Persistence/PetaPocoExtensionsTest.cs b/src/Umbraco.Tests/Persistence/PetaPocoExtensionsTest.cs new file mode 100644 index 0000000000..1cf6aff7b0 --- /dev/null +++ b/src/Umbraco.Tests/Persistence/PetaPocoExtensionsTest.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Persistence +{ + [TestFixture] + public class PetaPocoExtensionsTest : BaseDatabaseFactoryTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + + [Test] + public void Can_Bulk_Insert() + { + // Arrange + var db = DatabaseContext.Database; + + var servers = new List(); + for (var i = 0; i < 1000; i++) + { + servers.Add(new ServerRegistrationDto + { + Address = "address" + i, + ComputerName = "computer" + i, + DateRegistered = DateTime.Now, + IsActive = true, + LastNotified = DateTime.Now + }); + } + + // Act + using (DisposableTimer.TraceDuration("starting insert", "finished insert")) + { + db.BulkInsertRecords(servers); + } + + // Assert + Assert.That(db.ExecuteScalar("SELECT COUNT(*) FROM umbracoServer"), Is.EqualTo(1000)); + } + + [Test] + public void Generate_Bulk_Import_Sql() + { + // Arrange + var db = DatabaseContext.Database; + + var servers = new List(); + for (var i = 0; i < 2; i++) + { + servers.Add(new ServerRegistrationDto + { + Address = "address" + i, + ComputerName = "computer" + i, + DateRegistered = DateTime.Now, + IsActive = true, + LastNotified = DateTime.Now + }); + } + db.OpenSharedConnection(); + + // Act + string sql; + db.GenerateBulkInsertCommand(servers, db.Connection, out sql); + db.CloseSharedConnection(); + + // Assert + Assert.That(sql, + Is.EqualTo("INSERT INTO [umbracoServer] ([umbracoServer].[address], [umbracoServer].[computerName], [umbracoServer].[registeredDate], [umbracoServer].[lastNotifiedDate], [umbracoServer].[isActive]) VALUES (@0,@1,@2,@3,@4), (@5,@6,@7,@8,@9)")); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs b/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs index 4c71c072c7..8494ec7c8c 100644 --- a/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs +++ b/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs @@ -1,7 +1,10 @@ using System; using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Repositories; using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Persistence.Querying @@ -9,6 +12,26 @@ namespace Umbraco.Tests.Persistence.Querying [TestFixture] public class PetaPocoSqlTests : BaseUsingSqlCeSyntax { + [Test] + public void Generate_Replace_Entity_Permissions_Test() + { + // Act + var sql = PermissionRepository.GenerateReplaceEntityPermissionsSql(123, "A", new object[] {10, 11, 12}); + + // Assert + Assert.AreEqual(@"SET [permission]='A' WHERE (([nodeId]=123) AND ([userId]=10 OR [userId]=11 OR [userId]=12))", sql); + } + + [Test] + public void Generate_Replace_Entity_Permissions_With_Descendants_Test() + { + // Act + var sql = PermissionRepository.GenerateReplaceEntityPermissionsSql(new[] {123, 456}, "A", new object[] {10, 11, 12}); + + // Assert + Assert.AreEqual(@"SET [permission]='A' WHERE (([nodeId]=123 OR [nodeId]=456) AND ([userId]=10 OR [userId]=11 OR [userId]=12))", sql); + } + [Test] public void Can_Select_From_With_Type() { diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index b7717c6a23..973000b9ca 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; @@ -32,6 +34,42 @@ namespace Umbraco.Tests.Persistence.Repositories base.TearDown(); } + [Test] + public void Ensures_Permissions_Are_Set_If_Parent_Entity_Permissions_Exist() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var contentTypeRepository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var repository = (ContentRepository)RepositoryResolver.Current.ResolveByType(unitOfWork); + + var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage"); + contentType.AllowedContentTypes = new List + { + new ContentTypeSort + { + Alias = contentType.Alias, + Id = new Lazy(() => contentType.Id), + SortOrder = 0 + } + }; + var parentPage = MockedContent.CreateSimpleContent(contentType); + contentTypeRepository.AddOrUpdate(contentType); + repository.AddOrUpdate(parentPage); + unitOfWork.Commit(); + + // Act + repository.AssignEntityPermissions(parentPage, "A", new object[] {0}); + var childPage = MockedContent.CreateSimpleContent(contentType, "child", parentPage); + repository.AddOrUpdate(childPage); + unitOfWork.Commit(); + + // Assert + var permissions = repository.GetPermissionsForEntity(childPage.Id); + Assert.AreEqual(1, permissions.Count()); + Assert.AreEqual("A", permissions.Single().Permission); + } + [Test] public void Can_Instantiate_Repository() { diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index 76d21e1d56..a341a5cc74 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -115,7 +115,7 @@ namespace Umbraco.Tests.Persistence.Repositories var resolved = repository.Get((int)user.Id); resolved.Name = "New Name"; - resolved.Permissions = "ZYX"; + resolved.DefaultPermissions = "ZYX"; resolved.Language = "fr"; resolved.IsApproved = false; resolved.Password = "new"; @@ -134,7 +134,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Assert Assert.That(updatedItem.Id, Is.EqualTo(resolved.Id)); Assert.That(updatedItem.Name, Is.EqualTo(resolved.Name)); - Assert.That(updatedItem.Permissions, Is.EqualTo(resolved.Permissions)); + Assert.That(updatedItem.DefaultPermissions, Is.EqualTo(resolved.DefaultPermissions)); Assert.That(updatedItem.Language, Is.EqualTo(resolved.Language)); Assert.That(updatedItem.IsApproved, Is.EqualTo(resolved.IsApproved)); Assert.That(updatedItem.Password, Is.EqualTo(resolved.Password)); @@ -173,6 +173,32 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(resolved, Is.Null); } + //[Test] + //public void Can_Perform_Delete_On_UserRepository_With_Permissions_Assigned() + //{ + // // Arrange + // var provider = new PetaPocoUnitOfWorkProvider(); + // var unitOfWork = provider.GetUnitOfWork(); + // var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + + // var user = MockedUser.CreateUser(CreateAndCommitUserType()); + // //repository.AssignPermissions() + + // // Act + // repository.AddOrUpdate(user); + // unitOfWork.Commit(); + // var id = user.Id; + + // var repository2 = RepositoryResolver.Current.ResolveByType(unitOfWork); + // repository2.Delete(user); + // unitOfWork.Commit(); + + // var resolved = repository2.Get((int)id); + + // // Assert + // Assert.That(resolved, Is.Null); + //} + [Test] public void Can_Perform_Get_On_UserRepository() { @@ -190,7 +216,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Assert AssertPropertyValues(updatedItem, user); } - + [Test] public void Can_Perform_GetByQuery_On_UserRepository() { @@ -405,7 +431,7 @@ namespace Umbraco.Tests.Persistence.Repositories { Assert.That(updatedItem.Id, Is.EqualTo(originalUser.Id)); Assert.That(updatedItem.Name, Is.EqualTo(originalUser.Name)); - Assert.That(updatedItem.Permissions, Is.EqualTo(originalUser.Permissions)); + Assert.That(updatedItem.DefaultPermissions, Is.EqualTo(originalUser.DefaultPermissions)); Assert.That(updatedItem.Language, Is.EqualTo(originalUser.Language)); Assert.That(updatedItem.IsApproved, Is.EqualTo(originalUser.IsApproved)); Assert.That(updatedItem.Password, Is.EqualTo(originalUser.Password)); diff --git a/src/Umbraco.Tests/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedMediaCacheTests.cs index a1d922e86d..d795ebe18d 100644 --- a/src/Umbraco.Tests/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/PublishedCache/PublishedMediaCacheTests.cs @@ -61,7 +61,7 @@ namespace Umbraco.Tests.PublishedCache Assert.AreEqual(mRoot.Id, publishedMedia.Id); Assert.AreEqual(mRoot.CreateDateTime.ToString("dd/MM/yyyy HH:mm:ss"), publishedMedia.CreateDate.ToString("dd/MM/yyyy HH:mm:ss")); Assert.AreEqual(mRoot.User.Id, publishedMedia.CreatorId); - Assert.AreEqual(mRoot.User.Name, publishedMedia.CreatorName); + Assert.AreEqual(mRoot.User.LoginName, publishedMedia.CreatorName); Assert.AreEqual(mRoot.ContentType.Alias, publishedMedia.DocumentTypeAlias); Assert.AreEqual(mRoot.ContentType.Id, publishedMedia.DocumentTypeId); Assert.AreEqual(mRoot.Level, publishedMedia.Level); diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index 6da5002436..b8cc7f6225 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -39,7 +39,7 @@ namespace Umbraco.Tests.Services Assert.That(membershipUser.HasIdentity, Is.True); IUser user = membershipUser as User; Assert.That(user, Is.Not.Null); - Assert.That(user.Permissions, Is.EqualTo(userType.Permissions)); + Assert.That(user.DefaultPermissions, Is.EqualTo(userType.Permissions)); } [Test] @@ -63,7 +63,7 @@ namespace Umbraco.Tests.Services Assert.That(membershipUser.Password, Is.EqualTo(encodedPassword)); IUser user = membershipUser as User; Assert.That(user, Is.Not.Null); - Assert.That(user.Permissions, Is.EqualTo(userType.Permissions)); + Assert.That(user.DefaultPermissions, Is.EqualTo(userType.Permissions)); } [Test] @@ -140,7 +140,7 @@ namespace Umbraco.Tests.Services Assert.IsNotNull(updatedItem); Assert.That(updatedItem.Id, Is.EqualTo(originalUser.Id)); Assert.That(updatedItem.Name, Is.EqualTo(originalUser.Name)); - Assert.That(updatedItem.Permissions, Is.EqualTo(originalUser.Permissions)); + Assert.That(updatedItem.DefaultPermissions, Is.EqualTo(originalUser.DefaultPermissions)); Assert.That(updatedItem.Language, Is.EqualTo(originalUser.Language)); Assert.That(updatedItem.IsApproved, Is.EqualTo(originalUser.IsApproved)); Assert.That(updatedItem.Password, Is.EqualTo(originalUser.Password)); diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs index 19e0148383..7df112515d 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs @@ -19,7 +19,7 @@ namespace Umbraco.Tests.TestHelpers.Entities Name = "TestUser" + suffix, Password = "testing", NoConsole = false, - Permissions = "ABC", + DefaultPermissions = "ABC", StartContentId = -1, StartMediaId = -1, DefaultToLiveEditing = false, diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 4a7abc4054..1e98b91873 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -102,7 +102,7 @@ ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll - ..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll + ..\packages\Microsoft.AspNet.WebApi.Client.4.0.30506.0\lib\net40\System.Net.Http.Formatting.dll ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll @@ -111,33 +111,33 @@ True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll - ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll + ..\packages\Microsoft.AspNet.WebApi.Core.4.0.30506.0\lib\net40\System.Web.Http.dll - ..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.20710.0\lib\net40\System.Web.Http.WebHost.dll + ..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.30506.0\lib\net40\System.Web.Http.WebHost.dll True - ..\packages\Microsoft.AspNet.Mvc.4.0.20710.0\lib\net40\System.Web.Mvc.dll + ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll True - ..\packages\Microsoft.AspNet.Razor.2.0.20715.0\lib\net40\System.Web.Razor.dll + ..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll @@ -196,6 +196,7 @@ + diff --git a/src/Umbraco.Tests/packages.config b/src/Umbraco.Tests/packages.config index 07ac8de73d..13dbc067bd 100644 --- a/src/Umbraco.Tests/packages.config +++ b/src/Umbraco.Tests/packages.config @@ -4,13 +4,13 @@ - - - - - - - + + + + + + + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index c0f5cfba47..429ba87931 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -102,13 +102,13 @@ {07fbc26b-2927-4a22-8d96-d644c667fecc} UmbracoExamine - + False - ..\packages\ClientDependency.1.7.0.2\lib\ClientDependency.Core.dll + ..\packages\ClientDependency.1.7.0.3\lib\ClientDependency.Core.dll - + False - ..\packages\ClientDependency-Mvc.1.7.0.0\lib\ClientDependency.Core.Mvc.dll + ..\packages\ClientDependency-Mvc.1.7.0.3\lib\ClientDependency.Core.Mvc.dll False @@ -133,9 +133,9 @@ False ..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll - + False - ..\packages\MySql.Data.6.6.4\lib\net40\MySql.Data.dll + ..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll False @@ -173,7 +173,7 @@ ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll - ..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll + ..\packages\Microsoft.AspNet.WebApi.Client.4.0.30506.0\lib\net40\System.Net.Http.Formatting.dll ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll @@ -191,36 +191,36 @@ True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll - ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll + ..\packages\Microsoft.AspNet.WebApi.Core.4.0.30506.0\lib\net40\System.Web.Http.dll - ..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.20710.0\lib\net40\System.Web.Http.WebHost.dll + ..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.30506.0\lib\net40\System.Web.Http.WebHost.dll True - ..\packages\Microsoft.AspNet.Mvc.4.0.20710.0\lib\net40\System.Web.Mvc.dll + ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll True - ..\packages\Microsoft.AspNet.Razor.2.0.20715.0\lib\net40\System.Web.Razor.dll + ..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll System.Web.Services True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll System.XML diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index d1ed9ea678..a481c72581 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -1,23 +1,23 @@  - - + + - + - - - - - - + + + + + + - + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index e895731f4b..f331e88d9b 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -548,6 +548,7 @@ To manage your website, simply open the umbraco back office and start adding con No node selected yet, please select a node in the list above before clicking 'ok' The current node is not allowed under the chosen node because of its type The current node cannot be moved to one of its subpages + The current node cannot exist at the root The action isn't allowed since you have insufficient permissions on 1 or more child documents. Relate copied items to original diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index a3e66517da..2723e209f7 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -535,6 +535,7 @@ To manage your website, simply open the umbraco back office and start adding con No node selected yet, please select a node in the list above before clicking 'ok' The current node is not allowed under the chosen node because of its type The current node cannot be moved to one of its subpages + The current node cannot exist at the root The action isn't allowed since you have insufficient permissions on 1 or more child documents. Relate copied items to original diff --git a/src/Umbraco.Web.UI/umbraco_client/tags/css/jquery.tagsinput.css b/src/Umbraco.Web.UI/umbraco_client/tags/css/jquery.tagsinput.css index c595e249f9..3af1d36e55 100644 --- a/src/Umbraco.Web.UI/umbraco_client/tags/css/jquery.tagsinput.css +++ b/src/Umbraco.Web.UI/umbraco_client/tags/css/jquery.tagsinput.css @@ -1,3 +1,8 @@ +/* From jquery-ui.custom.css */ +.ui-autocomplete { position: absolute; cursor: default; } +/* workarounds */ +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ + div.tagsinput { border:1px solid #CCC; background: #FFF; padding:5px; width:300px; height:100px; overflow-y: auto;} div.tagsinput span.tag { border: 1px solid #a5d24a; -moz-border-radius:2px; -webkit-border-radius:2px; display: block; float: left; padding: 5px; text-decoration:none; background: #cde69c; color: #638421; margin-right: 5px; margin-bottom:5px;font-family: helvetica; font-size:13px;} div.tagsinput span.tag a { font-weight: bold; color: #82ad2b; text-decoration:none; font-size: 11px; } diff --git a/src/Umbraco.Web.UI/web.Template.config b/src/Umbraco.Web.UI/web.Template.config index ede0ce13bd..db9007f728 100644 --- a/src/Umbraco.Web.UI/web.Template.config +++ b/src/Umbraco.Web.UI/web.Template.config @@ -54,7 +54,7 @@ - + diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs index 589ca60e59..b7a9ad1128 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs +++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs @@ -10,6 +10,7 @@ using umbraco.cms.businesslogic; using umbraco.cms.businesslogic.member; using System.Linq; using umbraco.cms.businesslogic.web; +using Content = Umbraco.Core.Models.Content; using ApplicationTree = Umbraco.Core.Models.ApplicationTree; using Macro = umbraco.cms.businesslogic.macro.Macro; using Template = umbraco.cms.businesslogic.template.Template; @@ -124,8 +125,46 @@ namespace Umbraco.Web.Cache MediaService.Deleting += MediaServiceDeleting; MediaService.Moving += MediaServiceMoving; MediaService.Trashing += MediaServiceTrashing; + + ContentService.Created += ContentServiceCreated; + ContentService.Copied += ContentServiceCopied; } + #region Content service event handlers + + /// + /// When an entity is copied new permissions may be assigned to it based on it's parent, if that is the + /// case then we need to clear all user permissions cache. + /// + /// + /// + static void ContentServiceCopied(IContentService sender, Core.Events.CopyEventArgs e) + { + //check if permissions have changed + var permissionsChanged = ((Content)e.Copy).WasPropertyDirty("PermissionsChanged"); + if (permissionsChanged) + { + DistributedCache.Instance.RefreshAllUserPermissionsCache(); + } + } + + /// + /// When an entity is created new permissions may be assigned to it based on it's parent, if that is the + /// case then we need to clear all user permissions cache. + /// + /// + /// + static void ContentServiceCreated(IContentService sender, Core.Events.NewEventArgs e) + { + //check if permissions have changed + var permissionsChanged = ((Content)e.Entity).WasPropertyDirty("PermissionsChanged"); + if (permissionsChanged) + { + DistributedCache.Instance.RefreshAllUserPermissionsCache(); + } + } + #endregion + #region ApplicationTree event handlers static void ApplicationTreeNew(ApplicationTree sender, System.EventArgs e) { @@ -397,15 +436,15 @@ namespace Umbraco.Web.Cache { if (sender.User != null) { - DistributedCache.Instance.RefreshUserCache(sender.User.Id); + DistributedCache.Instance.RefreshUserPermissionsCache(sender.User.Id); } - if (sender.UserId > -1) + else if (sender.UserId > -1) { - DistributedCache.Instance.RefreshUserCache(sender.UserId); + DistributedCache.Instance.RefreshUserPermissionsCache(sender.UserId); } - if (sender.NodeIds.Any()) + else if (sender.NodeIds.Any()) { - DistributedCache.Instance.RefreshAllUserCache(); + DistributedCache.Instance.RefreshAllUserPermissionsCache(); } } diff --git a/src/Umbraco.Web/Cache/DistributedCache.cs b/src/Umbraco.Web/Cache/DistributedCache.cs index a0ce50d4c7..1f7307b289 100644 --- a/src/Umbraco.Web/Cache/DistributedCache.cs +++ b/src/Umbraco.Web/Cache/DistributedCache.cs @@ -43,6 +43,7 @@ namespace Umbraco.Web.Cache public const string MediaCacheRefresherId = "B29286DD-2D40-4DDB-B325-681226589FEC"; public const string MacroCacheRefresherId = "7B1E683C-5F34-43dd-803D-9699EA1E98CA"; public const string UserCacheRefresherId = "E057AF6D-2EE6-41F4-8045-3694010F0AA6"; + public const string UserPermissionsCacheRefresherId = "840AB9C5-5C0B-48DB-A77E-29FE4B80CD3A"; public const string UserTypeCacheRefresherId = "7E707E21-0195-4522-9A3C-658CC1761BD4"; public const string ContentTypeCacheRefresherId = "6902E22C-9C10-483C-91F3-66B7CAE9E2F5"; public const string LanguageCacheRefresherId = "3E0F95D8-0BE5-44B8-8394-2B8750B62654"; diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs index 5e46927d57..274705439e 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs @@ -61,6 +61,23 @@ namespace Umbraco.Web.Cache } #endregion + #region User permissions cache + public static void RemoveUserPermissionsCache(this DistributedCache dc, int userId) + { + dc.Remove(new Guid(DistributedCache.UserPermissionsCacheRefresherId), userId); + } + + public static void RefreshUserPermissionsCache(this DistributedCache dc, int userId) + { + dc.Refresh(new Guid(DistributedCache.UserPermissionsCacheRefresherId), userId); + } + + public static void RefreshAllUserPermissionsCache(this DistributedCache dc) + { + dc.RefreshAll(new Guid(DistributedCache.UserPermissionsCacheRefresherId)); + } + #endregion + #region Template cache /// /// Refreshes the cache amongst servers for a template diff --git a/src/Umbraco.Web/Cache/UserCacheRefresher.cs b/src/Umbraco.Web/Cache/UserCacheRefresher.cs index 0e4e32bc48..c80e8ed9b7 100644 --- a/src/Umbraco.Web/Cache/UserCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UserCacheRefresher.cs @@ -28,6 +28,7 @@ namespace Umbraco.Web.Cache public override void RefreshAll() { ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserCacheKey); + ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserPermissionsCacheKey); ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserContextCacheKey); } @@ -39,6 +40,7 @@ namespace Umbraco.Web.Cache public override void Remove(int id) { ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserCacheKey, id)); + ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id)); //we need to clear all UserContextCacheKey since we cannot invalidate based on ID since the cache is done so based //on the current contextId stored in the database diff --git a/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs b/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs new file mode 100644 index 0000000000..fdafdedb09 --- /dev/null +++ b/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs @@ -0,0 +1,47 @@ +using System; +using Umbraco.Core; +using Umbraco.Core.Cache; + +namespace Umbraco.Web.Cache +{ + /// + /// Used only to invalidate the user permissions cache + /// + /// + /// The UserCacheRefresher will also clear a user's permissions cache, this refresher is for invalidating only permissions + /// for users/content, not the users themselves. + /// + public sealed class UserPermissionsCacheRefresher : CacheRefresherBase + { + protected override UserPermissionsCacheRefresher Instance + { + get { return this; } + } + + public override Guid UniqueIdentifier + { + get { return Guid.Parse(DistributedCache.UserPermissionsCacheRefresherId); } + } + + + public override string Name + { + get { return "User permissions cache refresher"; } + } + + public override void RefreshAll() + { + ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserPermissionsCacheKey); + } + + public override void Refresh(int id) + { + Remove(id); + } + + public override void Remove(int id) + { + ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id)); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/UmbracoMvcHandler.cs b/src/Umbraco.Web/Mvc/UmbracoMvcHandler.cs index 1ace297bfd..649ac54f27 100644 --- a/src/Umbraco.Web/Mvc/UmbracoMvcHandler.cs +++ b/src/Umbraco.Web/Mvc/UmbracoMvcHandler.cs @@ -1,5 +1,7 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Web; @@ -38,6 +40,15 @@ namespace Umbraco.Web.Mvc routeDef.Controller = controller; } + /// + /// This is used internally purely to render an Umbraco MVC template to string and shouldn't be used for anything else. + /// + internal void ExecuteUmbracoRequest() + { + StoreControllerInRouteDefinition(); + base.ProcessRequest(RequestContext.HttpContext); + } + protected override void ProcessRequest(HttpContextBase httpContext) { StoreControllerInRouteDefinition(); @@ -54,4 +65,6 @@ namespace Umbraco.Web.Mvc return base.BeginProcessRequest(httpContext, callback, state); } } + + } \ No newline at end of file diff --git a/src/Umbraco.Web/Standalone/StandaloneHttpContext.cs b/src/Umbraco.Web/Standalone/StandaloneHttpContext.cs index 4cf64a8adf..55339e97b6 100644 --- a/src/Umbraco.Web/Standalone/StandaloneHttpContext.cs +++ b/src/Umbraco.Web/Standalone/StandaloneHttpContext.cs @@ -1,5 +1,7 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Web; @@ -11,11 +13,59 @@ namespace Umbraco.Web.Standalone /// internal class StandaloneHttpContext : HttpContextBase { + private readonly string _url; + private readonly HttpSessionStateBase _session = new StandaloneHttpSessionState(); + private readonly HttpResponseBase _response; + private readonly HttpRequestBase _request = new StandaloneHttpRequest(); + private readonly TextWriter _writer = new StringWriter(); + private readonly IDictionary _items = new Dictionary(); + + public StandaloneHttpContext() + { + //create a custom response with a custom writer. + _response = new HttpResponseWrapper(new HttpResponse(_writer)); + } + + public StandaloneHttpContext(string url) + : this() + { + if (url == null) throw new ArgumentNullException("url"); + _url = url; + _request = new HttpRequestWrapper(new HttpRequest("", _url, "")); + } + + // fixme - what shall we implement here? + + public override IDictionary Items + { + get { return _items; } + } + + public override HttpSessionStateBase Session + { + get { return _session; } + } public override HttpRequestBase Request { - get { return null; } + get { return _request; } } + + public override HttpResponseBase Response + { + get { return _response; } + } + + } + + internal class StandaloneHttpSessionState : HttpSessionStateBase + { + + } + + internal class StandaloneHttpRequest : HttpRequestBase + { + } } diff --git a/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterPublish.cs b/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterPublish.cs index f76198d2c3..1371c6fdb9 100644 --- a/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterPublish.cs +++ b/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterPublish.cs @@ -8,6 +8,8 @@ using Umbraco.Web.Cache; namespace Umbraco.Web.Strategies.Publishing { + //TODO: I think we should move this logic into the CacheRefresherEventHandler since all other handlers are registered there for invalidating cache. + /// /// Represents the UpdateCacheAfterPublish class, which subscribes to the Published event /// of the class and is responsible for doing the actual diff --git a/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterUnPublish.cs b/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterUnPublish.cs index 7af7724c7e..39ca0beda3 100644 --- a/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterUnPublish.cs +++ b/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterUnPublish.cs @@ -10,6 +10,8 @@ using Umbraco.Web.Cache; namespace Umbraco.Web.Strategies.Publishing { + //TODO: I think we should move this logic into the CacheRefresherEventHandler since all other handlers are registered there for invalidating cache. + /// /// Represents the UpdateCacheAfterUnPublish class, which subscribes to the UnPublished event /// of the class and is responsible for doing the actual diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index fe60c6959e..a13ed031f8 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -137,12 +137,9 @@ namespace Umbraco.Web.Templates requestContext.RouteData.Values.Add("controller", routeDef.ControllerName); //add the rest of the required route data routeHandler.SetupRouteDataForRequest(renderModel, requestContext, contentRequest); - //create and assign the controller context - routeDef.Controller.ControllerContext = new ControllerContext(requestContext, routeDef.Controller); - //render as string - var stringOutput = routeDef.Controller.RenderViewToString( - routeDef.ActionName, - renderModel); + + var stringOutput = RenderUmbracoRequestToString(requestContext); + sw.Write(stringOutput); break; case RenderingEngine.WebForms: @@ -157,6 +154,33 @@ namespace Umbraco.Web.Templates } + /// + /// This will execute the UmbracoMvcHandler for the request specified and get the string output. + /// + /// + /// Assumes the RequestContext is setup specifically to render an Umbraco view. + /// + /// + /// + /// To acheive this we temporarily change the output text writer of the current HttpResponse, then + /// execute the controller via the handler which innevitably writes the result to the text writer + /// that has been assigned to the response. Then we change the response textwriter back to the original + /// before continuing . + /// + private string RenderUmbracoRequestToString(RequestContext requestContext) + { + var currentWriter = requestContext.HttpContext.Response.Output; + var newWriter = new StringWriter(); + requestContext.HttpContext.Response.Output = newWriter; + + var handler = new UmbracoMvcHandler(requestContext); + handler.ExecuteUmbracoRequest(); + + //reset it + requestContext.HttpContext.Response.Output = currentWriter; + return newWriter.ToString(); + } + private void SetNewItemsOnContextObjects(PublishedContentRequest contentRequest) { // handlers like default.aspx will want it and most macros currently need it diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 774128138b..9f7087ceea 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -98,9 +98,9 @@ ..\packages\AutoMapper.2.2.1\lib\net40\AutoMapper.dll - + False - ..\packages\ClientDependency.1.7.0.2\lib\ClientDependency.Core.dll + ..\packages\ClientDependency.1.7.0.3\lib\ClientDependency.Core.dll False @@ -110,9 +110,9 @@ False ..\packages\Examine.0.1.51.2941\lib\Examine.dll - + False - ..\packages\HtmlAgilityPack.1.4.5\lib\Net40\HtmlAgilityPack.dll + ..\packages\HtmlAgilityPack.1.4.6\lib\Net40\HtmlAgilityPack.dll False @@ -169,7 +169,7 @@ ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll - ..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll + ..\packages\Microsoft.AspNet.WebApi.Client.4.0.30506.0\lib\net40\System.Net.Http.Formatting.dll ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll @@ -186,28 +186,28 @@ True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll - ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll + ..\packages\Microsoft.AspNet.WebApi.Core.4.0.30506.0\lib\net40\System.Web.Http.dll - ..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.20710.0\lib\net40\System.Web.Http.WebHost.dll + ..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.30506.0\lib\net40\System.Web.Http.WebHost.dll True - ..\packages\Microsoft.AspNet.Mvc.4.0.20710.0\lib\net40\System.Web.Mvc.dll + ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll True - ..\packages\Microsoft.AspNet.Razor.2.0.20715.0\lib\net40\System.Web.Razor.dll + ..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll System.Web.Services True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll True @@ -215,11 +215,11 @@ True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll @@ -293,6 +293,7 @@ + @@ -438,6 +439,7 @@ + ASPXCodeBehind diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index 9f5ddf7b61..10ff3eb8d8 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -1,19 +1,19 @@  - + - + - + - - - - - - + + + + + + diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/ActionHandlers/SimilarNodeNameComparer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/ActionHandlers/SimilarNodeNameComparer.cs new file mode 100644 index 0000000000..148d395524 --- /dev/null +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/ActionHandlers/SimilarNodeNameComparer.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; + +namespace umbraco.ActionHandlers +{ + /// + /// Comparer that takes into account the duplicate index of a node name + /// This is needed as a normal alphabetic sort would go Page (1), Page (10), Page (2) etc. + /// + [Obsolete("This class is no longer used and will be removed from the codebase in future versions")] + public class SimilarNodeNameComparer : IComparer + { + public int Compare(string x, string y) + { + if (x.LastIndexOf(')') == x.Length - 1 && y.LastIndexOf(')') == y.Length - 1) + { + if (x.ToLower().Substring(0, x.LastIndexOf('(')) == y.ToLower().Substring(0, y.LastIndexOf('('))) + { + int xDuplicateIndex = ExtractDuplicateIndex(x); + int yDuplicateIndex = ExtractDuplicateIndex(y); + + if (xDuplicateIndex != 0 && yDuplicateIndex != 0) + { + return xDuplicateIndex.CompareTo(yDuplicateIndex); + } + } + } + return x.ToLower().CompareTo(y.ToLower()); + } + + private int ExtractDuplicateIndex(string text) + { + int index = 0; + + if (text.LastIndexOf('(') != -1 && text.LastIndexOf('(') < text.Length - 2) + { + int startPos = text.LastIndexOf('(') + 1; + int length = text.Length - 1 - startPos; + + int.TryParse(text.Substring(startPos, length), out index); + } + + return index; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/ActionHandlers/umbEnsureUniqueName.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/ActionHandlers/umbEnsureUniqueName.cs index baacbff4f6..d5fab83940 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/ActionHandlers/umbEnsureUniqueName.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/ActionHandlers/umbEnsureUniqueName.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using Umbraco.Core.Logging; using umbraco.cms.businesslogic.web; @@ -89,44 +88,4 @@ namespace umbraco.ActionHandlers #endregion } - - /// - /// Comparer that takes into account the duplicate index of a node name - /// This is needed as a normal alphabetic sort would go Page (1), Page (10), Page (2) etc. - /// - public class SimilarNodeNameComparer : IComparer - { - public int Compare(string x, string y) - { - if (x.LastIndexOf(')') == x.Length - 1 && y.LastIndexOf(')') == y.Length - 1) - { - if (x.ToLower().Substring(0, x.LastIndexOf('(')) == y.ToLower().Substring(0, y.LastIndexOf('('))) - { - int xDuplicateIndex = ExtractDuplicateIndex(x); - int yDuplicateIndex = ExtractDuplicateIndex(y); - - if (xDuplicateIndex != 0 && yDuplicateIndex != 0) - { - return xDuplicateIndex.CompareTo(yDuplicateIndex); - } - } - } - return x.ToLower().CompareTo(y.ToLower()); - } - - private int ExtractDuplicateIndex(string text) - { - int index = 0; - - if (text.LastIndexOf('(') != -1 && text.LastIndexOf('(') < text.Length - 2) - { - int startPos = text.LastIndexOf('(') + 1; - int length = text.Length - 1 - startPos; - - int.TryParse(text.Substring(startPos, length), out index); - } - - return index; - } - } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/content.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/content.ascx.cs index 14347b5c04..91b2a8c519 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/content.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/content.ascx.cs @@ -3,12 +3,12 @@ using System.Linq; using System.Text; using System.Web.UI; using System.Web.UI.WebControls; +using Umbraco.Core.IO; using umbraco.cms.businesslogic.web; using umbraco.presentation.create; using Content=umbraco.cms.businesslogic.Content; using umbraco.cms.helpers; using umbraco.BasePages; -using umbraco.IO; namespace umbraco.cms.presentation.create.controls { @@ -19,48 +19,48 @@ namespace umbraco.cms.presentation.create.controls { protected void Page_Load(object sender, EventArgs e) { - if (!IsPostBack) + if (IsPostBack == false) { sbmt.Text = ui.Text("create"); - int NodeId = int.Parse(Request["nodeID"]); + var nodeId = int.Parse(Request["nodeID"]); - int[] allowedIds = new int[0]; - if (NodeId > 0) + var allowedIds = new int[0]; + if (nodeId > 0) { - Content c = new Document(NodeId); + var c = new Document(nodeId); allowedIds = c.ContentType.AllowedChildContentTypeIDs; } nodeType.Attributes.Add("onChange", "document.getElementById('typeDescription').innerHTML = typeInfo[this.selectedIndex];"); - int counter = 0; - bool typeInited = false; - StringBuilder js = new StringBuilder(); + var counter = 0; + var typeInited = false; + var js = new StringBuilder(); var documentTypeList = DocumentType.GetAllAsList().ToList(); - foreach (DocumentType dt in documentTypeList) + foreach (var dt in documentTypeList) { string docDescription = "No description available..."; - if (dt.Description != null && dt.Description != "") + if (string.IsNullOrEmpty(dt.Description) == false) docDescription = dt.Description; docDescription = "" + dt.Text + "
" + docDescription.Replace(Environment.NewLine, "
"); docDescription = docDescription.Replace("'", "\\'"); - string docImage = (dt.Thumbnail != "") ? dt.Thumbnail : "../nada.gif"; + var docImage = (dt.Thumbnail != "") ? dt.Thumbnail : "../nada.gif"; docImage = IOHelper.ResolveUrl( SystemDirectories.Umbraco ) + "/images/thumbnails/" + docImage; - ListItem li = new ListItem(); + var li = new ListItem(); li.Text = dt.Text; li.Value = dt.Id.ToString(); - if (NodeId > 0) + if (nodeId > 0) { - foreach (int i in allowedIds) if (i == dt.Id) + foreach (var i in allowedIds) if (i == dt.Id) { nodeType.Items.Add(li); js.Append("typeInfo[" + counter + "] = '

" + docDescription + "

'\n"); - if (!typeInited) + if (typeInited == false) { descr.Text = "

" + docDescription + "

"; @@ -76,7 +76,7 @@ namespace umbraco.cms.presentation.create.controls nodeType.Items.Add(li); js.Append("typeInfo[" + counter + "] = '

" + docDescription + "

'\n"); - if (!typeInited) + if (typeInited == false) { descr.Text = "

" + docDescription + "

'"; @@ -94,34 +94,14 @@ namespace umbraco.cms.presentation.create.controls } } - #region Web Form Designer generated code - - protected override void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - } - - #endregion - + protected void sbmt_Click(object sender, EventArgs e) { - doCreation(); + DoCreation(); } - private void doCreation() + private void DoCreation() { if (Page.IsValid) { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/contentTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/contentTasks.cs index a0c6c616fb..45cc4a737c 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/contentTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/contentTasks.cs @@ -13,14 +13,14 @@ namespace umbraco { private string _alias; - private int _parentID; - private int _typeID; - private int _userID; + private int _parentId; + private int _typeId; + private int _userId; private string _returnUrl = ""; public int UserId { - set { _userID = value; } + set { _userId = value; } } public string ReturnUrl @@ -30,8 +30,8 @@ namespace umbraco public int TypeID { - set { _typeID = value; } - get { return _typeID; } + set { _typeId = value; } + get { return _typeId; } } public string Alias @@ -44,18 +44,18 @@ namespace umbraco { set { - _parentID = value; + _parentId = value; } get { - return _parentID; + return _parentId; } } public bool Save() { - cms.businesslogic.web.DocumentType dt = new cms.businesslogic.web.DocumentType(TypeID); - cms.businesslogic.web.Document d = cms.businesslogic.web.Document.MakeNew(Alias, dt, BusinessLogic.User.GetUser(_userID), ParentID); + var dt = new cms.businesslogic.web.DocumentType(TypeID); + var d = cms.businesslogic.web.Document.MakeNew(Alias, dt, User.GetUser(_userId), ParentID); if (d == null) { //TODO: Slace - Fix this to use the language files @@ -71,13 +71,13 @@ namespace umbraco public bool Delete() { - cms.businesslogic.web.Document d = new cms.businesslogic.web.Document(ParentID); - - // Log - BusinessLogic.Log.Add(BusinessLogic.LogTypes.Delete, User.GetCurrent(), d.Id, ""); + var d = new cms.businesslogic.web.Document(ParentID); d.delete(); + // Log + Log.Add(LogTypes.Delete, User.GetCurrent(), d.Id, ""); + return true; } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/create.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/create.aspx.cs index ca384bd3af..7bc9354816 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/create.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/create.aspx.cs @@ -9,8 +9,8 @@ using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; using System.Xml; +using Umbraco.Core.IO; using umbraco.cms.businesslogic; -using umbraco.IO; using umbraco.presentation; using umbraco.BusinessLogic.Actions; using umbraco.BasePages; @@ -44,19 +44,19 @@ namespace umbraco.dialogs if (helper.Request("app") == Constants.Applications.Media || CheckCreatePermissions(nodeId)) { //pane_chooseName.Text = ui.Text("create", "updateData", this.getUser()); - cms.businesslogic.CMSNode c = new cms.businesslogic.CMSNode(nodeId); + var c = new CMSNode(nodeId); path.Value = c.Path; pane_chooseNode.Visible = false; panel_buttons.Visible = false; pane_chooseName.Visible = true; - XmlDocument createDef = new XmlDocument(); - XmlTextReader defReader = new XmlTextReader(Server.MapPath(umbraco.IO.IOHelper.ResolveUrl(umbraco.IO.SystemDirectories.Umbraco) + "/config/create/UI.xml")); + var createDef = new XmlDocument(); + var defReader = new XmlTextReader(Server.MapPath(IOHelper.ResolveUrl(SystemDirectories.Umbraco) + "/config/create/UI.xml")); createDef.Load(defReader); defReader.Close(); // Find definition for current nodeType XmlNode def = createDef.SelectSingleNode("//nodeType [@alias = '" + Request.QueryString["app"] + "']"); - phCreate.Controls.Add(new UserControl().LoadControl(umbraco.IO.IOHelper.ResolveUrl(umbraco.IO.SystemDirectories.Umbraco) + def.SelectSingleNode("./usercontrol").FirstChild.Value)); + phCreate.Controls.Add(new UserControl().LoadControl(IOHelper.ResolveUrl(SystemDirectories.Umbraco) + def.SelectSingleNode("./usercontrol").FirstChild.Value)); } else { @@ -71,8 +71,8 @@ namespace umbraco.dialogs protected override void OnPreRender(EventArgs e) { base.OnPreRender(e); - ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference( IOHelper.ResolveUrl( SystemDirectories.Webservices) +"/cmsnode.asmx")); - ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference( IOHelper.ResolveUrl( SystemDirectories.Webservices) +"/legacyAjaxCalls.asmx")); + ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference( IOHelper.ResolveUrl( SystemDirectories.WebServices) +"/cmsnode.asmx")); + ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference( IOHelper.ResolveUrl( SystemDirectories.WebServices) +"/legacyAjaxCalls.asmx")); } private bool CheckCreatePermissions(int nodeId) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/cruds.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/cruds.aspx.cs index cf570d93c1..7a96b7ae3e 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/cruds.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/cruds.aspx.cs @@ -35,13 +35,8 @@ namespace umbraco.dialogs // Put user code to initialize the page here } - #region Web Form Designer generated code override protected void OnInit(EventArgs e) { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); base.OnInit(e); node = new cms.businesslogic.CMSNode(int.Parse(helper.Request("id"))); @@ -99,15 +94,6 @@ namespace umbraco.dialogs PlaceHolder1.Controls.Add(ht); } - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - - } - #endregion protected void Button1_Click(object sender, System.EventArgs e) { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs index ba48e944fa..3cb48ce274 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs @@ -217,25 +217,52 @@ namespace umbraco.dialogs var nodeAllowed = false; IContentBase currContent; - IContentBase parentContent; - IContentTypeBase parentContentType; + IContentBase parentContent = null; + IContentTypeBase parentContentType = null; if (CurrentApp == "content") { currContent = Services.ContentService.GetById(Request.GetItemAs("id")); - parentContent = Services.ContentService.GetById(Request.GetItemAs("copyTo")); - parentContentType = Services.ContentTypeService.GetContentType(parentContent.ContentTypeId); + if (Request.GetItemAs("copyTo") != -1) + { + parentContent = Services.ContentService.GetById(Request.GetItemAs("copyTo")); + if (parentContent != null) + { + parentContentType = Services.ContentTypeService.GetContentType(parentContent.ContentTypeId); + } + } } else { currContent = Services.MediaService.GetById(Request.GetItemAs("id")); - parentContent = Services.MediaService.GetById(Request.GetItemAs("copyTo")); - parentContentType = Services.ContentTypeService.GetMediaType(parentContent.ContentTypeId); + if (Request.GetItemAs("copyTo") != -1) + { + parentContent = Services.MediaService.GetById(Request.GetItemAs("copyTo")); + if (parentContent != null) + { + parentContentType = Services.ContentTypeService.GetMediaType(parentContent.ContentTypeId); + } + } } // Check on contenttypes - if (Request.GetItemAs("copyTo") == -1) + if (parentContentType == null) { - nodeAllowed = true; + //check if this is allowed at root + IContentTypeBase currContentType; + if (CurrentApp == "content") + { + currContentType = Services.ContentTypeService.GetContentType(currContent.ContentTypeId); + } + else + { + currContentType = Services.ContentTypeService.GetMediaType(currContent.ContentTypeId); + } + nodeAllowed = currContentType.AllowedAsRoot; + if (!nodeAllowed) + { + feedback.Text = ui.Text("moveOrCopy", "notAllowedAtRoot", UmbracoUser); + feedback.type = uicontrols.Feedback.feedbacktype.error; + } } else { @@ -268,7 +295,7 @@ namespace umbraco.dialogs pane_form_notice.Visible = false; panel_buttons.Visible = false; - var newNodeCaption = Request.GetItemAs("copyTo") == -1 + var newNodeCaption = parentContent == null ? ui.Text(CurrentApp) : parentContent.Name; @@ -290,19 +317,19 @@ namespace umbraco.dialogs feedback.type = uicontrols.Feedback.feedbacktype.success; // refresh tree - ClientTools.MoveNode(currContent.Id.ToString(), parentContent.Path); + ClientTools.MoveNode(currContent.Id.ToString(), currContent.Path); } else { //NOTE: We ONLY support Copy on content not media for some reason. - Services.ContentService.Copy((IContent)currContent, Request.GetItemAs("copyTo"), RelateDocuments.Checked, getUser().Id); + var newContent = Services.ContentService.Copy((IContent)currContent, Request.GetItemAs("copyTo"), RelateDocuments.Checked, getUser().Id); feedback.Text = ui.Text("moveOrCopy", "copyDone", nodes, getUser()) + "

" + ui.Text("closeThisWindow") + ""; feedback.type = uicontrols.Feedback.feedbacktype.success; // refresh tree - ClientTools.CopyNode(currContent.Id.ToString(), parentContent.Path); + ClientTools.CopyNode(currContent.Id.ToString(), newContent.Path); } } } diff --git a/src/umbraco.MacroEngines/packages.config b/src/umbraco.MacroEngines/packages.config index f76f93a114..023ebfc6b9 100644 --- a/src/umbraco.MacroEngines/packages.config +++ b/src/umbraco.MacroEngines/packages.config @@ -1,15 +1,15 @@  - + - - - - - - - + + + + + + + diff --git a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj index 07891470f2..d6fecdbaca 100644 --- a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj +++ b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj @@ -49,9 +49,9 @@ False ..\packages\Examine.0.1.51.2941\lib\Examine.dll - + False - ..\packages\HtmlAgilityPack.1.4.5\lib\Net40\HtmlAgilityPack.dll + ..\packages\HtmlAgilityPack.1.4.6\lib\Net40\HtmlAgilityPack.dll False @@ -77,7 +77,7 @@ ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll - ..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll + ..\packages\Microsoft.AspNet.WebApi.Client.4.0.30506.0\lib\net40\System.Net.Http.Formatting.dll ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll @@ -85,33 +85,33 @@ True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll - ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll + ..\packages\Microsoft.AspNet.WebApi.Core.4.0.30506.0\lib\net40\System.Web.Http.dll - ..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.20710.0\lib\net40\System.Web.Http.WebHost.dll + ..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.30506.0\lib\net40\System.Web.Http.WebHost.dll True - ..\packages\Microsoft.AspNet.Mvc.4.0.20710.0\lib\net40\System.Web.Mvc.dll + ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll True - ..\packages\Microsoft.AspNet.Razor.2.0.20715.0\lib\net40\System.Web.Razor.dll + ..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll diff --git a/src/umbraco.businesslogic/User.cs b/src/umbraco.businesslogic/User.cs index ae5c0202ab..5da83d2a6d 100644 --- a/src/umbraco.businesslogic/User.cs +++ b/src/umbraco.businesslogic/User.cs @@ -1,6 +1,8 @@ using System; using System.Collections; +using System.Web.Caching; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Logging; using umbraco.DataLayer; using System.Collections.Generic; @@ -27,9 +29,6 @@ namespace umbraco.BusinessLogic private bool _userDisabled; private bool _defaultToLiveEditing; - private Hashtable _cruds = new Hashtable(); - private bool _crudsInitialized = false; - private Hashtable _notifications = new Hashtable(); private bool _notificationsInitialized = false; @@ -667,52 +666,56 @@ namespace umbraco.BusinessLogic { if (!_isInitialized) setupUser(_id); - string cruds = UserType.DefaultPermissions; + string defaultPermissions = UserType.DefaultPermissions; - if (!_crudsInitialized) - initCruds(); + //get the cached permissions for the user + var cachedPermissions = ApplicationContext.Current.ApplicationCache.GetCacheItem( + string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, _id), + //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will make this priority below average + CacheItemPriority.BelowNormal, + null, + //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will only have this exist in cache for 20 minutes, + // then it will refresh from the database. + new TimeSpan(0, 20, 0), + () => + { + var cruds = new Hashtable(); + using (var dr = SqlHelper.ExecuteReader("select * from umbracoUser2NodePermission where userId = @userId order by nodeId", SqlHelper.CreateParameter("@userId", this.Id))) + { + while (dr.Read()) + { + if (!cruds.ContainsKey(dr.GetInt("nodeId"))) + { + cruds.Add(dr.GetInt("nodeId"), string.Empty); + } + cruds[dr.GetInt("nodeId")] += dr.GetString("permission"); + } + } + return cruds; + }); // NH 4.7.1 changing default permission behavior to default to User Type permissions IF no specific permissions has been // set for the current node - int nodeId = Path.Contains(",") ? int.Parse(Path.Substring(Path.LastIndexOf(",")+1)) : int.Parse(Path); - if (_cruds.ContainsKey(nodeId)) + var nodeId = Path.Contains(",") ? int.Parse(Path.Substring(Path.LastIndexOf(",", StringComparison.Ordinal)+1)) : int.Parse(Path); + if (cachedPermissions.ContainsKey(nodeId)) { - return _cruds[int.Parse(Path.Substring(Path.LastIndexOf(",")+1))].ToString(); + return cachedPermissions[int.Parse(Path.Substring(Path.LastIndexOf(",", StringComparison.Ordinal) + 1))].ToString(); } // exception to everything. If default cruds is empty and we're on root node; allow browse of root node - if (String.IsNullOrEmpty(cruds) && Path == "-1") - cruds = "F"; + if (string.IsNullOrEmpty(defaultPermissions) && Path == "-1") + defaultPermissions = "F"; // else return default user type cruds - return cruds; + return defaultPermissions; } ///

/// Initializes the user node permissions /// + [Obsolete("This method doesn't do anything whatsoever and will be removed in future versions")] public void initCruds() - { - if (!_isInitialized) - setupUser(_id); - - // clear cruds - System.Web.HttpContext.Current.Application.Lock(); - _cruds.Clear(); - System.Web.HttpContext.Current.Application.UnLock(); - - using (IRecordsReader dr = SqlHelper.ExecuteReader("select * from umbracoUser2NodePermission where userId = @userId order by nodeId", SqlHelper.CreateParameter("@userId", this.Id))) - { - // int currentId = -1; - while (dr.Read()) - { - if (!_cruds.ContainsKey(dr.GetInt("nodeId"))) - _cruds.Add(dr.GetInt("nodeId"), String.Empty); - - _cruds[dr.GetInt("nodeId")] += dr.GetString("permission"); - } - } - _crudsInitialized = true; + { } /// @@ -904,7 +907,7 @@ namespace umbraco.BusinessLogic public void FlushFromCache() { OnFlushingFromCache(EventArgs.Empty); - ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("UmbracoUser{0}", Id.ToString())); + ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserCacheKey, Id.ToString())); } /// @@ -915,7 +918,7 @@ namespace umbraco.BusinessLogic public static User GetUser(int id) { return ApplicationContext.Current.ApplicationCache.GetCacheItem( - string.Format("UmbracoUser{0}", id.ToString()), () => + string.Format("{0}{1}", CacheKeys.UserCacheKey, id.ToString()), () => { try { diff --git a/src/umbraco.businesslogic/packages.config b/src/umbraco.businesslogic/packages.config index 9febdbd940..02e046e7cb 100644 --- a/src/umbraco.businesslogic/packages.config +++ b/src/umbraco.businesslogic/packages.config @@ -1,8 +1,8 @@  - - - + + + \ No newline at end of file diff --git a/src/umbraco.businesslogic/umbraco.businesslogic.csproj b/src/umbraco.businesslogic/umbraco.businesslogic.csproj index 32127a6b3a..5eb81794fc 100644 --- a/src/umbraco.businesslogic/umbraco.businesslogic.csproj +++ b/src/umbraco.businesslogic/umbraco.businesslogic.csproj @@ -128,27 +128,27 @@ True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll True - ..\packages\Microsoft.AspNet.Mvc.4.0.20710.0\lib\net40\System.Web.Mvc.dll + ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll True - ..\packages\Microsoft.AspNet.Razor.2.0.20715.0\lib\net40\System.Web.Razor.dll + ..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll True - ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll System.XML diff --git a/src/umbraco.cms/Actions/ActionNew.cs b/src/umbraco.cms/Actions/ActionNew.cs index 303baa3e35..ff17c91a0b 100644 --- a/src/umbraco.cms/Actions/ActionNew.cs +++ b/src/umbraco.cms/Actions/ActionNew.cs @@ -10,7 +10,7 @@ namespace umbraco.BusinessLogic.Actions public class ActionNew : IAction { //create singleton - private static readonly ActionNew InternalInstance = new ActionNew(); + private static readonly ActionNew InnerInstance = new ActionNew(); /// /// A public constructor exists ONLY for backwards compatibility in regards to 3rd party add-ons. @@ -22,7 +22,7 @@ namespace umbraco.BusinessLogic.Actions public static ActionNew Instance { - get { return InternalInstance; } + get { return InnerInstance; } } #region IAction Members diff --git a/src/umbraco.cms/businesslogic/CMSNode.cs b/src/umbraco.cms/businesslogic/CMSNode.cs index 76455c1361..ba8a6ccc35 100644 --- a/src/umbraco.cms/businesslogic/CMSNode.cs +++ b/src/umbraco.cms/businesslogic/CMSNode.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Threading; using System.Xml; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence.Caching; @@ -55,12 +57,13 @@ namespace umbraco.cms.businesslogic #region Private static - private static readonly string m_DefaultIconCssFile = IOHelper.MapPath(SystemDirectories.Umbraco_client + "/Tree/treeIcons.css"); - private static List m_DefaultIconClasses = new List(); + private static readonly string DefaultIconCssFile = IOHelper.MapPath(SystemDirectories.Umbraco_client + "/Tree/treeIcons.css"); + private static readonly List InternalDefaultIconClasses = new List(); + private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(); - private static void initializeIconClasses() + private static void InitializeIconClasses() { - StreamReader re = File.OpenText(m_DefaultIconCssFile); + StreamReader re = File.OpenText(DefaultIconCssFile); string content = string.Empty; string input = null; while ((input = re.ReadLine()) != null) @@ -70,20 +73,20 @@ namespace umbraco.cms.businesslogic re.Close(); // parse the classes - MatchCollection m = Regex.Matches(content, "([^{]*){([^}]*)}", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + var m = Regex.Matches(content, "([^{]*){([^}]*)}", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); foreach (Match match in m) { - GroupCollection groups = match.Groups; - string cssClass = groups[1].Value.Replace("\n", "").Replace("\r", "").Trim().Trim(Environment.NewLine.ToCharArray()); - if (!String.IsNullOrEmpty(cssClass)) + var groups = match.Groups; + var cssClass = groups[1].Value.Replace("\n", "").Replace("\r", "").Trim().Trim(Environment.NewLine.ToCharArray()); + if (string.IsNullOrEmpty(cssClass) == false) { - m_DefaultIconClasses.Add(cssClass); + InternalDefaultIconClasses.Add(cssClass); } } } - private const string m_SQLSingle = "SELECT id, createDate, trashed, parentId, nodeObjectType, nodeUser, level, path, sortOrder, uniqueID, text FROM umbracoNode WHERE id = @id"; - private const string m_SQLDescendants = @" + private const string SqlSingle = "SELECT id, createDate, trashed, parentId, nodeObjectType, nodeUser, level, path, sortOrder, uniqueID, text FROM umbracoNode WHERE id = @id"; + private const string SqlDescendants = @" SELECT id, createDate, trashed, parentId, nodeObjectType, nodeUser, level, path, sortOrder, uniqueID, text FROM umbracoNode WHERE path LIKE '%,{0},%'"; @@ -137,10 +140,16 @@ namespace umbraco.cms.businesslogic { get { - if (m_DefaultIconClasses.Count == 0) - initializeIconClasses(); + using (var l = new UpgradeableReadLock(Locker)) + { + if (InternalDefaultIconClasses.Count == 0) + { + l.UpgradeToWriteLock(); + InitializeIconClasses(); + } + return InternalDefaultIconClasses; + } - return m_DefaultIconClasses; } } @@ -309,7 +318,7 @@ namespace umbraco.cms.businesslogic return sortOrder; } - + /// /// Retrieve a list of the id's of all CMSNodes given the objecttype and the first letter of the name. /// @@ -519,9 +528,9 @@ order by level,sortOrder"; SqlHelper.CreateParameter("@parentId", newParent.Id)); this.Parent = newParent; - this.sortOrder = maxSortOrder + 1; + this.sortOrder = maxSortOrder + 1; } - + //detect if we have moved, then update the level and path // issue: http://issues.umbraco.org/issue/U4-1579 if (this.Path != newParent.Path + "," + this.Id.ToString()) @@ -556,7 +565,7 @@ order by level,sortOrder"; //regenerate the xml for the newParent node var parentDocument = new Document(newParent.Id); parentDocument.XmlGenerate(new XmlDocument()); - + } else if (!IsTrashed && newParent.nodeObjectType == Media._objectType) { @@ -661,7 +670,7 @@ order by level,sortOrder"; public virtual IEnumerable GetDescendants() { var descendants = new List(); - using (IRecordsReader dr = SqlHelper.ExecuteReader(string.Format(m_SQLDescendants, Id))) + using (IRecordsReader dr = SqlHelper.ExecuteReader(string.Format(SqlDescendants, Id))) { while (dr.Read()) { @@ -998,7 +1007,7 @@ order by level,sortOrder"; /// protected virtual void setupNode() { - using (IRecordsReader dr = SqlHelper.ExecuteReader(m_SQLSingle, + using (IRecordsReader dr = SqlHelper.ExecuteReader(SqlSingle, SqlHelper.CreateParameter("@id", this.Id))) { if (dr.Read()) @@ -1187,7 +1196,7 @@ order by level,sortOrder"; } /// - /// Occurs after a node is saved. + /// Occurs after a node is saved. /// public static event EventHandler AfterSave; diff --git a/src/umbraco.cms/businesslogic/media/Media.cs b/src/umbraco.cms/businesslogic/media/Media.cs index 252d8cd799..6dcff5792f 100644 --- a/src/umbraco.cms/businesslogic/media/Media.cs +++ b/src/umbraco.cms/businesslogic/media/Media.cs @@ -92,12 +92,11 @@ namespace umbraco.cms.businesslogic.media return null; } - var media = ApplicationContext.Current.Services.MediaService.CreateMedia(Name, ParentId, dct.Alias, u.Id); + var media = ApplicationContext.Current.Services.MediaService.CreateMediaWithIdentity(Name, ParentId, dct.Alias, u.Id); //The media object will only have the 'WasCancelled' flag set to 'True' if the 'Creating' event has been cancelled if (((Entity)media).WasCancelled) return null; - ApplicationContext.Current.Services.MediaService.Save(media); var tmp = new Media(media); tmp.OnNew(e); diff --git a/src/umbraco.cms/businesslogic/web/Document.cs b/src/umbraco.cms/businesslogic/web/Document.cs index 6be26cfa77..2df9e19f37 100644 --- a/src/umbraco.cms/businesslogic/web/Document.cs +++ b/src/umbraco.cms/businesslogic/web/Document.cs @@ -306,14 +306,11 @@ namespace umbraco.cms.businesslogic.web } //Create a new IContent object based on the passed in DocumentType's alias, set the name and save it - IContent content = ApplicationContext.Current.Services.ContentService.CreateContent(Name, ParentId, dct.Alias, u.Id); + IContent content = ApplicationContext.Current.Services.ContentService.CreateContentWithIdentity(Name, ParentId, dct.Alias, u.Id); //The content object will only have the 'WasCancelled' flag set to 'True' if the 'Creating' event has been cancelled, so we return null. if (((Entity)content).WasCancelled) return null; - //don't raise events here (false), they will get raised with the d.Save() call. - ApplicationContext.Current.Services.ContentService.Save(content, u.Id, false); - //read the whole object from the db Document d = new Document(content); diff --git a/src/umbraco.cms/businesslogic/web/Domain.cs b/src/umbraco.cms/businesslogic/web/Domain.cs index 83bbb850ac..f5bcb46090 100644 --- a/src/umbraco.cms/businesslogic/web/Domain.cs +++ b/src/umbraco.cms/businesslogic/web/Domain.cs @@ -138,12 +138,12 @@ namespace umbraco.cms.businesslogic.web #region Statics - internal static List GetDomains() + public static IEnumerable GetDomains() { return GetDomains(false); } - internal static List GetDomains(bool includeWildcards) + internal static IEnumerable GetDomains(bool includeWildcards) { var domains = ApplicationContext.Current.ApplicationCache.GetCacheItem( CacheKeys.DomainCacheKey, @@ -177,7 +177,7 @@ namespace umbraco.cms.businesslogic.web public static Domain GetDomain(string DomainName) { - return GetDomains().Find(delegate(Domain d) { return d.Name == DomainName; }); + return GetDomains().FirstOrDefault(d => d.Name == DomainName); } public static int GetRootFromDomain(string DomainName) @@ -189,7 +189,7 @@ namespace umbraco.cms.businesslogic.web public static Domain[] GetDomainsById(int nodeId) { - return GetDomains().FindAll(delegate(Domain d) { return d._root == nodeId; }).ToArray(); + return GetDomains().Where(d => d._root == nodeId).ToArray(); } public static bool Exists(string DomainName) diff --git a/src/umbraco.cms/packages.config b/src/umbraco.cms/packages.config index 06959d20f2..3a63559557 100644 --- a/src/umbraco.cms/packages.config +++ b/src/umbraco.cms/packages.config @@ -1,7 +1,7 @@  - - + + \ No newline at end of file diff --git a/src/umbraco.cms/umbraco.cms.csproj b/src/umbraco.cms/umbraco.cms.csproj index b0509a302b..3e2e8da4c2 100644 --- a/src/umbraco.cms/umbraco.cms.csproj +++ b/src/umbraco.cms/umbraco.cms.csproj @@ -106,13 +106,13 @@ false - + False - ..\packages\ClientDependency.1.7.0.2\lib\ClientDependency.Core.dll + ..\packages\ClientDependency.1.7.0.3\lib\ClientDependency.Core.dll - + False - ..\packages\HtmlAgilityPack.1.4.5\lib\Net40\HtmlAgilityPack.dll + ..\packages\HtmlAgilityPack.1.4.6\lib\Net40\HtmlAgilityPack.dll False diff --git a/src/umbraco.controls/DatePicker/DateTimePicker.cs b/src/umbraco.controls/DatePicker/DateTimePicker.cs index 5c453649e1..4df20fbb9d 100644 --- a/src/umbraco.controls/DatePicker/DateTimePicker.cs +++ b/src/umbraco.controls/DatePicker/DateTimePicker.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using ClientDependency.Core; using System.Web.UI; using System.Web.UI.WebControls; -using umbraco.IO; using System.Web.UI.HtmlControls; namespace umbraco.uicontrols.DatePicker diff --git a/src/umbraco.controls/packages.config b/src/umbraco.controls/packages.config index babc1d8a83..d30bbbe9c0 100644 --- a/src/umbraco.controls/packages.config +++ b/src/umbraco.controls/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/umbraco.controls/umbraco.controls.csproj b/src/umbraco.controls/umbraco.controls.csproj index c8ec886edf..520d173367 100644 --- a/src/umbraco.controls/umbraco.controls.csproj +++ b/src/umbraco.controls/umbraco.controls.csproj @@ -68,9 +68,9 @@ false - + False - ..\packages\ClientDependency.1.7.0.2\lib\ClientDependency.Core.dll + ..\packages\ClientDependency.1.7.0.3\lib\ClientDependency.Core.dll diff --git a/src/umbraco.datalayer/packages.config b/src/umbraco.datalayer/packages.config index 87b5ad1860..02379f5503 100644 --- a/src/umbraco.datalayer/packages.config +++ b/src/umbraco.datalayer/packages.config @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/src/umbraco.datalayer/umbraco.datalayer.csproj b/src/umbraco.datalayer/umbraco.datalayer.csproj index 7e8b055cbd..253e058e2b 100644 --- a/src/umbraco.datalayer/umbraco.datalayer.csproj +++ b/src/umbraco.datalayer/umbraco.datalayer.csproj @@ -76,8 +76,9 @@ False ..\packages\Microsoft.ApplicationBlocks.Data.1.0.1559.20655\lib\Microsoft.ApplicationBlocks.Data.dll - - ..\packages\MySql.Data.6.6.4\lib\net40\MySql.Data.dll + + False + ..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll diff --git a/src/umbraco.editorControls/packages.config b/src/umbraco.editorControls/packages.config index babc1d8a83..d30bbbe9c0 100644 --- a/src/umbraco.editorControls/packages.config +++ b/src/umbraco.editorControls/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/umbraco.editorControls/tags/DataEditor.cs b/src/umbraco.editorControls/tags/DataEditor.cs index 4281e1f12a..22c9fd423b 100644 --- a/src/umbraco.editorControls/tags/DataEditor.cs +++ b/src/umbraco.editorControls/tags/DataEditor.cs @@ -62,7 +62,6 @@ namespace umbraco.editorControls.tags base.OnInit(e); // register all dependencies - only registered once - ClientDependencyLoader.Instance.RegisterDependency("ui/ui-lightness/jquery-ui.custom.css", "UmbracoClient", ClientDependencyType.Css); ClientDependencyLoader.Instance.RegisterDependency("css/umbracoGui.css", "UmbracoRoot", ClientDependencyType.Css); ClientDependencyLoader.Instance.RegisterDependency("tags/css/jquery.tagsinput.css", "UmbracoClient", ClientDependencyType.Css); ClientDependencyLoader.Instance.RegisterDependency("tags/js/jquery.tagsinput.min.js", "UmbracoClient", ClientDependencyType.Javascript); diff --git a/src/umbraco.editorControls/umbraco.editorControls.csproj b/src/umbraco.editorControls/umbraco.editorControls.csproj index bd395aa192..c702e92e6a 100644 --- a/src/umbraco.editorControls/umbraco.editorControls.csproj +++ b/src/umbraco.editorControls/umbraco.editorControls.csproj @@ -114,9 +114,9 @@ {651E1350-91B6-44B7-BD60-7207006D7003} Umbraco.Web - + False - ..\packages\ClientDependency.1.7.0.2\lib\ClientDependency.Core.dll + ..\packages\ClientDependency.1.7.0.3\lib\ClientDependency.Core.dll System diff --git a/src/umbraco.macroRenderings/packages.config b/src/umbraco.macroRenderings/packages.config index 2a8355e1b9..c840bab970 100644 --- a/src/umbraco.macroRenderings/packages.config +++ b/src/umbraco.macroRenderings/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/umbraco.macroRenderings/umbraco.macroRenderings.csproj b/src/umbraco.macroRenderings/umbraco.macroRenderings.csproj index 4fb59d605d..7bb292dffd 100644 --- a/src/umbraco.macroRenderings/umbraco.macroRenderings.csproj +++ b/src/umbraco.macroRenderings/umbraco.macroRenderings.csproj @@ -106,9 +106,9 @@ false - + False - ..\packages\ClientDependency.1.7.0.2\lib\ClientDependency.Core.dll + ..\packages\ClientDependency.1.7.0.3\lib\ClientDependency.Core.dll False diff --git a/src/umbraco.sln b/src/umbraco.sln index 00674dae46..40e367de59 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -83,6 +83,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UmbracoExamine.Azure", "Umb EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UmbracoExamine.PDF.Azure", "UmbracoExamine.PDF.Azure\UmbracoExamine.PDF.Azure.csproj", "{B555AAE6-0F56-442F-AC9F-EF497DB38DE7}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{227C3B55-80E5-4E7E-A802-BE16C5128B9D}" + ProjectSection(SolutionItems) = preProject + ..\build\NuSpecs\UmbracoCms.Core.nuspec = ..\build\NuSpecs\UmbracoCms.Core.nuspec + ..\build\NuSpecs\UmbracoCms.nuspec = ..\build\NuSpecs\UmbracoCms.nuspec + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -168,6 +174,7 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {227C3B55-80E5-4E7E-A802-BE16C5128B9D} = {2849E9D4-3B4E-40A3-A309-F3CB4F0E125F} {5D3B8245-ADA6-453F-A008-50ED04BFE770} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {07FBC26B-2927-4A22-8D96-D644C667FECC} = {DD32977B-EF54-475B-9A1B-B97A502C6E58} {F30DDDB8-3994-4673-82AE-057123C6E1A8} = {DD32977B-EF54-475B-9A1B-B97A502C6E58}