Adding the ability to select whether a Template (content item) will be rendered on the frontend, admin, or both. Fixes #5579.

This commit is contained in:
Lombiq
2016-11-07 21:41:43 +01:00
committed by Zoltán Lehóczky
parent 68acf2694d
commit 8f2411dbf9
9 changed files with 95 additions and 65 deletions

View File

@@ -11,35 +11,45 @@ using Orchard.Templates.Models;
using Orchard.Templates.Services;
using Orchard.Templates.ViewModels;
using Orchard.Utility.Extensions;
using Orchard.Core.Title.Models;
namespace Orchard.Templates.Drivers {
public class ShapePartDriver : ContentPartDriver<ShapePart> {
private readonly IEnumerable<ITemplateProcessor> _processors;
private readonly ITransactionManager _transactions;
private readonly IContentManager _contentManager;
public ShapePartDriver(
IEnumerable<ITemplateProcessor> processors,
ITransactionManager transactions) {
ITransactionManager transactions,
IContentManager contentManager) {
_processors = processors;
_transactions = transactions;
_contentManager = contentManager;
T = NullLocalizer.Instance;
}
Localizer T { get; set; }
protected override DriverResult Display(ShapePart part, string displayType, dynamic shapeHelper) {
return ContentShape("Parts_Shape_SummaryAdmin", () => shapeHelper.Parts_Shape_SummaryAdmin());
}
protected override DriverResult Editor(ShapePart part, dynamic shapeHelper) {
return Editor(part, null, shapeHelper);
}
protected override DriverResult Editor(ShapePart part, IUpdateModel updater, dynamic shapeHelper) {
var viewModel = new ShapePartViewModel {
Template = part.Template
Template = part.Template,
RenderingMode = part.RenderingMode
};
if (updater != null
&& updater.TryUpdateModel(viewModel, Prefix, null, new[] { "AvailableLanguages" })
&& ValidateShapeName(part, updater)) {
part.Template = viewModel.Template;
part.RenderingMode = viewModel.RenderingMode;
try {
var processor = _processors.FirstOrDefault(x => String.Equals(x.Type, part.ProcessorName, StringComparison.OrdinalIgnoreCase)) ?? _processors.First();
@@ -49,6 +59,24 @@ namespace Orchard.Templates.Drivers {
updater.AddModelError("", T("Template processing error: {0}", ex.Message));
_transactions.Cancel();
}
// We need to query for the content type names because querying for content parts has no effect on the database side.
var contentTypesWithShapePart = _contentManager
.GetContentTypeDefinitions()
.Where(typeDefinition => typeDefinition.Parts.Any(partDefinition => partDefinition.PartDefinition.Name == "ShapePart"))
.Select(typeDefinition => typeDefinition.Name);
// If ShapePart is only dynamically added to this content type or even this content item then we won't find
// a corresponding content type definition, so using the current content type too.
contentTypesWithShapePart = contentTypesWithShapePart.Union(new[] { part.ContentItem.ContentType });
var existingShapes = _contentManager
.Query(VersionOptions.Latest, contentTypesWithShapePart.ToArray())
.Where<TitlePartRecord>(record => record.Title == part.As< TitlePart>().Title && record.ContentItemRecord.Id != part.ContentItem.Id);
if (existingShapes.List().Any(x => x.As<ShapePart>().RenderingMode == part.RenderingMode)) {
updater.AddModelError("ShapeNameAlreadyExists", T("A template with the given name and rendering mode already exists."));
}
}
return ContentShape("Parts_Shape_Edit", () => shapeHelper.EditorTemplate(TemplateName: "Parts.Shape", Model: viewModel, Prefix: Prefix));
}

View File

@@ -1,49 +0,0 @@
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.Core.Title.Models;
using Orchard.Localization;
using Orchard.Templates.Models;
using System.Linq;
namespace Orchard.Templates.Drivers {
public class TitlePartDriver : ContentPartDriver<TitlePart> {
private readonly IContentManager _contentManager;
public Localizer T { get; set; }
public TitlePartDriver(IContentManager contentManager) {
_contentManager = contentManager;
T = NullLocalizer.Instance;
}
protected override DriverResult Editor(TitlePart part, IUpdateModel updater, dynamic shapeHelper) {
if (!part.ContentItem.Has<ShapePart>()) {
return null;
}
updater.TryUpdateModel(part, Prefix, null, null);
// We need to query for the content type names because querying for content parts has no effect on the database side.
var contentTypesWithShapePart = _contentManager
.GetContentTypeDefinitions()
.Where(typeDefinition => typeDefinition.Parts.Any(partDefinition => partDefinition.PartDefinition.Name == "ShapePart"))
.Select(typeDefinition => typeDefinition.Name);
// If ShapePart is only dynamically added to this content type or even this content item then we won't find
// a corresponding content type definition, so using the current content type too.
contentTypesWithShapePart = contentTypesWithShapePart.Union(new[] { part.ContentItem.ContentType });
var existingShapeCount = _contentManager
.Query(VersionOptions.Latest, contentTypesWithShapePart.ToArray())
.Where<TitlePartRecord>(record => record.Title == part.Title && record.ContentItemRecord.Id != part.ContentItem.Id)
.Count();
if (existingShapeCount > 0) {
updater.AddModelError("ShapeNameAlreadyExists", T("A template with the given name already exists."));
}
return null;
}
}
}

View File

@@ -16,5 +16,16 @@ namespace Orchard.Templates.Models {
get { return this.Retrieve(x => x.Template); }
set { this.Store(x => x.Template, value); }
}
public RenderingMode RenderingMode {
get { return this.Retrieve(x => x.RenderingMode); }
set { this.Store(x => x.RenderingMode, value); }
}
}
public enum RenderingMode {
FrontEndAndAdmin,
FrontEnd,
Admin
}
}

View File

@@ -180,7 +180,6 @@
<ItemGroup>
<Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Drivers\TitlePartDriver.cs" />
<Compile Include="Helpers\StringExtensions.cs" />
<Compile Include="Handlers\ShapePartHandler.cs" />
<Compile Include="Drivers\ShapePartDriver.cs" />
@@ -227,6 +226,9 @@
<ItemGroup>
<Content Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Parts.Shape.SummaryAdmin.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@@ -1,3 +1,7 @@
<Placement>
<Place Parts_Shape_Edit="Content:0" />
<Place Parts_Shape_Edit="Content:0" />
<Match DisplayType="SummaryAdmin">
<Place Parts_Shape_SummaryAdmin="Content:5" />
</Match>
</Placement>

View File

@@ -10,6 +10,8 @@ using System.Web;
using System.Web.Mvc;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using System.Web.Routing;
using Orchard.UI.Admin;
namespace Orchard.Templates.Services {
public class TemplateShapeBindingResolver : IShapeBindingResolver {
@@ -18,30 +20,50 @@ namespace Orchard.Templates.Services {
private IContentManager _contentManager;
private IContentDefinitionManager _contentDefinitionManager;
private ITemplateService _templateService;
private readonly RequestContext _requestContext;
public TemplateShapeBindingResolver(
ICacheManager cacheManager,
ISignals signals,
IContentManager contentManager,
IContentDefinitionManager contentDefinitionManager,
ITemplateService templateService
) {
ITemplateService templateService,
RequestContext requestContext) {
_cacheManager = cacheManager;
_signals = signals;
_contentManager = contentManager;
_contentDefinitionManager = contentDefinitionManager;
_templateService = templateService;
_requestContext = requestContext;
}
public bool TryGetDescriptorBinding(string shapeType, out ShapeBinding shapeBinding) {
var processors = BuildShapeProcessors();
var acceptableRenderingModes = new List<RenderingMode>() { RenderingMode.FrontEndAndAdmin };
if (AdminFilter.IsApplied(_requestContext)) {
acceptableRenderingModes.Add(RenderingMode.Admin);
}
else {
acceptableRenderingModes.Add(RenderingMode.FrontEnd);
}
var templateResults = processors[shapeType].Where(template => acceptableRenderingModes.Contains(template.RenderingMode));
TemplateResult templateResult = null;
if (processors.TryGetValue(shapeType, out templateResult)) {
var templateResultsCount = templateResults.Count();
if (templateResultsCount == 1) {
templateResult = templateResults.FirstOrDefault();
}
else if (templateResultsCount > 1) {
// Templates with the same name but specified rendering mode are prioritized.
templateResult = templateResults.FirstOrDefault(template => template.RenderingMode != RenderingMode.FrontEndAndAdmin);
}
if (templateResult != null) {
shapeBinding = new ShapeBinding {
BindingName = "Templates",
Binding = ctx => CoerceHtmlString(_templateService.Execute(
templateResult.Template,
templateResult.Template,
templateResult.Name,
templateResult.Processor, ctx.Value)),
ShapeDescriptor = new ShapeDescriptor { ShapeType = shapeType }
@@ -54,7 +76,7 @@ namespace Orchard.Templates.Services {
return false;
}
private IDictionary<string, TemplateResult> BuildShapeProcessors() {
private ILookup<string, TemplateResult> BuildShapeProcessors() {
return _cacheManager.Get("Template.ShapeProcessors", true, ctx => {
ctx.Monitor(_signals.When(DefaultTemplateService.TemplatesSignal));
@@ -67,11 +89,12 @@ namespace Orchard.Templates.Services {
var allTemplates = _contentManager.Query<ShapePart>(typesWithShapePart).List();
return allTemplates.Select(x => new TemplateResult {
Name = x.Name,
Template = x.Template,
Processor = x.ProcessorName
}).ToDictionary(x => x.Name, x => x);
return allTemplates.Select(shapePart => new TemplateResult {
Name = shapePart.Name,
Template = shapePart.Template,
Processor = shapePart.ProcessorName,
RenderingMode = shapePart.RenderingMode
}).ToLookup(template => template.Name);
});
}
@@ -83,6 +106,7 @@ namespace Orchard.Templates.Services {
public string Name { get; set; }
public string Processor { get; set; }
public string Template { get; set; }
public RenderingMode RenderingMode { get; set; }
}
}
}

View File

@@ -1,5 +1,9 @@
namespace Orchard.Templates.ViewModels {
using Orchard.Templates.Models;
namespace Orchard.Templates.ViewModels {
public class ShapePartViewModel {
public string Template { get; set; }
public RenderingMode RenderingMode { get; set; }
}
}

View File

@@ -1,4 +1,5 @@
@model Orchard.Templates.ViewModels.ShapePartViewModel
@using Orchard.Templates.Models
@model Orchard.Templates.ViewModels.ShapePartViewModel
@{
Style.Include("~/modules/orchard.templates/scripts/codemirror/lib/codemirror.css");
Style.Include("template-editor.css");
@@ -15,4 +16,8 @@
<div>
@Html.TextAreaFor(m => m.Template, new { @class = "text large code-editor" })
</div>
<div>
@Html.LabelFor(m => m.RenderingMode, T("Rendering Mode"))
@Html.DropDownListFor(m => m.RenderingMode, Enum.GetValues(typeof(RenderingMode)).Cast<RenderingMode>().Select(renderingMode => new SelectListItem { Text = renderingMode.ToString(), Value = renderingMode.ToString() }))
</div>
</fieldset>

View File

@@ -0,0 +1 @@
@T("Rendering mode: {0}", Model.ContentPart.RenderingMode)