mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +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.Services;
|
||||||
using Orchard.Templates.ViewModels;
|
using Orchard.Templates.ViewModels;
|
||||||
using Orchard.Utility.Extensions;
|
using Orchard.Utility.Extensions;
|
||||||
|
using Orchard.Core.Title.Models;
|
||||||
|
|
||||||
namespace Orchard.Templates.Drivers {
|
namespace Orchard.Templates.Drivers {
|
||||||
public class ShapePartDriver : ContentPartDriver<ShapePart> {
|
public class ShapePartDriver : ContentPartDriver<ShapePart> {
|
||||||
private readonly IEnumerable<ITemplateProcessor> _processors;
|
private readonly IEnumerable<ITemplateProcessor> _processors;
|
||||||
private readonly ITransactionManager _transactions;
|
private readonly ITransactionManager _transactions;
|
||||||
|
private readonly IContentManager _contentManager;
|
||||||
|
|
||||||
public ShapePartDriver(
|
public ShapePartDriver(
|
||||||
IEnumerable<ITemplateProcessor> processors,
|
IEnumerable<ITemplateProcessor> processors,
|
||||||
ITransactionManager transactions) {
|
ITransactionManager transactions,
|
||||||
|
IContentManager contentManager) {
|
||||||
_processors = processors;
|
_processors = processors;
|
||||||
_transactions = transactions;
|
_transactions = transactions;
|
||||||
|
_contentManager = contentManager;
|
||||||
T = NullLocalizer.Instance;
|
T = NullLocalizer.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
Localizer T { get; set; }
|
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) {
|
protected override DriverResult Editor(ShapePart part, dynamic shapeHelper) {
|
||||||
return Editor(part, null, shapeHelper);
|
return Editor(part, null, shapeHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override DriverResult Editor(ShapePart part, IUpdateModel updater, dynamic shapeHelper) {
|
protected override DriverResult Editor(ShapePart part, IUpdateModel updater, dynamic shapeHelper) {
|
||||||
var viewModel = new ShapePartViewModel {
|
var viewModel = new ShapePartViewModel {
|
||||||
Template = part.Template
|
Template = part.Template,
|
||||||
|
RenderingMode = part.RenderingMode
|
||||||
};
|
};
|
||||||
|
|
||||||
if (updater != null
|
if (updater != null
|
||||||
&& updater.TryUpdateModel(viewModel, Prefix, null, new[] { "AvailableLanguages" })
|
&& updater.TryUpdateModel(viewModel, Prefix, null, new[] { "AvailableLanguages" })
|
||||||
&& ValidateShapeName(part, updater)) {
|
&& ValidateShapeName(part, updater)) {
|
||||||
part.Template = viewModel.Template;
|
part.Template = viewModel.Template;
|
||||||
|
part.RenderingMode = viewModel.RenderingMode;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var processor = _processors.FirstOrDefault(x => String.Equals(x.Type, part.ProcessorName, StringComparison.OrdinalIgnoreCase)) ?? _processors.First();
|
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));
|
updater.AddModelError("", T("Template processing error: {0}", ex.Message));
|
||||||
_transactions.Cancel();
|
_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));
|
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); }
|
get { return this.Retrieve(x => x.Template); }
|
||||||
set { this.Store(x => x.Template, value); }
|
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>
|
<ItemGroup>
|
||||||
<Compile Include="AdminMenu.cs" />
|
<Compile Include="AdminMenu.cs" />
|
||||||
<Compile Include="Controllers\AdminController.cs" />
|
<Compile Include="Controllers\AdminController.cs" />
|
||||||
<Compile Include="Drivers\TitlePartDriver.cs" />
|
|
||||||
<Compile Include="Helpers\StringExtensions.cs" />
|
<Compile Include="Helpers\StringExtensions.cs" />
|
||||||
<Compile Include="Handlers\ShapePartHandler.cs" />
|
<Compile Include="Handlers\ShapePartHandler.cs" />
|
||||||
<Compile Include="Drivers\ShapePartDriver.cs" />
|
<Compile Include="Drivers\ShapePartDriver.cs" />
|
||||||
@@ -227,6 +226,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="packages.config" />
|
<Content Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Views\Parts.Shape.SummaryAdmin.cshtml" />
|
||||||
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
<Placement>
|
<Placement>
|
||||||
<Place Parts_Shape_Edit="Content:0" />
|
<Place Parts_Shape_Edit="Content:0" />
|
||||||
|
|
||||||
|
<Match DisplayType="SummaryAdmin">
|
||||||
|
<Place Parts_Shape_SummaryAdmin="Content:5" />
|
||||||
|
</Match>
|
||||||
</Placement>
|
</Placement>
|
||||||
@@ -10,6 +10,8 @@ using System.Web;
|
|||||||
using System.Web.Mvc;
|
using System.Web.Mvc;
|
||||||
using Orchard.DisplayManagement;
|
using Orchard.DisplayManagement;
|
||||||
using Orchard.DisplayManagement.Descriptors;
|
using Orchard.DisplayManagement.Descriptors;
|
||||||
|
using System.Web.Routing;
|
||||||
|
using Orchard.UI.Admin;
|
||||||
|
|
||||||
namespace Orchard.Templates.Services {
|
namespace Orchard.Templates.Services {
|
||||||
public class TemplateShapeBindingResolver : IShapeBindingResolver {
|
public class TemplateShapeBindingResolver : IShapeBindingResolver {
|
||||||
@@ -18,26 +20,46 @@ namespace Orchard.Templates.Services {
|
|||||||
private IContentManager _contentManager;
|
private IContentManager _contentManager;
|
||||||
private IContentDefinitionManager _contentDefinitionManager;
|
private IContentDefinitionManager _contentDefinitionManager;
|
||||||
private ITemplateService _templateService;
|
private ITemplateService _templateService;
|
||||||
|
private readonly RequestContext _requestContext;
|
||||||
|
|
||||||
public TemplateShapeBindingResolver(
|
public TemplateShapeBindingResolver(
|
||||||
ICacheManager cacheManager,
|
ICacheManager cacheManager,
|
||||||
ISignals signals,
|
ISignals signals,
|
||||||
IContentManager contentManager,
|
IContentManager contentManager,
|
||||||
IContentDefinitionManager contentDefinitionManager,
|
IContentDefinitionManager contentDefinitionManager,
|
||||||
ITemplateService templateService
|
ITemplateService templateService,
|
||||||
) {
|
RequestContext requestContext) {
|
||||||
_cacheManager = cacheManager;
|
_cacheManager = cacheManager;
|
||||||
_signals = signals;
|
_signals = signals;
|
||||||
_contentManager = contentManager;
|
_contentManager = contentManager;
|
||||||
_contentDefinitionManager = contentDefinitionManager;
|
_contentDefinitionManager = contentDefinitionManager;
|
||||||
_templateService = templateService;
|
_templateService = templateService;
|
||||||
|
_requestContext = requestContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetDescriptorBinding(string shapeType, out ShapeBinding shapeBinding) {
|
public bool TryGetDescriptorBinding(string shapeType, out ShapeBinding shapeBinding) {
|
||||||
var processors = BuildShapeProcessors();
|
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;
|
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 {
|
shapeBinding = new ShapeBinding {
|
||||||
BindingName = "Templates",
|
BindingName = "Templates",
|
||||||
Binding = ctx => CoerceHtmlString(_templateService.Execute(
|
Binding = ctx => CoerceHtmlString(_templateService.Execute(
|
||||||
@@ -54,7 +76,7 @@ namespace Orchard.Templates.Services {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IDictionary<string, TemplateResult> BuildShapeProcessors() {
|
private ILookup<string, TemplateResult> BuildShapeProcessors() {
|
||||||
return _cacheManager.Get("Template.ShapeProcessors", true, ctx => {
|
return _cacheManager.Get("Template.ShapeProcessors", true, ctx => {
|
||||||
ctx.Monitor(_signals.When(DefaultTemplateService.TemplatesSignal));
|
ctx.Monitor(_signals.When(DefaultTemplateService.TemplatesSignal));
|
||||||
|
|
||||||
@@ -67,11 +89,12 @@ namespace Orchard.Templates.Services {
|
|||||||
|
|
||||||
var allTemplates = _contentManager.Query<ShapePart>(typesWithShapePart).List();
|
var allTemplates = _contentManager.Query<ShapePart>(typesWithShapePart).List();
|
||||||
|
|
||||||
return allTemplates.Select(x => new TemplateResult {
|
return allTemplates.Select(shapePart => new TemplateResult {
|
||||||
Name = x.Name,
|
Name = shapePart.Name,
|
||||||
Template = x.Template,
|
Template = shapePart.Template,
|
||||||
Processor = x.ProcessorName
|
Processor = shapePart.ProcessorName,
|
||||||
}).ToDictionary(x => x.Name, x => x);
|
RenderingMode = shapePart.RenderingMode
|
||||||
|
}).ToLookup(template => template.Name);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +106,7 @@ namespace Orchard.Templates.Services {
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Processor { get; set; }
|
public string Processor { get; set; }
|
||||||
public string Template { 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 class ShapePartViewModel {
|
||||||
public string Template { get; set; }
|
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("~/modules/orchard.templates/scripts/codemirror/lib/codemirror.css");
|
||||||
Style.Include("template-editor.css");
|
Style.Include("template-editor.css");
|
||||||
@@ -15,4 +16,8 @@
|
|||||||
<div>
|
<div>
|
||||||
@Html.TextAreaFor(m => m.Template, new { @class = "text large code-editor" })
|
@Html.TextAreaFor(m => m.Template, new { @class = "text large code-editor" })
|
||||||
</div>
|
</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>
|
</fieldset>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
@T("Rendering mode: {0}", Model.ContentPart.RenderingMode)
|
||||||
Reference in New Issue
Block a user