mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
By default, html-encode translation parameters
Also ensure that any existing parameter is not double-encoded by removing .Text when used as a translation parameter.
This commit is contained in:
46
src/Orchard.Tests/Localization/TextTests.cs
Normal file
46
src/Orchard.Tests/Localization/TextTests.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using Autofac;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Localization.Services;
|
||||
using Orchard.Mvc;
|
||||
using Orchard.Tests.Stubs;
|
||||
using System.Web;
|
||||
|
||||
namespace Orchard.Tests.Localization {
|
||||
[TestFixture]
|
||||
public class TextTests {
|
||||
private IContainer _container;
|
||||
private IText _text;
|
||||
|
||||
[SetUp]
|
||||
public void Init() {
|
||||
var mockLocalizedManager = new Mock<ILocalizedStringManager>();
|
||||
mockLocalizedManager
|
||||
.Setup(x => x.GetLocalizedString(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Returns("foo {0}");
|
||||
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterInstance(new StubCultureSelector("fr-CA")).As<ICultureSelector>();
|
||||
builder.RegisterInstance(new StubWorkContext()).As<WorkContext>();
|
||||
builder.RegisterType<StubWorkContextAccessor>().As<IWorkContextAccessor>();
|
||||
builder.RegisterInstance(mockLocalizedManager.Object);
|
||||
builder.RegisterType<Orchard.Localization.Text>().As<IText>().WithParameter(new NamedParameter("scope", "scope"));
|
||||
_container = builder.Build();
|
||||
_text = _container.Resolve<IText>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TextHtmlEncodeAllArguments() {
|
||||
Assert.That(_text.Get("foo {0}", "bar").Text, Is.EqualTo("foo bar"));
|
||||
Assert.That(_text.Get("foo {0}", "<bar>").Text, Is.EqualTo("foo <bar>"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TextDoesEncodeHtmlEncodedArguments()
|
||||
{
|
||||
Assert.That(_text.Get("foo {0}", new HtmlString("bar")).Text, Is.EqualTo("foo bar"));
|
||||
Assert.That(_text.Get("foo {0}", new HtmlString("<bar>")).Text, Is.EqualTo("foo <bar>"));
|
||||
}
|
||||
}
|
||||
}
|
@@ -259,6 +259,7 @@
|
||||
<Compile Include="FileSystems\Dependencies\AssemblyProbingFolderTests.cs" />
|
||||
<Compile Include="FileSystems\Dependencies\DependenciesFolderTests.cs" />
|
||||
<Compile Include="FileSystems\VirtualPath\DefaultVirtualPathProviderTests.cs" />
|
||||
<Compile Include="Localization\TextTests.cs" />
|
||||
<Compile Include="Localization\CurrentCultureWorkContextTests.cs" />
|
||||
<Compile Include="Localization\CultureManagerTests.cs" />
|
||||
<Compile Include="Localization\DateTimePartsTests.cs" />
|
||||
|
@@ -1 +1,6 @@
|
||||
<div class="message message-@Model.Type">@Html.Raw(Html.Encode(Model.Message).Replace("\n", "<br />"))</div>
|
||||
@*
|
||||
Model.Message can either be:
|
||||
- an IHtmlString and won't be re-encoded
|
||||
- a string and will be encoded
|
||||
*@
|
||||
<div class="message message-@Model.Type">@Html.Raw(Html.Encode(Model.Message).Replace("\n", "<br />"))</div>
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<div class="user-display">
|
||||
@if (WorkContext.CurrentUser != null) {
|
||||
<span class="user-actions welcome">
|
||||
@T("Welcome, <strong>{0}</strong>!", "<a href=\"" + @Url.Action("ChangePassword", new { Controller = "Account", Area = "Orchard.Users" }) + "\">" + @Html.ItemDisplayText(WorkContext.CurrentUser) + "</a>")
|
||||
@T("Welcome, <strong>{0}</strong>!", Html.Raw("<a href=\"" + @Url.Action("ChangePassword", new { Controller = "Account", Area = "Orchard.Users" }) + "\">" + Html.ItemDisplayText(WorkContext.CurrentUser) + "</a>"))
|
||||
</span>
|
||||
<span class="user-actions">
|
||||
@Html.ActionLink(T("Sign Out").ToString(), "LogOff", new { Controller = "Account", Area = "Orchard.Users", ReturnUrl = Context.Request.RawUrl }, new { rel = "nofollow" })
|
||||
|
@@ -32,5 +32,5 @@
|
||||
}
|
||||
<div class="publication-status">
|
||||
<em>@T("Status:")</em>
|
||||
<span>@T("{0}", mediaPart.ContentItem.VersionRecord.Published ? "Published" : "Draft")</span>
|
||||
<span>@T(mediaPart.ContentItem.VersionRecord.Published ? "Published" : "Draft")</span>
|
||||
</div>
|
@@ -89,7 +89,7 @@
|
||||
featureClassName += " missingDependencies";
|
||||
}
|
||||
|
||||
<li class="@featureClassName" id="@featureId" title="@T("{0} is {1}", Html.AttributeEncode(featureName), featureState)">
|
||||
<li class="@featureClassName" id="@featureId" title="@T(feature.IsEnabled ? "{0} is enabled" : "{0} is disabled", Html.AttributeEncode(featureName))">
|
||||
<div class="summary">
|
||||
<div class="properties">
|
||||
<h3>
|
||||
|
@@ -249,21 +249,21 @@ namespace Orchard.Projections.FilterEditors.Forms {
|
||||
|
||||
switch (op) {
|
||||
case DateTimeOperator.LessThan:
|
||||
return T("{0} is less than {1}{2}", fieldName, value, T(valueUnit).Text);
|
||||
return T("{0} is less than {1}{2}", fieldName, value, T(valueUnit));
|
||||
case DateTimeOperator.LessThanEquals:
|
||||
return T("{0} is less or equal than {1}{2}", fieldName, value, T(valueUnit).Text);
|
||||
return T("{0} is less or equal than {1}{2}", fieldName, value, T(valueUnit));
|
||||
case DateTimeOperator.Equals:
|
||||
return T("{0} equals {1}{2}", fieldName, value, T(valueUnit).Text);
|
||||
return T("{0} equals {1}{2}", fieldName, value, T(valueUnit));
|
||||
case DateTimeOperator.NotEquals:
|
||||
return T("{0} is not equal to {1}{2}", fieldName, value, T(valueUnit).Text);
|
||||
return T("{0} is not equal to {1}{2}", fieldName, value, T(valueUnit));
|
||||
case DateTimeOperator.GreaterThan:
|
||||
return T("{0} is greater than {1}{2}", fieldName, value, T(valueUnit).Text);
|
||||
return T("{0} is greater than {1}{2}", fieldName, value, T(valueUnit));
|
||||
case DateTimeOperator.GreaterThanEquals:
|
||||
return T("{0} is greater or equal than {1}{2}", fieldName, value, T(valueUnit).Text);
|
||||
return T("{0} is greater or equal than {1}{2}", fieldName, value, T(valueUnit));
|
||||
case DateTimeOperator.Between:
|
||||
return T("{0} is between {1}{2} and {3}{4}", fieldName, min, T(minUnit).Text, max, T(maxUnit).Text);
|
||||
return T("{0} is between {1}{2} and {3}{4}", fieldName, min, T(minUnit), max, T(maxUnit));
|
||||
case DateTimeOperator.NotBetween:
|
||||
return T("{0} is not between {1}{2} and {3}{4}", fieldName, min, T(minUnit).Text, max, T(maxUnit).Text);
|
||||
return T("{0} is not between {1}{2} and {3}{4}", fieldName, min, T(minUnit), max, T(maxUnit));
|
||||
}
|
||||
|
||||
// should never be hit, but fail safe
|
||||
|
@@ -1,9 +1,8 @@
|
||||
@model AdminEditViewModel
|
||||
@using Orchard.Projections.Models;
|
||||
@using Orchard.Projections.ViewModels;
|
||||
|
||||
@{
|
||||
Layout.Title = T("Edit Query - {0}", Model.Name).Text;
|
||||
Layout.Title = T("Edit Query - {0}", Model.Name);
|
||||
Style.Include("admin-projections.css");
|
||||
}
|
||||
|
||||
|
@@ -2,11 +2,11 @@
|
||||
|
||||
@{
|
||||
string name = Model.Name;
|
||||
string title = null;
|
||||
IHtmlString title = null;
|
||||
if (Model.State != null && HasText(Model.State.Unity)) {
|
||||
string amount = Model.State.Amount;
|
||||
string unity = Model.State.Unity;
|
||||
title = T("{0} {1} after", amount, T(unity).Text).Text;
|
||||
title = T("{0} {1} after", amount, T(unity));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1 +1 @@
|
||||
<h1 id="page-title">@Model.Title.ToString()</h1>
|
||||
<h1 id="page-title">@Model.Title</h1>
|
@@ -2,6 +2,10 @@
|
||||
using System.Web;
|
||||
|
||||
namespace Orchard.Localization {
|
||||
|
||||
/// <summary>
|
||||
/// An HTML-encoded localized string
|
||||
/// </summary>
|
||||
public class LocalizedString : MarshalByRefObject, IHtmlString {
|
||||
private readonly string _localized;
|
||||
private readonly string _scope;
|
||||
@@ -30,6 +34,9 @@ namespace Orchard.Localization {
|
||||
get { return _scope; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The HTML-Encoded original text
|
||||
/// </summary>
|
||||
public string TextHint {
|
||||
get { return _textHint; }
|
||||
}
|
||||
@@ -38,10 +45,16 @@ namespace Orchard.Localization {
|
||||
get { return _args; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The HTML-encoded localized text
|
||||
/// </summary>
|
||||
public string Text {
|
||||
get { return _localized; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The HTML-encoded localized text
|
||||
/// </summary>
|
||||
public override string ToString() {
|
||||
return _localized;
|
||||
}
|
||||
|
@@ -2,6 +2,13 @@ using System.Linq;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Localization {
|
||||
|
||||
/// <summary>
|
||||
/// Localizes some text based on the current Work Context culture
|
||||
/// </summary>
|
||||
/// <param name="text">The text format to localize</param>
|
||||
/// <param name="args">The arguments used in the text format. The arguments are HTML-encoded if they don't implement <see cref="System.Web.IHtmlString"/>.</param>
|
||||
/// <returns>An HTML-encoded localized string</returns>
|
||||
public delegate LocalizedString Localizer(string text, params object[] args);
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,8 @@ using System;
|
||||
using System.Globalization;
|
||||
using Orchard.Localization.Services;
|
||||
using Orchard.Logging;
|
||||
using System.Web;
|
||||
using System.Linq;
|
||||
|
||||
namespace Orchard.Localization {
|
||||
public class Text : IText {
|
||||
@@ -27,9 +29,15 @@ namespace Orchard.Localization {
|
||||
var currentCulture = workContext.CurrentCulture;
|
||||
var localizedFormat = _localizedStringManager.GetLocalizedString(_scope, textHint, currentCulture);
|
||||
|
||||
// localization arguments are HTML-encoded unless they implement IHtmlString
|
||||
|
||||
return args.Length == 0
|
||||
? new LocalizedString(localizedFormat, _scope, textHint, args)
|
||||
: new LocalizedString(string.Format(GetFormatProvider(currentCulture), localizedFormat, args), _scope, textHint, args);
|
||||
: new LocalizedString(
|
||||
String.Format(GetFormatProvider(currentCulture), localizedFormat, args.Select(Encode).ToArray()),
|
||||
_scope,
|
||||
textHint,
|
||||
args);
|
||||
}
|
||||
|
||||
return new LocalizedString(textHint, _scope, textHint, args);
|
||||
@@ -43,5 +51,14 @@ namespace Orchard.Localization {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static object Encode(object arg)
|
||||
{
|
||||
if (arg is IFormattable || arg is IHtmlString) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
return HttpUtility.HtmlEncode(arg);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user