From a995c9557cfbb8d6e7f303eabb559adfb4f45c1b Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 18 Feb 2013 08:31:00 -0100 Subject: [PATCH] Core.Strings - new IUrlSegmentProvider --- src/Umbraco.Core/CoreBootManager.cs | 2 + src/Umbraco.Core/Models/ContentExtensions.cs | 17 +++---- .../Strings/ContentBaseExtensions.cs | 40 +++++++++++++++++ .../Strings/DefaultUrlSegmentProvider.cs | 45 +++++++++++++++++++ .../Strings/IUrlSegmentProvider.cs | 30 +++++++++++++ .../Strings/UrlSegmentProviderResolver.cs | 41 +++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 4 ++ src/umbraco.cms/businesslogic/web/Document.cs | 15 +++---- src/umbraco.cms/helpers/url.cs | 19 ++------ 9 files changed, 176 insertions(+), 37 deletions(-) create mode 100644 src/Umbraco.Core/Strings/ContentBaseExtensions.cs create mode 100644 src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs create mode 100644 src/Umbraco.Core/Strings/IUrlSegmentProvider.cs create mode 100644 src/Umbraco.Core/Strings/UrlSegmentProviderResolver.cs diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index 29eaa32814..ab80c3d5ff 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -215,6 +215,8 @@ namespace Umbraco.Core ShortStringHelperResolver.Current = new ShortStringHelperResolver( new LegacyShortStringHelper()); + UrlSegmentProviderResolver.Current = new UrlSegmentProviderResolver( + typeof (DefaultUrlSegmentProvider)); } } } diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index a2815d04f8..db2c20a530 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Media; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Strings; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.UnitOfWork; @@ -511,7 +512,8 @@ namespace Umbraco.Core.Models /// Xml representation of the passed in private static XElement ToXml(this IContentBase contentBase, string nodeName) { - var niceUrl = contentBase.Name.FormatUrl().ToLower(); + // note: that one will take care of umbracoUrlName + var url = contentBase.GetUrlSegment(); var xml = new XElement(nodeName, new XAttribute("id", contentBase.Id), @@ -522,22 +524,13 @@ namespace Umbraco.Core.Models new XAttribute("createDate", contentBase.CreateDate.ToString("s")), new XAttribute("updateDate", contentBase.UpdateDate.ToString("s")), new XAttribute("nodeName", contentBase.Name), - new XAttribute("urlName", niceUrl),//Format Url ? + new XAttribute("urlName", url), new XAttribute("path", contentBase.Path), new XAttribute("isDoc", "")); - foreach (var property in contentBase.Properties) - { - if (property == null) continue; - + foreach (var property in contentBase.Properties.Where(p => p != null)) xml.Add(property.ToXml()); - //Check for umbracoUrlName convention - if (property.Alias == "umbracoUrlName" && property.Value != null && - property.Value.ToString().Trim() != string.Empty) - xml.SetAttributeValue("urlName", property.Value.ToString().FormatUrl().ToLower()); - } - return xml; } diff --git a/src/Umbraco.Core/Strings/ContentBaseExtensions.cs b/src/Umbraco.Core/Strings/ContentBaseExtensions.cs new file mode 100644 index 0000000000..d802bc3706 --- /dev/null +++ b/src/Umbraco.Core/Strings/ContentBaseExtensions.cs @@ -0,0 +1,40 @@ +using System; +using System.Globalization; +using System.Linq; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Strings +{ + /// + /// Provides extension methods to IContentBase to get url segments. + /// + internal static class ContentBaseExtensions + { + /// + /// Gets the default url segment for a specified content. + /// + /// The content. + /// The url segment. + public static string GetUrlSegment(this IContentBase content) + { + var urlSegmentProviders = UrlSegmentProviderResolver.Current.Providers; + var url = urlSegmentProviders.Select(p => p.GetUrlSegment(content)).First(u => u != null); + url = url ?? new DefaultUrlSegmentProvider().GetUrlSegment(content); // be safe + return url; + } + + /// + /// Gets the url segment for a specified content and culture. + /// + /// The content. + /// The culture. + /// The url segment. + public static string GetUrlSegment(this IContentBase content, CultureInfo culture) + { + var urlSegmentProviders = UrlSegmentProviderResolver.Current.Providers; + var url = urlSegmentProviders.Select(p => p.GetUrlSegment(content, culture)).First(u => u != null); + url = url ?? new DefaultUrlSegmentProvider().GetUrlSegment(content, culture); // be safe + return url; + } + } +} diff --git a/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs b/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs new file mode 100644 index 0000000000..a361b67fdd --- /dev/null +++ b/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs @@ -0,0 +1,45 @@ +using System; +using System.Globalization; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Strings +{ + /// + /// Default implementation of IUrlSegmentProvider. + /// + internal class DefaultUrlSegmentProvider : IUrlSegmentProvider + { + const string UmbracoUrlName = "umbracoUrlName"; + + /// + /// Gets the default url segment for a specified content. + /// + /// The content. + /// The url segment. + public string GetUrlSegment(IContentBase content) + { + return GetUrlSegmentSource(content).ToUrlSegment(); + } + + /// + /// Gets the url segment for a specified content and culture. + /// + /// The content. + /// The culture. + /// The url segment. + public string GetUrlSegment(IContentBase content, CultureInfo culture) + { + return GetUrlSegmentSource(content).ToUrlSegment(culture); + } + + private static string GetUrlSegmentSource(IContentBase content) + { + string source = null; + if (content.HasProperty(UmbracoUrlName)) + source = (content.GetValue(UmbracoUrlName) ?? string.Empty).Trim(); + if (string.IsNullOrWhiteSpace(source)) + source = content.Name; + return source; + } + } +} diff --git a/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs b/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs new file mode 100644 index 0000000000..b448db6c02 --- /dev/null +++ b/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs @@ -0,0 +1,30 @@ +using System.Globalization; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Strings +{ + /// + /// Provides url segments for content. + /// + /// Url segments should comply with IETF RFCs regarding content, encoding, etc. + internal interface IUrlSegmentProvider + { + /// + /// Gets the default url segment for a specified content. + /// + /// The content. + /// The url segment. + string GetUrlSegment(IContentBase content); + + /// + /// Gets the url segment for a specified content and culture. + /// + /// The content. + /// The culture. + /// The url segment. + /// This is for when Umbraco is capable of managing more than one url + /// per content, in 1-to-1 multilingual configurations. Then there would be one + /// url per culture. + string GetUrlSegment(IContentBase content, CultureInfo culture); + } +} diff --git a/src/Umbraco.Core/Strings/UrlSegmentProviderResolver.cs b/src/Umbraco.Core/Strings/UrlSegmentProviderResolver.cs new file mode 100644 index 0000000000..ef0be3513b --- /dev/null +++ b/src/Umbraco.Core/Strings/UrlSegmentProviderResolver.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Umbraco.Core.ObjectResolution; + +namespace Umbraco.Core.Strings +{ + /// + /// Resolves IUrlSegmentProvider objects. + /// + internal sealed class UrlSegmentProviderResolver : ManyObjectsResolverBase + { + /// + /// Initializes a new instance of the class with an initial list of provider types. + /// + /// The list of provider types. + /// The resolver is created by the WebBootManager and thus the constructor remains internal. + internal UrlSegmentProviderResolver(IEnumerable providerTypes) + : base(providerTypes) + { } + + /// + /// Initializes a new instance of the class with an initial list of provider types. + /// + /// The list of provider types. + /// The resolver is created by the WebBootManager and thus the constructor remains internal. + internal UrlSegmentProviderResolver(params Type[] providerTypes) + : base(providerTypes) + { } + + /// + /// Gets the providers. + /// + public IEnumerable Providers + { + get { return this.Values; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 5f708b916e..675ee57f4d 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -677,6 +677,10 @@ + + + + diff --git a/src/umbraco.cms/businesslogic/web/Document.cs b/src/umbraco.cms/businesslogic/web/Document.cs index 93b1dd95e3..8a2cd1c22e 100644 --- a/src/umbraco.cms/businesslogic/web/Document.cs +++ b/src/umbraco.cms/businesslogic/web/Document.cs @@ -14,6 +14,7 @@ using umbraco.BusinessLogic.Actions; using umbraco.cms.helpers; using umbraco.DataLayer; using Property = umbraco.cms.businesslogic.property.Property; +using Umbraco.Core.Strings; namespace umbraco.cms.businesslogic.web { @@ -1190,15 +1191,9 @@ namespace umbraco.cms.businesslogic.web /// If true the documents childrens xmlrepresentation will be appended to the Xmlnode recursive public override void XmlPopulate(XmlDocument xd, ref XmlNode x, bool Deep) { - string urlName = this.Text; - foreach (Property p in GenericProperties) - if (p != null) - { - x.AppendChild(p.ToXml(xd)); - if (p.PropertyType.Alias == "umbracoUrlName" && p.Value != null - && p.Value.ToString().Trim() != string.Empty) - urlName = p.Value.ToString(); - } + string urlName = this.Content.GetUrlSegment().ToLower(); + foreach (Property p in GenericProperties.Where(p => p != null)) + x.AppendChild(p.ToXml(xd)); // attributes x.Attributes.Append(addAttribute(xd, "id", Id.ToString())); @@ -1217,7 +1212,7 @@ namespace umbraco.cms.businesslogic.web x.Attributes.Append(addAttribute(xd, "createDate", CreateDateTime.ToString("s"))); x.Attributes.Append(addAttribute(xd, "updateDate", VersionDate.ToString("s"))); x.Attributes.Append(addAttribute(xd, "nodeName", Text)); - x.Attributes.Append(addAttribute(xd, "urlName", url.FormatUrl(urlName.ToLower()))); + x.Attributes.Append(addAttribute(xd, "urlName", urlName)); x.Attributes.Append(addAttribute(xd, "writerName", Writer.Name)); x.Attributes.Append(addAttribute(xd, "creatorName", Creator.Name.ToString())); if (ContentType != null && UmbracoSettings.UseLegacyXmlSchema) diff --git a/src/umbraco.cms/helpers/url.cs b/src/umbraco.cms/helpers/url.cs index 3fdc380785..1bd2efd682 100644 --- a/src/umbraco.cms/helpers/url.cs +++ b/src/umbraco.cms/helpers/url.cs @@ -2,6 +2,8 @@ using System; using System.Xml; using System.Text.RegularExpressions; using umbraco.IO; +using Umbraco.Core; +using Umbraco.Core.CodeAnnotations; namespace umbraco.cms.helpers { @@ -17,23 +19,10 @@ namespace umbraco.cms.helpers // } + [UmbracoWillObsolete("Use Umbraco.Core.StringExtensions.ToUrlSegment() instead.")] public static string FormatUrl(string url) { - string _newUrl = url; - XmlNode replaceChars = UmbracoSettings.UrlReplaceCharacters; - foreach (XmlNode n in replaceChars.SelectNodes("char")) - { - if (n.Attributes.GetNamedItem("org") != null && n.Attributes.GetNamedItem("org").Value != "") - _newUrl = _newUrl.Replace(n.Attributes.GetNamedItem("org").Value, xmlHelper.GetNodeValue(n)); - } - - // check for double dashes - if (UmbracoSettings.RemoveDoubleDashesFromUrlReplacing) - { - _newUrl = Regex.Replace(_newUrl, @"[-]{2,}", "-"); - } - - return _newUrl; + return url.ToUrlSegment(); } ///