Roughing in extensibility model for content type definition editor

IEventHandler method invocation will aggregate return values of type IEnumerable<T>
Added IContentDefinitionEditorEvents and ContentDefinitionEditorEventsBase to framework assembly
ContentTypes editor fires events to build and apply additional type and typepart TemplateViewModel classes
As an example, added some content type settings to devtools "ShowDebugLinks"
In content type definition builder moved DisplayedAs(name) into it's own method
Added Html.RenderTemplates helper to execute renderpartial with prefix directly to bypass (for some cases) Html.EditorXxx, Html.DisplayXxx

--HG--
branch : dev
This commit is contained in:
Louis DeJardin
2010-06-25 10:11:50 -07:00
parent 185c7d125b
commit c35369fabc
22 changed files with 370 additions and 72 deletions

View File

@@ -1,7 +1,11 @@
using System.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.ViewModels;
using Orchard.ContentTypes.Services;
using Orchard.ContentTypes.ViewModels;
using Orchard.Data;
@@ -14,20 +18,26 @@ namespace Orchard.ContentTypes.Controllers {
public class AdminController : Controller {
private readonly INotifier _notifier;
private readonly IContentDefinitionService _contentDefinitionService;
private readonly IContentDefinitionManager _contentDefinitionManager;
private readonly IContentManager _contentManager;
private readonly ITransactionManager _transactionManager;
private readonly IContentDefinitionEditorEvents _extendViewModels;
public AdminController(
IOrchardServices orchardServices,
INotifier notifier,
IContentDefinitionService contentDefinitionService,
IContentDefinitionManager contentDefinitionManager,
IContentManager contentManager,
ITransactionManager transactionManager) {
ITransactionManager transactionManager,
IContentDefinitionEditorEvents extendViewModels) {
Services = orchardServices;
_notifier = notifier;
_contentDefinitionService = contentDefinitionService;
_contentDefinitionManager = contentDefinitionManager;
_contentManager = contentManager;
_transactionManager = transactionManager;
_extendViewModels = extendViewModels;
T = NullLocalizer.Instance;
Logger = NullLogger.Instance;
}
@@ -72,6 +82,19 @@ namespace Orchard.ContentTypes.Controllers {
return RedirectToAction("Index");
}
class Updater : IUpdateModel {
public AdminController Thunk { get; set; }
public Func<string, string> _prefix = x => x;
public bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) where TModel : class {
return Thunk.TryUpdateModel(model, _prefix(prefix), includeProperties, excludeProperties);
}
public void AddModelError(string key, LocalizedString errorMessage) {
Thunk.ModelState.AddModelError(_prefix(key), errorMessage.ToString());
}
}
public ActionResult Edit(string id) {
if (!Services.Authorizer.Authorize(Permissions.CreateContentTypes, T("Not allowed to edit a content type.")))
return new HttpUnauthorizedResult();
@@ -81,7 +104,19 @@ namespace Orchard.ContentTypes.Controllers {
if (contentTypeDefinition == null)
return new NotFoundResult();
return View(new EditTypeViewModel(contentTypeDefinition));
var viewModel = new EditTypeViewModel(contentTypeDefinition);
viewModel.Parts = viewModel.Parts.ToArray();
viewModel.Templates = _extendViewModels.TypeEditor(contentTypeDefinition);
var entries = viewModel.Parts.Join(contentTypeDefinition.Parts,
m => m.PartDefinition.Name,
d => d.PartDefinition.Name,
(model, definition) => new { model, definition });
foreach (var entry in entries) {
entry.model.Templates = _extendViewModels.TypePartEditor(entry.definition);
}
return View(viewModel);
}
[HttpPost, ActionName("Edit")]
@@ -94,26 +129,53 @@ namespace Orchard.ContentTypes.Controllers {
if (contentTypeDefinition == null)
return new NotFoundResult();
var updater = new Updater { Thunk = this };
var viewModel = new EditTypeViewModel();
TryUpdateModel(viewModel);
if (!ModelState.IsValid)
return Edit(id);
var contentTypeDefinitionParts = viewModel.Parts.Select(GenerateTypePart).ToList();
if (viewModel.Fields.Any())
contentTypeDefinitionParts.Add(GenerateTypePart(viewModel));
_contentDefinitionManager.AlterTypeDefinition(id, typeBuilder => {
//todo: apply the changes along the lines of but definately not resembling
// for now this _might_ just get a little messy ->
_contentDefinitionService.AlterTypeDefinition(
new ContentTypeDefinition(
viewModel.Name,
viewModel.DisplayName,
contentTypeDefinitionParts,
viewModel.Settings
)
);
typeBuilder.DisplayedAs(viewModel.DisplayName);
// allow extensions to alter type configuration
viewModel.Templates = _extendViewModels.TypeEditorUpdate(typeBuilder, updater);
foreach (var entry in viewModel.Parts.Select((part, index) => new { part, index })) {
var partViewModel = entry.part;
// enable updater to be aware of changing part prefix
var firstHalf = "Parts[" + entry.index + "].";
updater._prefix = secondHalf => firstHalf + secondHalf;
// allow extensions to alter typePart configuration
typeBuilder.WithPart(entry.part.PartDefinition.Name, typePartBuilder => {
partViewModel.Templates = _extendViewModels.TypePartEditorUpdate(typePartBuilder, updater);
});
}
});
if (!ModelState.IsValid) {
_transactionManager.Cancel();
return View(viewModel);
}
//var contentTypeDefinitionParts = viewModel.Parts.Select(GenerateTypePart).ToList();
//if (viewModel.Fields.Any())
// contentTypeDefinitionParts.Add(GenerateTypePart(viewModel));
////todo: apply the changes along the lines of but definately not resembling
//// for now this _might_ just get a little messy ->
//_contentDefinitionService.AlterTypeDefinition(
// new ContentTypeDefinition(
// viewModel.Name,
// viewModel.DisplayName,
// contentTypeDefinitionParts,
// viewModel.Settings
// )
// );
return RedirectToAction("Index");
}

View File

@@ -108,6 +108,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="App_Data\" />
<Folder Include="Helpers\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">

View File

@@ -1,12 +1,14 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.ViewModels;
using Orchard.Mvc.ViewModels;
namespace Orchard.ContentTypes.ViewModels {
public class EditTypeViewModel : BaseViewModel {
public EditTypeViewModel() {
Settings = new SettingsDictionary();
Fields = new List<EditPartFieldViewModel>();
Parts = new List<EditTypePartViewModel>();
}
public EditTypeViewModel(ContentTypeDefinition contentTypeDefinition) {
@@ -19,6 +21,8 @@ namespace Orchard.ContentTypes.ViewModels {
public string Name { get; set; }
public string DisplayName { get; set; }
public IEnumerable<TemplateViewModel> Templates { get; set; }
public SettingsDictionary Settings { get; set; }
public IEnumerable<EditPartFieldViewModel> Fields { get; set; }
public IEnumerable<EditTypePartViewModel> Parts { get; set; }
@@ -47,6 +51,7 @@ namespace Orchard.ContentTypes.ViewModels {
public EditPartViewModel PartDefinition { get; set; }
public SettingsDictionary Settings { get; set; }
public IEnumerable<TemplateViewModel> Templates { get; set; }
}
public class EditPartViewModel : BaseViewModel {

View File

@@ -1,5 +1,6 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<Orchard.ContentTypes.ViewModels.EditTypeViewModel>" %>
<%@ Import Namespace="Orchard.Core.Contents.ViewModels" %><%
<%@ Import Namespace="Orchard.Core.Contents.ViewModels" %>
<%@ Import Namespace="Orchard.ContentTypes.ViewModels" %><%
Html.RegisterStyle("admin.css"); %>
<h1><%:Html.TitleForPage(T("Edit Content Type").ToString())%></h1>
<p class="breadcrumb"><%:Html.ActionLink(T("Content Types").Text, "index") %><%:T(" &#62; ") %><%:T("Edit Content Type") %></p><%
@@ -13,7 +14,7 @@ using (Html.BeginFormAntiForgeryPost()) { %>
<%:Html.TextBoxFor(m => m.Name, new {@class = "textMedium", disabled = "disabled"}) %>
<%:Html.HiddenFor(m => m.Name) %>
</fieldset>
<%:Html.EditorFor(m => m.Settings) %>
<% Html.RenderTemplate(Model.Templates); %>
<h2><%:T("Parts") %></h2>
<div class="manage add-to-type"><%: Html.ActionLink(T("Add").Text, "AddPart", new { }, new { @class = "button" }) %></div>
<%:Html.EditorFor(m => m.Parts, "Parts", "") %>

View File

@@ -1,5 +1,6 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<Orchard.ContentTypes.ViewModels.EditTypePartViewModel>" %>
<%@ Import Namespace="Orchard.Core.Contents.ViewModels" %>
<%@ Import Namespace="Orchard.ContentTypes.ViewModels" %>
<fieldset class="manage-part">
<h3><%:Model.PartDefinition.Name %></h3>
<div class="manage">
@@ -11,7 +12,8 @@
<button type="submit" title="<%:T("Remove") %>"><%:T("Remove") %></button>
<% } %> --%>
</div>
<%:Html.EditorFor(m => m.Settings, "Settings", "") %>
<% Html.RenderTemplate(Model.Templates); %>
<h4><%:T("Global configuration") %></h4>
<div class="manage minor"><%:Html.ActionLink(T("Edit").Text, "EditPart", new { area = "Orchard.ContentTypes", id = Model.PartDefinition.Name }) %></div>
<%:Html.DisplayFor(m => m.PartDefinition.Settings, "Settings", "PartDefinition") %>

View File

@@ -7,10 +7,14 @@ namespace Orchard.DevTools.Handlers {
[UsedImplicitly]
public class DebugLinkHandler : ContentHandler {
protected override void BuildDisplayModel(BuildDisplayModelContext context) {
context.AddDisplay(new TemplateViewModel(new ShowDebugLink { ContentItem = context.ContentItem }) { TemplateName="Parts/DevTools.ShowDebugLink", ZoneName = "recap", Position = "9999" });
var devToolsSettings = context.ContentItem.TypeDefinition.Settings.GetModel<Settings.DevToolsSettings>();
if (devToolsSettings.ShowDebugLinks)
context.AddDisplay(new TemplateViewModel(new ShowDebugLink { ContentItem = context.ContentItem }) { TemplateName = "Parts/DevTools.ShowDebugLink", ZoneName = "recap", Position = "9999" });
}
protected override void BuildEditorModel(BuildEditorModelContext context) {
context.AddEditor(new TemplateViewModel(new ShowDebugLink { ContentItem = context.ContentItem }) { TemplateName = "Parts/DevTools.ShowDebugLink", ZoneName = "recap", Position = "9999" });
var devToolsSettings = context.ContentItem.TypeDefinition.Settings.GetModel<Settings.DevToolsSettings>();
if (devToolsSettings.ShowDebugLinks)
context.AddEditor(new TemplateViewModel(new ShowDebugLink { ContentItem = context.ContentItem }) { TemplateName = "Parts/DevTools.ShowDebugLink", ZoneName = "recap", Position = "9999" });
}
}
}

View File

@@ -80,6 +80,7 @@
<Compile Include="Models\Simple.cs" />
<Compile Include="Permissions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Settings\DevToolsSettings.cs" />
<Compile Include="ViewModels\ContentIndexViewModel.cs" />
<Compile Include="ViewModels\ContentDetailsViewModel.cs" />
<Compile Include="ViewModels\MetadataIndexViewModel.cs" />
@@ -91,6 +92,7 @@
<Content Include="ScaffoldingTemplates\ModuleCsProj.txt" />
<Content Include="ScaffoldingTemplates\ModuleManifest.txt" />
<Content Include="ScaffoldingTemplates\ModuleWebConfig.txt" />
<Content Include="Views\DefinitionTemplates\DevToolsSettings.ascx" />
<Content Include="Views\Home\_RenderableAction.ascx" />
<Content Include="Views\Home\Simple.aspx" />
<Content Include="Views\Content\Details.aspx" />

View File

@@ -0,0 +1,30 @@
using System.Collections.Generic;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.ViewModels;
namespace Orchard.DevTools.Settings {
public class DevToolsSettings {
public bool ShowDebugLinks { get; set; }
}
public class DevToolsSettingsHooks : ContentDefinitionEditorEventsBase {
public override IEnumerable<TemplateViewModel> TypeEditor(ContentTypeDefinition definition) {
var model = definition.Settings.GetModel<DevToolsSettings>();
yield return DefinitionTemplate(model);
}
public override IEnumerable<TemplateViewModel> TypeEditorUpdate(ContentTypeDefinitionBuilder builder, IUpdateModel updateModel) {
var model = new DevToolsSettings();
updateModel.TryUpdateModel(model, "DevToolsSettings", null, null);
builder
.WithSetting("DevToolsSettings.ShowDebugLinks", model.ShowDebugLinks ? true.ToString() : null);
yield return DefinitionTemplate(model);
}
}
}

View File

@@ -0,0 +1,6 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Orchard.DevTools.Settings.DevToolsSettings>" %>
<fieldset>
<%:Html.LabelFor(m=>m.ShowDebugLinks) %>
<%:Html.EditorFor(m=>m.ShowDebugLinks) %>
<%:Html.ValidationMessageFor(m=>m.ShowDebugLinks) %>
</fieldset>