From 6f3193e52b0f146e7a82f799790dace673c70ce2 Mon Sep 17 00:00:00 2001 From: MarcoViglione-Laser Date: Fri, 2 Dec 2016 17:07:43 +0100 Subject: [PATCH] [Fixes ##7430] Added support for culture neutral patterns (#7449) Fixes ##7430 --- .../Modules/Orchard.Autoroute/Migrations.cs | 59 +++++++++++- .../ContentDefinitionEventHandler.cs | 13 ++- .../Services/AutorouteService.cs | 75 ++++++++++----- .../Settings/AutorouteSettingsEvents.cs | 93 ++++++++++++------- .../AutorouteSettings.cshtml | 20 ++-- 5 files changed, 187 insertions(+), 73 deletions(-) diff --git a/src/Orchard.Web/Modules/Orchard.Autoroute/Migrations.cs b/src/Orchard.Web/Modules/Orchard.Autoroute/Migrations.cs index 545e7d35d..b31392396 100644 --- a/src/Orchard.Web/Modules/Orchard.Autoroute/Migrations.cs +++ b/src/Orchard.Web/Modules/Orchard.Autoroute/Migrations.cs @@ -1,10 +1,20 @@ -using Orchard.ContentManagement.MetaData; +using System; +using System.Collections.Generic; +using System.Linq; +using Orchard.Autoroute.Models; +using Orchard.Autoroute.Settings; +using Orchard.ContentManagement.MetaData; using Orchard.Core.Contents.Extensions; using Orchard.Data.Migration; +using Orchard.Localization.Services; -namespace Orchard.Autoroute -{ - public class Migrations : DataMigrationImpl { +namespace Orchard.Autoroute { + public class Migrations : DataMigrationImpl { + private readonly ICultureManager _cultureManager; + + public Migrations(ICultureManager cultureManager) { + _cultureManager = cultureManager; + } public int Create() { SchemaBuilder.CreateTable("AutoroutePartRecord", @@ -49,5 +59,46 @@ namespace Orchard.Autoroute return 4; } + + public int UpdateFrom4() { + // Adding some culture neutral patterns if they don't exist + var autoroutePartDefinitions = ContentDefinitionManager.ListTypeDefinitions() + .Where(t => t.Parts.Any(p => p.PartDefinition.Name.Equals(typeof(AutoroutePart).Name))) + .Select(s => new { contentTypeName = s.Name, autoroutePart = s.Parts.First(x => x.PartDefinition.Name == "AutoroutePart") }); + + foreach (var partDefinition in autoroutePartDefinitions) { + var settingsDictionary = partDefinition.autoroutePart.Settings; + var settings = settingsDictionary.GetModel(); + + if (!settings.Patterns.Any(x => String.IsNullOrWhiteSpace(x.Culture))) { + string siteCulture = _cultureManager.GetSiteCulture(); + List newPatterns = new List(); + + if (settings.Patterns.Any(x => String.Equals(x.Culture, siteCulture, StringComparison.OrdinalIgnoreCase))) { + var siteCulturePatterns = settings.Patterns.Where(x => String.Equals(x.Culture, siteCulture, StringComparison.OrdinalIgnoreCase)).ToList(); + + foreach (RoutePattern pattern in siteCulturePatterns) { + newPatterns.Add(String.Format("{{\"Name\":\"{0}\",\"Pattern\":\"{1}\",\"Description\":\"{2}\"}}", pattern.Name, pattern.Pattern, pattern.Description)); + } + } + else { + newPatterns.Add(String.Format("{{\"Name\":\"{0}\",\"Pattern\":\"{1}\",\"Description\":\"{2}\"}}", "Title", "{Content.Slug}", "my-title")); + } + + if (settingsDictionary.ContainsKey("AutorouteSettings.PatternDefinitions")) { + string oldPatterns = settingsDictionary["AutorouteSettings.PatternDefinitions"]; + if (oldPatterns.StartsWith("[") && oldPatterns.EndsWith("]")) + newPatterns.Add(oldPatterns.Substring(1, oldPatterns.Length - 2)); + } + + ContentDefinitionManager.AlterTypeDefinition(partDefinition.contentTypeName, cfg => cfg + .WithPart("AutoroutePart", builder => builder + .WithSetting("AutorouteSettings.PatternDefinitions", "[" + String.Join(",", newPatterns) + "]") + )); + } + } + + return 5; + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Autoroute/Providers/ContentDefinition/ContentDefinitionEventHandler.cs b/src/Orchard.Web/Modules/Orchard.Autoroute/Providers/ContentDefinition/ContentDefinitionEventHandler.cs index 97d61324f..6c8e02a7f 100644 --- a/src/Orchard.Web/Modules/Orchard.Autoroute/Providers/ContentDefinition/ContentDefinitionEventHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Autoroute/Providers/ContentDefinition/ContentDefinitionEventHandler.cs @@ -50,19 +50,24 @@ namespace Orchard.Autoroute.Providers.ContentDefinition { public void ContentPartAttached(ContentPartAttachedContext context) { if (context.ContentPartName == "AutoroutePart") { - //Create pattern and default pattern for each culture installed + // Create pattern and default pattern for each culture installed and for the neutral culture - //get cultures + // Get cultures var SiteCultures = _cultureManager.ListCultures().ToList(); - //Create Patterns and DefaultPatterns + // Adding a null culture for the culture neutral pattern + List cultures = new List(); + cultures.Add(null); + cultures.AddRange(SiteCultures); + + // Create Patterns and DefaultPatterns var settings = new AutorouteSettings { Patterns = new List() }; List newPatterns = new List(); List newDefaultPatterns = new List(); - foreach (string culture in SiteCultures) { + foreach (string culture in cultures) { newPatterns.Add(new RoutePattern { Name = "Title", Description = "my-title", diff --git a/src/Orchard.Web/Modules/Orchard.Autoroute/Services/AutorouteService.cs b/src/Orchard.Web/Modules/Orchard.Autoroute/Services/AutorouteService.cs index e015e7c91..ff52a64e7 100644 --- a/src/Orchard.Web/Modules/Orchard.Autoroute/Services/AutorouteService.cs +++ b/src/Orchard.Web/Modules/Orchard.Autoroute/Services/AutorouteService.cs @@ -135,35 +135,47 @@ namespace Orchard.Autoroute.Services { public RoutePattern GetDefaultPattern(string contentType, string culture) { var settings = GetTypePartSettings(contentType).GetModel(); - var defaultPattern = settings.DefaultPatterns.FirstOrDefault(x => x.Culture == culture); - var defaultPatternIndex = defaultPattern != null ? defaultPattern.PatternIndex : "0"; - if (String.IsNullOrWhiteSpace(defaultPatternIndex)) - defaultPatternIndex = "0"; + if (settings.UseCulturePattern) { + var defaultPatternIndex = "0"; - if (!settings.DefaultPatterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { - var patternIndex = String.IsNullOrWhiteSpace(settings.DefaultPatternIndex) ? "0" : settings.DefaultPatternIndex; - // Lazy updating from old setting. - if (String.Equals(culture, _cultureManager.GetSiteCulture(), StringComparison.OrdinalIgnoreCase)) { - settings.DefaultPatterns.Add(new DefaultPattern { PatternIndex = patternIndex, Culture = culture }); - return settings.Patterns.Where(x => x.Culture == null).ElementAt(Convert.ToInt32(defaultPatternIndex)); + if (!settings.DefaultPatterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { + // If no default pattern exists for the language return the default culture neutral pattern if it exists, else a generic pattern + if (settings.Patterns.Any(x => String.IsNullOrEmpty(x.Culture))) { + defaultPatternIndex = GetDefaultPatternIndex(contentType, null); + return settings.Patterns.Where(x => String.IsNullOrEmpty(x.Culture)).ElementAt(Convert.ToInt32(defaultPatternIndex)); + } + else { + return new RoutePattern { Name = "Title", Description = "my-title", Pattern = "{Content.Slug}", Culture = culture }; + } } - else { - settings.DefaultPatterns.Add(new DefaultPattern { PatternIndex = "0", Culture = culture }); - return new RoutePattern { Name = "Title", Description = "my-title", Pattern = "{Content.Slug}", Culture = culture }; + + // If patterns for the specified culture exist search one of them, else search a culture neutral pattern + var patternCultureSearch = settings.Patterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase)) ? culture : null; + defaultPatternIndex = GetDefaultPatternIndex(contentType, patternCultureSearch); + + if (settings.Patterns.Any()) { + if (settings.Patterns.Where(x => x.Culture == patternCultureSearch).ElementAt(Convert.ToInt32(defaultPatternIndex)) != null) { + return settings.Patterns.Where(x => x.Culture == patternCultureSearch).ElementAt(Convert.ToInt32(defaultPatternIndex)); + } + } + } + else { + // Using the culture neutral pattern + if (settings.Patterns.Any(x => String.IsNullOrEmpty(x.Culture))) { + var defaultPatternIndex = GetDefaultPatternIndex(contentType, null); + + // If no default culture neutral pattern exist use the default pattern + if (!settings.DefaultPatterns.Any(x => String.IsNullOrEmpty(x.Culture))) { + var patternIndex = String.IsNullOrWhiteSpace(settings.DefaultPatternIndex) ? "0" : settings.DefaultPatternIndex; + settings.DefaultPatterns.Add(new DefaultPattern { PatternIndex = patternIndex, Culture = null }); + } + + return settings.Patterns.Where(x => String.IsNullOrEmpty(x.Culture)).ElementAt(Convert.ToInt32(defaultPatternIndex)); } } - // Return a default pattern if set. - var patternCultureSearch = settings.Patterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase)) ? culture : null; - - if (settings.Patterns.Any()) { - if (settings.Patterns.Where(x => x.Culture == patternCultureSearch).ElementAt(Convert.ToInt32(defaultPatternIndex)) != null) { - return settings.Patterns.Where(x => x.Culture == patternCultureSearch).ElementAt(Convert.ToInt32(defaultPatternIndex)); - }; - } - - // Return a default pattern if none is defined. + // Return a default pattern if none is defined return new RoutePattern { Name = "Title", Description = "my-title", Pattern = "{Content.Slug}", Culture = culture }; } @@ -222,6 +234,23 @@ namespace Orchard.Autoroute.Services { return contentDefinition.Parts.First(x => x.PartDefinition.Name == "AutoroutePart").Settings; } + private string GetDefaultPatternIndex(string contentType, string culture) { + var settings = GetTypePartSettings(contentType).GetModel(); + + DefaultPattern defaultPattern = null; + if (String.IsNullOrEmpty(culture)) + defaultPattern = settings.DefaultPatterns.FirstOrDefault(x => String.IsNullOrEmpty(x.Culture)); + else + defaultPattern = settings.DefaultPatterns.FirstOrDefault(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase)); + + var defaultPatternIndex = defaultPattern != null ? defaultPattern.PatternIndex : "0"; + + if (String.IsNullOrWhiteSpace(defaultPatternIndex)) + defaultPatternIndex = "0"; + + return defaultPatternIndex; + } + private static int? GetSlugVersion(string path, string potentialConflictingPath) { int v; var slugParts = potentialConflictingPath.Split(new[] { path }, StringSplitOptions.RemoveEmptyEntries); diff --git a/src/Orchard.Web/Modules/Orchard.Autoroute/Settings/AutorouteSettingsEvents.cs b/src/Orchard.Web/Modules/Orchard.Autoroute/Settings/AutorouteSettingsEvents.cs index fb15c691e..fa369575f 100644 --- a/src/Orchard.Web/Modules/Orchard.Autoroute/Settings/AutorouteSettingsEvents.cs +++ b/src/Orchard.Web/Modules/Orchard.Autoroute/Settings/AutorouteSettingsEvents.cs @@ -28,56 +28,55 @@ namespace Orchard.Autoroute.Settings { var settings = definition.Settings.GetModel(); - //get cultures + // Get cultures settings.SiteCultures = _cultureManager.ListCultures().ToList(); - //get default site culture + // Get default site culture settings.DefaultSiteCulture = _cultureManager.GetSiteCulture(); - //if a culture is not set on the pattern we set it to the default site culture for backward compatibility - if (!settings.Patterns.Any(x => String.Equals(x.Culture, settings.DefaultSiteCulture, StringComparison.OrdinalIgnoreCase))) { - foreach (RoutePattern pattern in settings.Patterns.Where(x => String.IsNullOrWhiteSpace(x.Culture))) { - settings.Patterns.Where(x => x.GetHashCode() == pattern.GetHashCode()).FirstOrDefault().Culture = settings.DefaultSiteCulture; - } - } - - //Adding Patterns for the UI + // Adding Patterns for the UI List newPatterns = new List(); + + // Adding a null culture for the culture neutral pattern + List cultures = new List(); + cultures.Add(null); + cultures.AddRange(settings.SiteCultures); + int current = 0; - foreach (string culture in settings.SiteCultures) { + foreach (string culture in cultures) { + // Adding all existing patterns for the culture foreach (RoutePattern routePattern in settings.Patterns.Where(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { - if (settings.Patterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { - newPatterns.Add(settings.Patterns[current]); - } else { - newPatterns.Add(new RoutePattern { - Name = "Title", - Description = "my-title", - Pattern = "{Content.Slug}", - Culture = settings.DefaultSiteCulture - }); - } + newPatterns.Add(settings.Patterns[current]); current++; } - //We add a pattern for each culture if there is none + // Adding a pattern for each culture if there is none if (!settings.Patterns.Where(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase)).Any()) { newPatterns.Add(new RoutePattern { Culture = culture, Name = "Title", Description = "my-title", Pattern = "{Content.Slug}" }); } - //we add a new empty line for each culture + // Adding a new empty line for each culture newPatterns.Add(new RoutePattern { Culture = culture, Name = null, Description = null, Pattern = null }); - // if the content type has no defaultPattern for autoroute, then assign one - if (!settings.DefaultPatterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { - //if we are in the default culture check the old setting + // If the content type has no defaultPattern for autoroute, assign one + bool defaultPatternExists = false; + if (String.IsNullOrEmpty(culture)) + defaultPatternExists = settings.DefaultPatterns.Any(x => String.IsNullOrEmpty(x.Culture)); + else + defaultPatternExists = settings.DefaultPatterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase)); + + if (!defaultPatternExists) { + // If in the default culture check the old setting if (String.Equals(culture, _cultureManager.GetSiteCulture(), StringComparison.OrdinalIgnoreCase)) { var defaultPatternIndex = settings.DefaultPatternIndex; if (!String.IsNullOrWhiteSpace(defaultPatternIndex)) { var patternIndex = defaultPatternIndex; settings.DefaultPatterns.Add(new DefaultPattern { Culture = settings.DefaultSiteCulture, PatternIndex = patternIndex }); - } else { + } + else { settings.DefaultPatterns.Add(new DefaultPattern { PatternIndex = "0", Culture = culture }); } - } else { + } + else { settings.DefaultPatterns.Add(new DefaultPattern { PatternIndex = "0", Culture = culture }); } } @@ -96,25 +95,51 @@ namespace Orchard.Autoroute.Settings { Patterns = new List() }; - //get cultures + // Get cultures settings.SiteCultures = _cultureManager.ListCultures().ToList(); if (updateModel.TryUpdateModel(settings, "AutorouteSettings", null, null)) { //TODO need to add validations client and/or server side here - // remove empty patterns + + // If some default pattern is an empty pattern set it to the first pattern for the language + List newDefaultPatterns = new List(); + + foreach (var defaultPattern in settings.DefaultPatterns) { + RoutePattern correspondingPattern = null; + + if (string.IsNullOrEmpty(defaultPattern.Culture)) + correspondingPattern = settings.Patterns.Where(x => String.IsNullOrEmpty(x.Culture)).ElementAt(Convert.ToInt32(defaultPattern.PatternIndex)); + else + correspondingPattern = settings.Patterns.Where(x => String.Equals(x.Culture, defaultPattern.Culture, StringComparison.OrdinalIgnoreCase)).ElementAt(Convert.ToInt32(defaultPattern.PatternIndex)); + + if (String.IsNullOrWhiteSpace(correspondingPattern.Name) && String.IsNullOrWhiteSpace(correspondingPattern.Pattern) && String.IsNullOrWhiteSpace(correspondingPattern.Description)) + newDefaultPatterns.Add(new DefaultPattern { Culture = defaultPattern.Culture, PatternIndex = "0" }); + else + newDefaultPatterns.Add(defaultPattern); + } + + settings.DefaultPatterns = newDefaultPatterns; + + // Remove empty patterns var patterns = settings.Patterns; patterns.RemoveAll(p => String.IsNullOrWhiteSpace(p.Name) && String.IsNullOrWhiteSpace(p.Pattern) && String.IsNullOrWhiteSpace(p.Description)); - //If there is no default pattern for each culture we set default ones + // Adding a null culture for the culture neutral pattern + List cultures = new List(); + cultures.Add(null); + cultures.AddRange(settings.SiteCultures); + + //If there is no pattern for some culture create a default one List newPatterns = new List(); int current = 0; - foreach (string culture in settings.SiteCultures) { + foreach (string culture in cultures) { if (settings.Patterns.Any(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { foreach (RoutePattern routePattern in settings.Patterns.Where(x => String.Equals(x.Culture, culture, StringComparison.OrdinalIgnoreCase))) { newPatterns.Add(settings.Patterns[current]); current++; } - } else { + } + else { newPatterns.Add(new RoutePattern { Name = "Title", Description = "my-title", @@ -128,7 +153,7 @@ namespace Orchard.Autoroute.Settings { settings.Patterns = newPatterns; - // update the settings builder + // Update the settings builder settings.Build(builder); } diff --git a/src/Orchard.Web/Modules/Orchard.Autoroute/Views/DefinitionTemplates/AutorouteSettings.cshtml b/src/Orchard.Web/Modules/Orchard.Autoroute/Views/DefinitionTemplates/AutorouteSettings.cshtml index b128b071d..ebc345920 100644 --- a/src/Orchard.Web/Modules/Orchard.Autoroute/Views/DefinitionTemplates/AutorouteSettings.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Autoroute/Views/DefinitionTemplates/AutorouteSettings.cshtml @@ -1,11 +1,15 @@ @model Orchard.Autoroute.Settings.AutorouteSettings -@using Orchard.Utility.Extensions; @{ Script.Require("AutorouteBrowser"); Style.Require("AutorouteSettings"); + int patternCount = 0; int patternCultureCount = 0; + + List cultures = new List(); + cultures.Add(null); + cultures.AddRange(Model.SiteCultures); }
@@ -40,10 +44,10 @@ int i = 1; string cssClass = ""; } -
  • @Model.DefaultSiteCulture
  • - @foreach (var culture in Model.SiteCultures) { - if (culture != Model.DefaultSiteCulture) { - cssClass = i == Model.SiteCultures.Count - 1 ? "last" : "middle"; +
  • @T("All cultures")
  • + @foreach (var culture in cultures) { + if (!String.IsNullOrEmpty(culture)) { + cssClass = i == cultures.Count - 1 ? "last" : "middle";
  • @culture
  • i++; } @@ -51,8 +55,8 @@
    - @foreach (var culture in Model.SiteCultures) { -
    + @foreach (var culture in cultures) { +
    @@ -63,7 +67,7 @@ @for (int index = 0; index < Model.Patterns.Where(x => x.Culture == culture).Count(); index++) { - +
    @T("Default")
    @Html.RadioButtonFor(m => m.DefaultPatterns[Model.SiteCultures.IndexOf(culture)].Culture, culture + "|" + patternCultureCount, patternCultureCount.ToString() == Model.DefaultPatterns[Model.SiteCultures.IndexOf(culture)].PatternIndex ? new { @checked = "checked" } : null)@Html.RadioButtonFor(m => m.DefaultPatterns[cultures.IndexOf(culture)].Culture, culture + "|" + patternCultureCount, patternCultureCount.ToString() == Model.DefaultPatterns[cultures.IndexOf(culture)].PatternIndex ? new { @checked = "checked" } : null) @Html.TextBoxFor(m => m.Patterns[patternCount].Name, new { @class = "text" }) @Html.TextBoxFor(m => m.Patterns[patternCount].Pattern, new { @class = "tokenized text" }) @Html.TextBoxFor(m => m.Patterns[patternCount].Description, new { @class = "text" })