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

View File

@ -232,7 +232,7 @@ namespace Orchard.Projections.Drivers {
new XAttribute("MaxLength", property.MaxLength),
new XAttribute("NoResultText", property.NoResultText ?? ""),
new XAttribute("PreserveLines", property.PreserveLines),
new XAttribute("RewriteOutput", property.RewriteOutput),
new XAttribute("RewriteOutputCondition", property.RewriteOutputCondition ?? ""),
new XAttribute("RewriteText", property.RewriteText ?? ""),
new XAttribute("StripHtmlTags", property.StripHtmlTags),
new XAttribute("TrimLength", property.TrimLength),
@ -270,7 +270,10 @@ namespace Orchard.Projections.Drivers {
NoResultText = property.Attribute("NoResultText").Value,
Position = Convert.ToInt32(property.Attribute("Position").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,
State = property.Attribute("State").Value,
StripHtmlTags = Convert.ToBoolean(property.Attribute("StripHtmlTags").Value),

View File

@ -11,9 +11,14 @@ using Orchard.Projections.Models;
namespace Orchard.Projections {
public class Migrations : DataMigrationImpl {
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;
_propertyRecordRepository = propertyRecordRepository;
T = NullLocalizer.Instance;
}
@ -238,7 +243,7 @@ namespace Orchard.Projections {
DisplayName = T("Body Part Text").Text,
Description = T("The text from the Body part").Text
});
SchemaBuilder.AlterTable("StringFieldIndexRecord", table => table
.CreateIndex("IDX_Orchard_Projections_StringFieldIndexRecord", "FieldIndexPartRecord_Id")
);
@ -294,6 +299,7 @@ namespace Orchard.Projections {
return 4;
}
public int UpdateFrom4() {
SchemaBuilder.AlterTable("StringFieldIndexRecord", table => table
.AddColumn<string>("LatestValue", c => c.WithLength(4000)));
@ -322,7 +328,19 @@ namespace Orchard.Projections {
SchemaBuilder.AlterTable("QueryPartRecord", table => table
.AddColumn<string>("VersionScope", c => c.WithLength(15)));
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 {
public class PropertyRecord {
@ -42,7 +43,9 @@ namespace Orchard.Projections.Models {
public virtual bool HideEmpty { get; set; }
// Rewrite Result
[Obsolete("Set RewriteOutputCondition to \"true\" instead.")]
public virtual bool RewriteOutput { get; set; }
public virtual string RewriteOutputCondition { get; set; }
public virtual string RewriteText { get; set; }
public virtual bool StripHtmlTags { get; set; }
public virtual bool TrimLength { get; set; }

View File

@ -6,4 +6,4 @@ Version: 1.10.3
OrchardVersion: 1.10.3
Description: Provides methods to control how lists of content items are filtered and displayed
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>
<Private>$(MvcBuildViews)</Private>
</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">
<Project>{642A49D7-8752-4177-80D6-BFBBCFAD3DE0}</Project>
<Name>Orchard.Forms</Name>

View File

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

View File

@ -141,16 +141,18 @@
<div class="expando">
<div>
<div>
@Html.CheckBoxFor(m => m.RewriteOutput)
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.RewriteOutput)">@T("Rewrite output")</label>
<span class="hint">@T("Check to override the output of this property.")</span>
<div>
<label for="@Html.FieldIdFor(m => m.RewriteOutputCondition)">@T("Rewrite output condition")</label>
@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 data-controllerid="@Html.FieldIdFor(m => m.RewriteOutput)">
<div>
<div>
<label for="@Html.FieldIdFor(m => m.RewriteText)">@T("Rewrite text")</label>
@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>