diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 272dd6d4d4..2c92517b15 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -27,6 +27,8 @@ namespace Umbraco.Core.Persistence.Repositories { _contentTypeRepository = contentTypeRepository; _templateRepository = templateRepository; + + EnsureUniqueNaming = true; } public ContentRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository) @@ -34,8 +36,12 @@ namespace Umbraco.Core.Persistence.Repositories { _contentTypeRepository = contentTypeRepository; _templateRepository = templateRepository; + + EnsureUniqueNaming = true; } + public bool EnsureUniqueNaming { get; set; } + #region Overrides of RepositoryBase protected override IContent PerformGet(int id) @@ -180,6 +186,9 @@ namespace Umbraco.Core.Persistence.Repositories { ((Content)entity).AddingEntity(); + //Ensure unique name on the same level + entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name); + var factory = new ContentFactory(NodeObjectTypeId, entity.Id); var dto = factory.BuildDto(entity); @@ -260,6 +269,9 @@ namespace Umbraco.Core.Persistence.Repositories entity.UpdateDate = DateTime.Now; } + //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")) { @@ -489,5 +501,77 @@ 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.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; + } + + /// + /// 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/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index 5a7b72729b..38189576a7 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -43,7 +43,7 @@ namespace Umbraco.Core.Persistence uow, RuntimeCacheProvider.Current, CreateContentTypeRepository(uow), - CreateTemplateRepository(uow)); + CreateTemplateRepository(uow)) { EnsureUniqueNaming = Umbraco.Core.Configuration.UmbracoSettings.EnsureUniqueNaming }; } public virtual IContentTypeRepository CreateContentTypeRepository(IDatabaseUnitOfWork uow) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/ActionHandlers/umbEnsureUniqueName.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/ActionHandlers/umbEnsureUniqueName.cs index 4c57614846..baacbff4f6 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/ActionHandlers/umbEnsureUniqueName.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/ActionHandlers/umbEnsureUniqueName.cs @@ -12,7 +12,8 @@ namespace umbraco.ActionHandlers /// It ensures that new content nodes gets a unique name, and thereby avoiding conflictiong URLs. /// It can be disabled in the umbracoSettings.config file. /// - public class umbEnsureUniqueName : umbraco.BusinessLogic.Actions.IActionHandler + [Obsolete("This handler is no longer used")] + public class umbEnsureUniqueName : IActionHandler { public umbEnsureUniqueName() { @@ -82,7 +83,7 @@ namespace umbraco.ActionHandlers /// public interfaces.IAction[] ReturnActions() { - interfaces.IAction[] _retVal = { ActionNew.Instance }; + interfaces.IAction[] _retVal = { }; return _retVal; } diff --git a/src/umbraco.cms/Actions/IActionHandler.cs b/src/umbraco.cms/Actions/IActionHandler.cs index 7e28b7bd78..cc4fecd51c 100644 --- a/src/umbraco.cms/Actions/IActionHandler.cs +++ b/src/umbraco.cms/Actions/IActionHandler.cs @@ -1,12 +1,5 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.Web; -using System.Reflection; -using umbraco.BasePages; -using umbraco.BusinessLogic.Utils; using umbraco.cms.businesslogic.web; -using umbraco.cms.businesslogic.workflow; using umbraco.interfaces; namespace umbraco.BusinessLogic.Actions @@ -20,6 +13,7 @@ namespace umbraco.BusinessLogic.Actions /// /// /// + [Obsolete("Legacy! Use events instead")] public interface IActionHandler { bool Execute(Document documentObject, IAction action); diff --git a/src/umbraco.cms/businesslogic/member/Member.cs b/src/umbraco.cms/businesslogic/member/Member.cs index dd78840be4..4e2fa3e7c6 100644 --- a/src/umbraco.cms/businesslogic/member/Member.cs +++ b/src/umbraco.cms/businesslogic/member/Member.cs @@ -632,6 +632,7 @@ namespace umbraco.cms.businesslogic.member poco.Integer = property.Value != null && string.IsNullOrEmpty(property.Value.ToString()) ? 0 : Convert.ToInt32(property.Value); + : Convert.ToInt32(property.Value); } else {