Skip to content

Commit d5ba5be

Browse files
authored
Merge pull request #11386 from matthewcare/temp-11381
Request Handler Settings for character replacement
2 parents be8e7b2 + 04d2026 commit d5ba5be

File tree

9 files changed

+298
-59
lines changed

9 files changed

+298
-59
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Umbraco.Cms.Core.Configuration.UmbracoSettings;
2+
3+
namespace Umbraco.Cms.Core.Configuration.Models
4+
{
5+
public class CharItem : IChar
6+
{
7+
/// <summary>
8+
/// The character to replace
9+
/// </summary>
10+
public string Char { get; set; }
11+
12+
/// <summary>
13+
/// The replacement character
14+
/// </summary>
15+
public string Replacement { get; set; }
16+
}
17+
}
Lines changed: 35 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Umbraco.
22
// See LICENSE for more details.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.ComponentModel;
67
using Umbraco.Cms.Core.Configuration.UmbracoSettings;
@@ -16,33 +17,34 @@ public class RequestHandlerSettings
1617
{
1718
internal const bool StaticAddTrailingSlash = true;
1819
internal const string StaticConvertUrlsToAscii = "try";
20+
internal const bool StaticEnableDefaultCharReplacements = true;
1921

2022
internal static readonly CharItem[] DefaultCharCollection =
2123
{
22-
new CharItem { Char = " ", Replacement = "-" },
23-
new CharItem { Char = "\"", Replacement = string.Empty },
24-
new CharItem { Char = "'", Replacement = string.Empty },
25-
new CharItem { Char = "%", Replacement = string.Empty },
26-
new CharItem { Char = ".", Replacement = string.Empty },
27-
new CharItem { Char = ";", Replacement = string.Empty },
28-
new CharItem { Char = "/", Replacement = string.Empty },
29-
new CharItem { Char = "\\", Replacement = string.Empty },
30-
new CharItem { Char = ":", Replacement = string.Empty },
31-
new CharItem { Char = "#", Replacement = string.Empty },
32-
new CharItem { Char = "+", Replacement = "plus" },
33-
new CharItem { Char = "*", Replacement = "star" },
34-
new CharItem { Char = "&", Replacement = string.Empty },
35-
new CharItem { Char = "?", Replacement = string.Empty },
36-
new CharItem { Char = "æ", Replacement = "ae" },
37-
new CharItem { Char = "ä", Replacement = "ae" },
38-
new CharItem { Char = "ø", Replacement = "oe" },
39-
new CharItem { Char = "ö", Replacement = "oe" },
40-
new CharItem { Char = "å", Replacement = "aa" },
41-
new CharItem { Char = "ü", Replacement = "ue" },
42-
new CharItem { Char = "ß", Replacement = "ss" },
43-
new CharItem { Char = "|", Replacement = "-" },
44-
new CharItem { Char = "<", Replacement = string.Empty },
45-
new CharItem { Char = ">", Replacement = string.Empty }
24+
new () { Char = " ", Replacement = "-" },
25+
new () { Char = "\"", Replacement = string.Empty },
26+
new () { Char = "'", Replacement = string.Empty },
27+
new () { Char = "%", Replacement = string.Empty },
28+
new () { Char = ".", Replacement = string.Empty },
29+
new () { Char = ";", Replacement = string.Empty },
30+
new () { Char = "/", Replacement = string.Empty },
31+
new () { Char = "\\", Replacement = string.Empty },
32+
new () { Char = ":", Replacement = string.Empty },
33+
new () { Char = "#", Replacement = string.Empty },
34+
new () { Char = "+", Replacement = "plus" },
35+
new () { Char = "*", Replacement = "star" },
36+
new () { Char = "&", Replacement = string.Empty },
37+
new () { Char = "?", Replacement = string.Empty },
38+
new () { Char = "æ", Replacement = "ae" },
39+
new () { Char = "ä", Replacement = "ae" },
40+
new () { Char = "ø", Replacement = "oe" },
41+
new () { Char = "ö", Replacement = "oe" },
42+
new () { Char = "å", Replacement = "aa" },
43+
new () { Char = "ü", Replacement = "ue" },
44+
new () { Char = "ß", Replacement = "ss" },
45+
new () { Char = "|", Replacement = "-" },
46+
new () { Char = "<", Replacement = string.Empty },
47+
new () { Char = ">", Replacement = string.Empty }
4648
};
4749

4850
/// <summary>
@@ -67,41 +69,21 @@ public class RequestHandlerSettings
6769
/// </summary>
6870
public bool ShouldTryConvertUrlsToAscii => ConvertUrlsToAscii.InvariantEquals("try");
6971

70-
// We need to special handle ":", as this character is special in keys
71-
72-
// TODO: implement from configuration
73-
74-
//// var collection = _configuration.GetSection(Prefix + "CharCollection").GetChildren()
75-
//// .Select(x => new CharItem()
76-
//// {
77-
//// Char = x.GetValue<string>("Char"),
78-
//// Replacement = x.GetValue<string>("Replacement"),
79-
//// }).ToArray();
80-
81-
//// if (collection.Any() || _configuration.GetSection("Prefix").GetChildren().Any(x =>
82-
//// x.Key.Equals("CharCollection", StringComparison.OrdinalIgnoreCase)))
83-
//// {
84-
//// return collection;
85-
//// }
86-
87-
//// return DefaultCharCollection;
72+
/// <summary>
73+
/// Disable all default character replacements
74+
/// </summary>
75+
[DefaultValue(StaticEnableDefaultCharReplacements)]
76+
public bool EnableDefaultCharReplacements { get; set; } = StaticEnableDefaultCharReplacements;
8877

8978
/// <summary>
90-
/// Gets or sets a value for the default character collection for replacements.
79+
/// Add additional character replacements, or override defaults
9180
/// </summary>
92-
/// WB-TODO
81+
[Obsolete("Use the GetCharReplacements extension method in the Umbraco.Extensions namespace instead. Scheduled for removal in V11")]
9382
public IEnumerable<IChar> CharCollection { get; set; } = DefaultCharCollection;
9483

9584
/// <summary>
96-
/// Defines a character replacement.
85+
/// Add additional character replacements, or override defaults
9786
/// </summary>
98-
public class CharItem : IChar
99-
{
100-
/// <inheritdoc/>
101-
public string Char { get; set; }
102-
103-
/// <inheritdoc/>
104-
public string Replacement { get; set; }
105-
}
87+
public IEnumerable<CharItem> UserDefinedCharCollection { get; set; }
10688
}
10789
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System.Collections.Generic;
2+
using Umbraco.Cms.Core.Configuration.Models;
3+
4+
namespace Umbraco.Cms.Core.Configuration.UmbracoSettings
5+
{
6+
public class CharacterReplacementEqualityComparer : IEqualityComparer<IChar>
7+
{
8+
public bool Equals(IChar x, IChar y)
9+
{
10+
if (ReferenceEquals(x, y))
11+
{
12+
return true;
13+
}
14+
15+
if (x is null)
16+
{
17+
return false;
18+
}
19+
20+
if (y is null)
21+
{
22+
return false;
23+
}
24+
25+
if (x.GetType() != y.GetType())
26+
{
27+
return false;
28+
}
29+
30+
return x.Char == y.Char && x.Replacement == y.Replacement;
31+
}
32+
33+
public int GetHashCode(IChar obj)
34+
{
35+
unchecked
36+
{
37+
return ((obj.Char != null ? obj.Char.GetHashCode() : 0) * 397) ^ (obj.Replacement != null ? obj.Replacement.GetHashCode() : 0);
38+
}
39+
}
40+
}
41+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
namespace Umbraco.Cms.Core.Configuration.UmbracoSettings
1+
namespace Umbraco.Cms.Core.Configuration.UmbracoSettings
22
{
33
public interface IChar
44
{
55
string Char { get; }
6+
67
string Replacement { get; }
78
}
89
}

src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Reflection;
4+
using Microsoft.Extensions.Configuration;
35
using Microsoft.Extensions.DependencyInjection;
46
using Microsoft.Extensions.Options;
57
using Umbraco.Cms.Core.Configuration.Models;
68
using Umbraco.Cms.Core.Configuration.Models.Validation;
9+
using Umbraco.Extensions;
710

811
namespace Umbraco.Cms.Core.DependencyInjection
912
{
@@ -76,6 +79,8 @@ public static IUmbracoBuilder AddConfiguration(this IUmbracoBuilder builder)
7679
.AddUmbracoOptions<LegacyPasswordMigrationSettings>()
7780
.AddUmbracoOptions<PackageMigrationSettings>();
7881

82+
builder.Services.Configure<RequestHandlerSettings>(options => options.MergeReplacements(builder.Config));
83+
7984
return builder;
8085
}
8186
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Microsoft.Extensions.Configuration;
4+
using Umbraco.Cms.Core;
5+
using Umbraco.Cms.Core.Configuration.Models;
6+
using Umbraco.Cms.Core.Configuration.UmbracoSettings;
7+
8+
namespace Umbraco.Extensions
9+
{
10+
/// <summary>
11+
/// Get concatenated user and default character replacements
12+
/// taking into account <see cref="RequestHandlerSettings.EnableDefaultCharReplacements"/>
13+
/// </summary>
14+
public static class RequestHandlerSettingsExtension
15+
{
16+
/// <summary>
17+
/// Get concatenated user and default character replacements
18+
/// taking into account <see cref="RequestHandlerSettings.EnableDefaultCharReplacements"/>
19+
/// </summary>
20+
public static IEnumerable<CharItem> GetCharReplacements(this RequestHandlerSettings requestHandlerSettings)
21+
{
22+
if (requestHandlerSettings.EnableDefaultCharReplacements is false)
23+
{
24+
return requestHandlerSettings.UserDefinedCharCollection ?? Enumerable.Empty<CharItem>();
25+
}
26+
27+
if (requestHandlerSettings.UserDefinedCharCollection == null || requestHandlerSettings.UserDefinedCharCollection.Any() is false)
28+
{
29+
return RequestHandlerSettings.DefaultCharCollection;
30+
}
31+
32+
return MergeUnique(requestHandlerSettings.UserDefinedCharCollection, RequestHandlerSettings.DefaultCharCollection);
33+
}
34+
35+
/// <summary>
36+
/// Merges CharCollection and UserDefinedCharCollection, prioritizing UserDefinedCharCollection
37+
/// </summary>
38+
internal static void MergeReplacements(this RequestHandlerSettings requestHandlerSettings, IConfiguration configuration)
39+
{
40+
string sectionKey = $"{Constants.Configuration.ConfigRequestHandler}:";
41+
42+
IEnumerable<CharItem> charCollection = GetReplacements(
43+
configuration,
44+
$"{sectionKey}{nameof(RequestHandlerSettings.CharCollection)}");
45+
46+
IEnumerable<CharItem> userDefinedCharCollection = GetReplacements(
47+
configuration,
48+
$"{sectionKey}{nameof(requestHandlerSettings.UserDefinedCharCollection)}");
49+
50+
IEnumerable<CharItem> mergedCollection = MergeUnique(userDefinedCharCollection, charCollection);
51+
52+
requestHandlerSettings.UserDefinedCharCollection = mergedCollection;
53+
}
54+
55+
private static IEnumerable<CharItem> GetReplacements(IConfiguration configuration, string key)
56+
{
57+
var replacements = new List<CharItem>();
58+
IEnumerable<IConfigurationSection> config = configuration.GetSection(key).GetChildren();
59+
60+
foreach (IConfigurationSection section in config)
61+
{
62+
var @char = section.GetValue<string>(nameof(CharItem.Char));
63+
var replacement = section.GetValue<string>(nameof(CharItem.Replacement));
64+
replacements.Add(new CharItem { Char = @char, Replacement = replacement });
65+
}
66+
67+
return replacements;
68+
}
69+
70+
/// <summary>
71+
/// Merges two IEnumerable of CharItem without any duplicates, items in priorityReplacements will override those in alternativeReplacements
72+
/// </summary>
73+
private static IEnumerable<CharItem> MergeUnique(
74+
IEnumerable<CharItem> priorityReplacements,
75+
IEnumerable<CharItem> alternativeReplacements)
76+
{
77+
var priorityReplacementsList = priorityReplacements.ToList();
78+
var alternativeReplacementsList = alternativeReplacements.ToList();
79+
80+
foreach (CharItem alternativeReplacement in alternativeReplacementsList)
81+
{
82+
foreach (CharItem priorityReplacement in priorityReplacementsList)
83+
{
84+
if (priorityReplacement.Char == alternativeReplacement.Char)
85+
{
86+
alternativeReplacement.Replacement = priorityReplacement.Replacement;
87+
}
88+
}
89+
}
90+
91+
return priorityReplacementsList.Union<CharItem>(
92+
alternativeReplacementsList,
93+
new CharacterReplacementEqualityComparer());
94+
}
95+
}
96+
}

src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Linq;
44
using Umbraco.Cms.Core.Configuration.Models;
5+
using Umbraco.Cms.Core.Configuration.UmbracoSettings;
56
using Umbraco.Extensions;
67

78
namespace Umbraco.Cms.Core.Strings
@@ -60,7 +61,9 @@ public DefaultShortStringHelperConfig WithConfig(string culture, CleanStringType
6061
/// <returns>The short string helper.</returns>
6162
public DefaultShortStringHelperConfig WithDefault(RequestHandlerSettings requestHandlerSettings)
6263
{
63-
UrlReplaceCharacters = requestHandlerSettings.CharCollection
64+
IEnumerable<IChar> charCollection = requestHandlerSettings.GetCharReplacements();
65+
66+
UrlReplaceCharacters = charCollection
6467
.Where(x => string.IsNullOrEmpty(x.Char) == false)
6568
.ToDictionary(x => x.Char, x => x.Replacement);
6669

0 commit comments

Comments
 (0)