mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-26 20:16:15 +08:00
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:
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -0,0 +1 @@
|
||||
@T("Rendering mode: {0}", Model.ContentPart.RenderingMode)
|
||||
Reference in New Issue
Block a user