Projections: "RewriteOutput" bool property of a Layout Property migrated to a tokenized condition text, fixes #6928 (#8275)

This commit is contained in:
Benedek Farkas
2019-09-05 22:08:55 +02:00
committed by GitHub
parent 5815a2b9fa
commit 5668919dd4
9 changed files with 85 additions and 49 deletions

View File

@@ -10,7 +10,6 @@ using Orchard.Localization;
using Orchard.Projections.Models; using Orchard.Projections.Models;
using Orchard.Projections.Services; using Orchard.Projections.Services;
using Orchard.Projections.ViewModels; using Orchard.Projections.ViewModels;
using Orchard.Security;
using Orchard.UI.Admin; using Orchard.UI.Admin;
using Orchard.UI.Notify; using Orchard.UI.Notify;
@@ -59,7 +58,7 @@ namespace Orchard.Projections.Controllers {
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
var property = _repository.Get(propertyId); var property = _repository.Get(propertyId);
if(property == null) { if (property == null) {
return HttpNotFound(); return HttpNotFound();
} }
@@ -82,8 +81,8 @@ namespace Orchard.Projections.Controllers {
} }
var viewModel = new PropertyEditViewModel { var viewModel = new PropertyEditViewModel {
Id = id, Id = id,
Description = String.Empty, Description = String.Empty,
Property = property Property = property
}; };
@@ -123,7 +122,7 @@ namespace Orchard.Projections.Controllers {
viewModel.ZeroIsEmpty = propertyRecord.ZeroIsEmpty; viewModel.ZeroIsEmpty = propertyRecord.ZeroIsEmpty;
viewModel.HideEmpty = propertyRecord.HideEmpty; viewModel.HideEmpty = propertyRecord.HideEmpty;
viewModel.RewriteOutput = propertyRecord.RewriteOutput; viewModel.RewriteOutputCondition = propertyRecord.RewriteOutputCondition;
viewModel.RewriteText = propertyRecord.RewriteText; viewModel.RewriteText = propertyRecord.RewriteText;
viewModel.StripHtmlTags = propertyRecord.StripHtmlTags; viewModel.StripHtmlTags = propertyRecord.StripHtmlTags;
viewModel.TrimLength = propertyRecord.TrimLength; viewModel.TrimLength = propertyRecord.TrimLength;
@@ -158,8 +157,8 @@ namespace Orchard.Projections.Controllers {
// add new property record if it's a newly created property // add new property record if it's a newly created property
if (propertyRecord == null) { if (propertyRecord == null) {
propertyRecord = new PropertyRecord { propertyRecord = new PropertyRecord {
Category = category, Category = category,
Type = type, Type = type,
Position = layout.Properties.Count Position = layout.Properties.Count
}; };
layout.Properties.Add(propertyRecord); layout.Properties.Add(propertyRecord);
@@ -190,7 +189,7 @@ namespace Orchard.Projections.Controllers {
propertyRecord.ZeroIsEmpty = model.ZeroIsEmpty; propertyRecord.ZeroIsEmpty = model.ZeroIsEmpty;
propertyRecord.HideEmpty = model.HideEmpty; propertyRecord.HideEmpty = model.HideEmpty;
propertyRecord.RewriteOutput = model.RewriteOutput; propertyRecord.RewriteOutputCondition = model.RewriteOutputCondition;
propertyRecord.RewriteText = model.RewriteText; propertyRecord.RewriteText = model.RewriteText;
propertyRecord.StripHtmlTags = model.StripHtmlTags; propertyRecord.StripHtmlTags = model.StripHtmlTags;
propertyRecord.TrimLength = model.TrimLength; propertyRecord.TrimLength = model.TrimLength;
@@ -217,9 +216,11 @@ namespace Orchard.Projections.Controllers {
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
switch (direction) { switch (direction) {
case "up": _propertyService.MoveUp(id); case "up":
_propertyService.MoveUp(id);
break; break;
case "down": _propertyService.MoveDown(id); case "down":
_propertyService.MoveDown(id);
break; break;
default: default:
throw new ArgumentException("direction"); throw new ArgumentException("direction");

View File

@@ -232,7 +232,7 @@ namespace Orchard.Projections.Drivers {
new XAttribute("MaxLength", property.MaxLength), new XAttribute("MaxLength", property.MaxLength),
new XAttribute("NoResultText", property.NoResultText ?? ""), new XAttribute("NoResultText", property.NoResultText ?? ""),
new XAttribute("PreserveLines", property.PreserveLines), new XAttribute("PreserveLines", property.PreserveLines),
new XAttribute("RewriteOutput", property.RewriteOutput), new XAttribute("RewriteOutputCondition", property.RewriteOutputCondition ?? ""),
new XAttribute("RewriteText", property.RewriteText ?? ""), new XAttribute("RewriteText", property.RewriteText ?? ""),
new XAttribute("StripHtmlTags", property.StripHtmlTags), new XAttribute("StripHtmlTags", property.StripHtmlTags),
new XAttribute("TrimLength", property.TrimLength), new XAttribute("TrimLength", property.TrimLength),
@@ -270,7 +270,10 @@ namespace Orchard.Projections.Drivers {
NoResultText = property.Attribute("NoResultText").Value, NoResultText = property.Attribute("NoResultText").Value,
Position = Convert.ToInt32(property.Attribute("Position").Value), Position = Convert.ToInt32(property.Attribute("Position").Value),
PreserveLines = Convert.ToBoolean(property.Attribute("PreserveLines").Value), PreserveLines = Convert.ToBoolean(property.Attribute("PreserveLines").Value),
RewriteOutput = Convert.ToBoolean(property.Attribute("RewriteOutput").Value), // RewriteOutput is processed to ensure backwards-compatibility with recipes
// that were created before RewriteOutputCondition was added.
RewriteOutputCondition = property.Attribute("RewriteOutputCondition")?.Value ??
property.Attribute("RewriteOutput")?.Value,
RewriteText = property.Attribute("RewriteText").Value, RewriteText = property.Attribute("RewriteText").Value,
State = property.Attribute("State").Value, State = property.Attribute("State").Value,
StripHtmlTags = Convert.ToBoolean(property.Attribute("StripHtmlTags").Value), StripHtmlTags = Convert.ToBoolean(property.Attribute("StripHtmlTags").Value),

View File

@@ -11,9 +11,14 @@ using Orchard.Projections.Models;
namespace Orchard.Projections { namespace Orchard.Projections {
public class Migrations : DataMigrationImpl { public class Migrations : DataMigrationImpl {
private readonly IRepository<MemberBindingRecord> _memberBindingRepository; private readonly IRepository<MemberBindingRecord> _memberBindingRepository;
private readonly IRepository<PropertyRecord> _propertyRecordRepository;
public Migrations(IRepository<MemberBindingRecord> memberBindingRepository) { public Migrations(
IRepository<MemberBindingRecord> memberBindingRepository,
IRepository<PropertyRecord> propertyRecordRepository) {
_memberBindingRepository = memberBindingRepository; _memberBindingRepository = memberBindingRepository;
_propertyRecordRepository = propertyRecordRepository;
T = NullLocalizer.Instance; T = NullLocalizer.Instance;
} }
@@ -238,7 +243,7 @@ namespace Orchard.Projections {
DisplayName = T("Body Part Text").Text, DisplayName = T("Body Part Text").Text,
Description = T("The text from the Body part").Text Description = T("The text from the Body part").Text
}); });
SchemaBuilder.AlterTable("StringFieldIndexRecord", table => table SchemaBuilder.AlterTable("StringFieldIndexRecord", table => table
.CreateIndex("IDX_Orchard_Projections_StringFieldIndexRecord", "FieldIndexPartRecord_Id") .CreateIndex("IDX_Orchard_Projections_StringFieldIndexRecord", "FieldIndexPartRecord_Id")
); );
@@ -294,6 +299,7 @@ namespace Orchard.Projections {
return 4; return 4;
} }
public int UpdateFrom4() { public int UpdateFrom4() {
SchemaBuilder.AlterTable("StringFieldIndexRecord", table => table SchemaBuilder.AlterTable("StringFieldIndexRecord", table => table
.AddColumn<string>("LatestValue", c => c.WithLength(4000))); .AddColumn<string>("LatestValue", c => c.WithLength(4000)));
@@ -322,7 +328,19 @@ namespace Orchard.Projections {
SchemaBuilder.AlterTable("QueryPartRecord", table => table SchemaBuilder.AlterTable("QueryPartRecord", table => table
.AddColumn<string>("VersionScope", c => c.WithLength(15))); .AddColumn<string>("VersionScope", c => c.WithLength(15)));
return 5; return 5;
} }
public int UpdateFrom5() {
SchemaBuilder.AlterTable("PropertyRecord", table => table
.AddColumn<string>("RewriteOutputCondition", c => c.Unlimited())
);
foreach (var property in _propertyRecordRepository.Table)
if (property.RewriteOutput) property.RewriteOutputCondition = "true";
return 6;
}
} }
} }

View File

@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using System;
using System.ComponentModel.DataAnnotations;
namespace Orchard.Projections.Models { namespace Orchard.Projections.Models {
public class PropertyRecord { public class PropertyRecord {
@@ -42,7 +43,9 @@ namespace Orchard.Projections.Models {
public virtual bool HideEmpty { get; set; } public virtual bool HideEmpty { get; set; }
// Rewrite Result // Rewrite Result
[Obsolete("Set RewriteOutputCondition to \"true\" instead.")]
public virtual bool RewriteOutput { get; set; } public virtual bool RewriteOutput { get; set; }
public virtual string RewriteOutputCondition { get; set; }
public virtual string RewriteText { get; set; } public virtual string RewriteText { get; set; }
public virtual bool StripHtmlTags { get; set; } public virtual bool StripHtmlTags { get; set; }
public virtual bool TrimLength { get; set; } public virtual bool TrimLength { get; set; }

View File

@@ -6,4 +6,4 @@ Version: 1.10.3
OrchardVersion: 1.10.3 OrchardVersion: 1.10.3
Description: Provides methods to control how lists of content items are filtered and displayed Description: Provides methods to control how lists of content items are filtered and displayed
Category: Content Category: Content
Dependencies: Orchard.Tokens, Orchard.Forms, Feeds, Title Dependencies: Orchard.Tokens, Orchard.Conditions, Orchard.Forms, Feeds, Title

View File

@@ -150,6 +150,10 @@
<Name>Orchard.Core</Name> <Name>Orchard.Core</Name>
<Private>$(MvcBuildViews)</Private> <Private>$(MvcBuildViews)</Private>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\Orchard.Conditions\Orchard.Conditions.csproj">
<Project>{98251EAE-A41B-47B2-AA91-E28B8482DA70}</Project>
<Name>Orchard.Conditions</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Forms\Orchard.Forms.csproj"> <ProjectReference Include="..\Orchard.Forms\Orchard.Forms.csproj">
<Project>{642A49D7-8752-4177-80D6-BFBBCFAD3DE0}</Project> <Project>{642A49D7-8752-4177-80D6-BFBBCFAD3DE0}</Project>
<Name>Orchard.Forms</Name> <Name>Orchard.Forms</Name>

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Web.Mvc; using System.Web.Mvc;
using Orchard.Conditions.Services;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.DisplayManagement; using Orchard.DisplayManagement;
using Orchard.Environment; using Orchard.Environment;
@@ -13,9 +14,12 @@ using Orchard.Utility.Extensions;
namespace Orchard.Projections.Services { namespace Orchard.Projections.Services {
public class PropertyShapes : IDependency { public class PropertyShapes : IDependency {
private readonly Work<ITokenizer> _tokenizerWork; private readonly Work<ITokenizer> _tokenizerWork;
private readonly Work<IConditionManager> _conditionManagerWork;
private readonly Dictionary<string, bool> _evaluations = new Dictionary<string, bool>();
public PropertyShapes(Work<ITokenizer> tokenizerWork) { public PropertyShapes(Work<ITokenizer> tokenizerWork, Work<IConditionManager> conditionManagerWork) {
_tokenizerWork = tokenizerWork; _tokenizerWork = tokenizerWork;
_conditionManagerWork = conditionManagerWork;
T = NullLocalizer.Instance; T = NullLocalizer.Instance;
} }
@@ -24,7 +28,7 @@ namespace Orchard.Projections.Services {
[Shape] [Shape]
public void Properties(dynamic Display, TextWriter Output, HtmlHelper Html, IEnumerable<dynamic> Items) { public void Properties(dynamic Display, TextWriter Output, HtmlHelper Html, IEnumerable<dynamic> Items) {
foreach (var item in Items) { foreach (var item in Items) {
if((bool)item.Property.ExcludeFromDisplay) { if ((bool)item.Property.ExcludeFromDisplay) {
continue; continue;
} }
@@ -32,54 +36,55 @@ namespace Orchard.Projections.Services {
} }
} }
[Shape] [Shape]
public void LayoutGroup(dynamic Display, TextWriter Output, HtmlHelper Html, dynamic Key, dynamic List) { public void LayoutGroup(dynamic Display, TextWriter Output, HtmlHelper Html, dynamic Key, dynamic List) {
Output.WriteLine(Display(Key)); Output.WriteLine(Display(Key));
Output.WriteLine(Display(List)); Output.WriteLine(Display(List));
} }
[Shape] [Shape]
public void PropertyWrapper( public void PropertyWrapper(
dynamic Display, dynamic Display,
TextWriter Output, TextWriter Output,
HtmlHelper Html, HtmlHelper Html,
UrlHelper Url, UrlHelper Url,
dynamic Item, dynamic Item,
ContentItem ContentItem, ContentItem ContentItem,
ContentItemMetadata ContentItemMetadata, ContentItemMetadata ContentItemMetadata,
PropertyRecord Property PropertyRecord Property) {
) { // Display will encode any string which is not IHtmlString.
// Display will encode any string which is not IHtmlString
string resultOutput = Convert.ToString(Display(Item)); string resultOutput = Convert.ToString(Display(Item));
var resultIsEmpty = String.IsNullOrEmpty(resultOutput) || (resultOutput == "0" && Property.ZeroIsEmpty);
if(Property.HideEmpty && resultIsEmpty) { var tokenData = new Dictionary<string, object> { { "Text", resultOutput }, { "Content", ContentItem } };
if (!string.IsNullOrWhiteSpace(Property.RewriteOutputCondition) &&
_conditionManagerWork.Value.Matches(_tokenizerWork.Value.Replace(Property.RewriteOutputCondition, tokenData)))
resultOutput = string.IsNullOrWhiteSpace(Property.RewriteText) ? "" : _tokenizerWork.Value.Replace(Property.RewriteText, tokenData);
var resultIsEmpty = string.IsNullOrEmpty(resultOutput) || (resultOutput == "0" && Property.ZeroIsEmpty);
if (Property.HideEmpty && resultIsEmpty) {
return; return;
} }
if(Property.RewriteOutput) { if (Property.StripHtmlTags) {
resultOutput = _tokenizerWork.Value.Replace(Property.RewriteText, new Dictionary<string, object> { { "Text", resultOutput }, { "Content", ContentItem } });
}
if(Property.StripHtmlTags) {
resultOutput = resultOutput.RemoveTags(); resultOutput = resultOutput.RemoveTags();
} }
if(Property.TrimLength) { if (Property.TrimLength) {
var ellipsis = Property.AddEllipsis ? "&#160;&#8230;" : ""; var ellipsis = Property.AddEllipsis ? "&#160;&#8230;" : "";
resultOutput = resultOutput.Ellipsize(Property.MaxLength, ellipsis, Property.TrimOnWordBoundary); resultOutput = resultOutput.Ellipsize(Property.MaxLength, ellipsis, Property.TrimOnWordBoundary);
} }
if(Property.TrimWhiteSpace) { if (Property.TrimWhiteSpace) {
resultOutput = resultOutput.Trim(); resultOutput = resultOutput.Trim();
} }
if(Property.PreserveLines) { if (Property.PreserveLines) {
using(var sw = new StringWriter()) { using (var sw = new StringWriter()) {
using(var sr = new StringReader(resultOutput)) { using (var sr = new StringReader(resultOutput)) {
string line; string line;
while(null != (line = sr.ReadLine())) { while (null != (line = sr.ReadLine())) {
sw.WriteLine(line); sw.WriteLine(line);
sw.WriteLine("<br />"); sw.WriteLine("<br />");
} }
@@ -89,7 +94,7 @@ namespace Orchard.Projections.Services {
} }
var wrapperTag = new TagBuilder(Property.CustomizeWrapperHtml && !String.IsNullOrEmpty(Property.CustomWrapperTag) ? Property.CustomWrapperTag : "div"); var wrapperTag = new TagBuilder(Property.CustomizeWrapperHtml && !String.IsNullOrEmpty(Property.CustomWrapperTag) ? Property.CustomWrapperTag : "div");
if (Property.CustomizeWrapperHtml && !String.IsNullOrEmpty(Property.CustomWrapperCss)) { if (Property.CustomizeWrapperHtml && !String.IsNullOrEmpty(Property.CustomWrapperCss)) {
wrapperTag.AddCssClass(_tokenizerWork.Value.Replace(Property.CustomWrapperCss, new Dictionary<string, object>())); wrapperTag.AddCssClass(_tokenizerWork.Value.Replace(Property.CustomWrapperCss, new Dictionary<string, object>()));
} }
@@ -113,11 +118,11 @@ namespace Orchard.Projections.Services {
if (!(Property.CustomizeLabelHtml && Property.CustomLabelTag == "-")) { if (!(Property.CustomizeLabelHtml && Property.CustomLabelTag == "-")) {
Output.Write(labelTag.ToString(TagRenderMode.EndTag)); Output.Write(labelTag.ToString(TagRenderMode.EndTag));
} }
} }
var propertyTag = new TagBuilder(Property.CustomizePropertyHtml && !String.IsNullOrEmpty(Property.CustomPropertyTag) ? Property.CustomPropertyTag : "span"); var propertyTag = new TagBuilder(Property.CustomizePropertyHtml && !String.IsNullOrEmpty(Property.CustomPropertyTag) ? Property.CustomPropertyTag : "span");
if (Property.CustomizePropertyHtml && !String.IsNullOrEmpty(Property.CustomPropertyCss)) { if (Property.CustomizePropertyHtml && !String.IsNullOrEmpty(Property.CustomPropertyCss)) {
propertyTag.AddCssClass(_tokenizerWork.Value.Replace(Property.CustomPropertyCss, new Dictionary<string, object>())); propertyTag.AddCssClass(_tokenizerWork.Value.Replace(Property.CustomPropertyCss, new Dictionary<string, object>()));
} }

View File

@@ -42,7 +42,7 @@ namespace Orchard.Projections.ViewModels {
public bool ZeroIsEmpty { get; set; } public bool ZeroIsEmpty { get; set; }
public bool HideEmpty { get; set; } public bool HideEmpty { get; set; }
public bool RewriteOutput { get; set; } public string RewriteOutputCondition { get; set; }
public string RewriteText { get; set; } public string RewriteText { get; set; }
public bool StripHtmlTags { get; set; } public bool StripHtmlTags { get; set; }
public bool TrimLength { get; set; } public bool TrimLength { get; set; }

View File

@@ -141,16 +141,18 @@
<div class="expando"> <div class="expando">
<div> <div>
<div> <div>
@Html.CheckBoxFor(m => m.RewriteOutput) <div>
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.RewriteOutput)">@T("Rewrite output")</label> <label for="@Html.FieldIdFor(m => m.RewriteOutputCondition)">@T("Rewrite output condition")</label>
<span class="hint">@T("Check to override the output of this property.")</span> @Html.TextBoxFor(m => m.RewriteOutputCondition, new { @class = "text large tokenized" })
<span class="hint">@T("A condition that will be evaluated to decide whether to rewrite the output or not.")</span>
</div>
</div> </div>
<div data-controllerid="@Html.FieldIdFor(m => m.RewriteOutput)"> <div>
<div> <div>
<label for="@Html.FieldIdFor(m => m.RewriteText)">@T("Rewrite text")</label> <label for="@Html.FieldIdFor(m => m.RewriteText)">@T("Rewrite text")</label>
@Html.TextBoxFor(m => m.RewriteText, new { @class = "text large tokenized" }) @Html.TextBoxFor(m => m.RewriteText, new { @class = "text large tokenized" })
<span class="hint">@T("The text to write for this field. It may include HTML. {Text} can be used to inject the current property text.")</span> <span class="hint">@T("The tokenized text that will be evaluated and applied if \"Rewrite output condition\" evaluates to true. It may include HTML and {Text} can be used to inject the current property text.")</span>
</div> </div>
</div> </div>