mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-14 19:04:51 +08:00
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:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Autofac;
|
||||
@@ -28,9 +29,10 @@ namespace Orchard.Tests.Modules.Settings.Blueprint {
|
||||
public void Notify_Obsolete(string messageName, IDictionary<string, string> eventData) {
|
||||
}
|
||||
|
||||
public void Notify(string messageName, Dictionary<string, object> eventData) {
|
||||
public IEnumerable Notify(string messageName, Dictionary<string, object> eventData) {
|
||||
LastMessageName = messageName;
|
||||
LastEventData = eventData;
|
||||
return new object[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Autofac;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Events;
|
||||
@@ -13,10 +14,13 @@ namespace Orchard.Tests.Events {
|
||||
|
||||
[SetUp]
|
||||
public void Init() {
|
||||
var builder = new ContainerBuilder();
|
||||
_eventHandler = new StubEventHandler();
|
||||
builder.RegisterInstance(_eventHandler).As<IEventHandler>();
|
||||
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterType<DefaultOrchardEventBus>().As<IEventBus>();
|
||||
builder.RegisterType<StubEventHandler2>().As<IEventHandler>();
|
||||
builder.RegisterInstance(_eventHandler).As<IEventHandler>();
|
||||
|
||||
_container = builder.Build();
|
||||
_eventBus = _container.Resolve<IEventBus>();
|
||||
}
|
||||
@@ -139,6 +143,25 @@ namespace Orchard.Tests.Events {
|
||||
Assert.Throws<ArgumentException>(() => _eventBus.Notify("StubEventHandlerIncrement", new Dictionary<string, object>()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InterceptorCanCoerceResultingCollection() {
|
||||
var data = new object[]{"5","18","2"};
|
||||
var adjusted = EventsInterceptor.Adjust(data, typeof(IEnumerable<string>));
|
||||
Assert.That(data, Is.InstanceOf<IEnumerable<object>>());
|
||||
Assert.That(data, Is.Not.InstanceOf<IEnumerable<string>>());
|
||||
Assert.That(adjusted, Is.InstanceOf<IEnumerable<string>>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnumerableResultsAreTreatedLikeSelectMany() {
|
||||
var results = _eventBus.Notify("ITestEventHandler.Gather", new Dictionary<string, object> { { "a", 42 }, { "b", "alpha" } }).Cast<string>();
|
||||
Assert.That(results.Count(), Is.EqualTo(3));
|
||||
Assert.That(results, Has.Some.EqualTo("42"));
|
||||
Assert.That(results, Has.Some.EqualTo("alpha"));
|
||||
Assert.That(results, Has.Some.EqualTo("[42,alpha]"));
|
||||
}
|
||||
|
||||
|
||||
public interface ITestEventHandler : IEventHandler {
|
||||
void Increment();
|
||||
void Sum(int a);
|
||||
@@ -146,6 +169,7 @@ namespace Orchard.Tests.Events {
|
||||
void Sum(int a, int b, int c);
|
||||
void Substract(int a, int b);
|
||||
void Concat(string a, string b, string c);
|
||||
IEnumerable<string> Gather(int a, string b);
|
||||
}
|
||||
|
||||
public class StubEventHandler : ITestEventHandler {
|
||||
@@ -162,7 +186,7 @@ namespace Orchard.Tests.Events {
|
||||
}
|
||||
|
||||
public void Sum(int a, int b) {
|
||||
Result = 2 * ( a + b );
|
||||
Result = 2 * (a + b);
|
||||
}
|
||||
|
||||
public void Sum(int a, int b, int c) {
|
||||
@@ -176,7 +200,39 @@ namespace Orchard.Tests.Events {
|
||||
public void Concat(string a, string b, string c) {
|
||||
Summary = a + b + c;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> Gather(int a, string b) {
|
||||
yield return String.Format("[{0},{1}]", a, b);
|
||||
}
|
||||
}
|
||||
public class StubEventHandler2 : ITestEventHandler {
|
||||
public void Increment() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Sum(int a) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Sum(int a, int b) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Sum(int a, int b, int c) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Substract(int a, int b) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Concat(string a, string b, string c) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<string> Gather(int a, string b) {
|
||||
return new[] { a.ToString(), b };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +0,0 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<ContentTypeDefinition.Part>" %>
|
||||
<%@ Import Namespace="Orchard.ContentManagement.MetaData.Models" %>
|
||||
<fieldset>
|
||||
<h3><%:Model.PartDefinition.Name %></h3>
|
||||
<div class="manage add-to-type">
|
||||
<%--// these inline forms can't be here. should probably have some JavaScript in here to build up the forms and add the "remove" link.
|
||||
// get the antiforgery token from the edit type form and mark up the part in a semantic way so I can get some info from the DOM --%>
|
||||
<% using (Html.BeginFormAntiForgeryPost(Url.Action("RemovePart", new { area = "Contents" }), FormMethod.Post, new {@class = "inline link"})) { %>
|
||||
<%=Html.Hidden("name", Model.PartDefinition.Name, new { id = "" }) %>
|
||||
<button type="submit" title="<%:T("Remove") %>"><%:T("Remove") %></button>
|
||||
<% } %>
|
||||
</div>
|
||||
</fieldset>
|
@@ -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");
|
||||
}
|
||||
|
@@ -108,6 +108,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="App_Data\" />
|
||||
<Folder Include="Helpers\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
|
||||
|
@@ -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 {
|
||||
|
@@ -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(" > ") %><%: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", "") %>
|
||||
|
@@ -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") %>
|
||||
|
@@ -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" });
|
||||
}
|
||||
}
|
||||
}
|
@@ -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" />
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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>
|
@@ -100,7 +100,9 @@
|
||||
<Content Include="Default.aspx" />
|
||||
<Content Include="Global.asax" />
|
||||
<Content Include="Themes\TheAdmin\Styles\ie.css" />
|
||||
<Content Include="Web.config" />
|
||||
<Content Include="Web.config">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Orchard\Orchard.Framework.csproj">
|
||||
|
@@ -40,7 +40,7 @@ namespace Orchard.ContentManagement.MetaData.Builders {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentTypeDefinitionBuilder DisplayedAs(string displayName ) {
|
||||
public ContentTypeDefinitionBuilder DisplayedAs(string displayName) {
|
||||
_displayName = displayName;
|
||||
return this;
|
||||
}
|
||||
@@ -84,9 +84,12 @@ namespace Orchard.ContentManagement.MetaData.Builders {
|
||||
protected readonly SettingsDictionary _settings;
|
||||
|
||||
protected PartConfigurer(ContentTypeDefinition.Part part) {
|
||||
Name = part.PartDefinition.Name;
|
||||
_settings = new SettingsDictionary(part.Settings.ToDictionary(kv => kv.Key, kv => kv.Value));
|
||||
}
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public PartConfigurer WithSetting(string name, string value) {
|
||||
_settings[name] = value;
|
||||
return this;
|
||||
|
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement.MetaData.Builders;
|
||||
using Orchard.ContentManagement.MetaData.Models;
|
||||
using Orchard.ContentManagement.ViewModels;
|
||||
using Orchard.Events;
|
||||
|
||||
namespace Orchard.ContentManagement.MetaData {
|
||||
public interface IContentDefinitionEditorEvents : IEventHandler {
|
||||
IEnumerable<TemplateViewModel> TypeEditor(ContentTypeDefinition definition);
|
||||
IEnumerable<TemplateViewModel> TypePartEditor(ContentTypeDefinition.Part definition);
|
||||
|
||||
IEnumerable<TemplateViewModel> TypeEditorUpdate(ContentTypeDefinitionBuilder builder, IUpdateModel updateModel);
|
||||
IEnumerable<TemplateViewModel> TypePartEditorUpdate(ContentTypeDefinitionBuilder.PartConfigurer builder, IUpdateModel updateModel);
|
||||
}
|
||||
|
||||
public abstract class ContentDefinitionEditorEventsBase : IContentDefinitionEditorEvents {
|
||||
public virtual IEnumerable<TemplateViewModel> TypeEditor(ContentTypeDefinition definition) {
|
||||
return Enumerable.Empty<TemplateViewModel>();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<TemplateViewModel> TypePartEditor(ContentTypeDefinition.Part definition) {
|
||||
return Enumerable.Empty<TemplateViewModel>();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<TemplateViewModel> TypeEditorUpdate(ContentTypeDefinitionBuilder builder, IUpdateModel updateModel) {
|
||||
return Enumerable.Empty<TemplateViewModel>();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<TemplateViewModel> TypePartEditorUpdate(ContentTypeDefinitionBuilder.PartConfigurer builder, IUpdateModel updateModel) {
|
||||
return Enumerable.Empty<TemplateViewModel>();
|
||||
}
|
||||
|
||||
protected static TemplateViewModel DefinitionTemplate<TModel>(TModel model) {
|
||||
return new TemplateViewModel(model, typeof(TModel).Name) {
|
||||
TemplateName = "DefinitionTemplates/" + typeof(TModel).Name
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,22 +3,22 @@ using System.Web.Mvc;
|
||||
|
||||
namespace Orchard.ContentManagement.MetaData.Models {
|
||||
public class SettingsDictionary : Dictionary<string, string> {
|
||||
public SettingsDictionary() {}
|
||||
public SettingsDictionary(IDictionary<string, string> dictionary) : base(dictionary) {}
|
||||
public SettingsDictionary() { }
|
||||
public SettingsDictionary(IDictionary<string, string> dictionary) : base(dictionary) { }
|
||||
|
||||
public T GetModel<T>() {
|
||||
return GetModel<T>(null);
|
||||
public T GetModel<T>() where T : class, new() {
|
||||
return GetModel<T>(typeof(T).Name);
|
||||
}
|
||||
|
||||
public T GetModel<T>(string key) {
|
||||
public T GetModel<T>(string key) where T : class, new() {
|
||||
var binder = new DefaultModelBinder();
|
||||
var controllerContext = new ControllerContext();
|
||||
var context = new ModelBindingContext {
|
||||
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof (T)),
|
||||
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(T)),
|
||||
ModelName = key,
|
||||
ValueProvider = new DictionaryValueProvider<string>(this, null)
|
||||
};
|
||||
return (T) binder.BindModel(controllerContext, context);
|
||||
return (T)binder.BindModel(controllerContext, context) ?? new T();
|
||||
}
|
||||
}
|
||||
}
|
@@ -19,7 +19,7 @@ namespace Orchard.ContentManagement.MetaData.Services {
|
||||
if (source == null)
|
||||
return new XElement("settings");
|
||||
|
||||
return new XElement("settings", source.Select(kv => new XAttribute(XmlConvert.EncodeLocalName(kv.Key), kv.Value)));
|
||||
return new XElement("settings", source.Where(kv => kv.Value != null).Select(kv => new XAttribute(XmlConvert.EncodeLocalName(kv.Key), kv.Value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -18,9 +19,13 @@ namespace Orchard.Events {
|
||||
public ILogger Logger { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
#region Implementation of IEventBus
|
||||
|
||||
public void Notify(string messageName, Dictionary<string, object> eventData) {
|
||||
public IEnumerable Notify(string messageName, Dictionary<string, object> eventData) {
|
||||
// call ToArray to ensure evaluation has taken place
|
||||
return NotifyHandlers(messageName, eventData).ToArray();
|
||||
}
|
||||
|
||||
private IEnumerable<object> NotifyHandlers(string messageName, Dictionary<string, object> eventData) {
|
||||
string[] parameters = messageName.Split('.');
|
||||
if (parameters.Length != 2) {
|
||||
throw new ArgumentException(messageName + T(" is not formatted correctly"));
|
||||
@@ -30,40 +35,61 @@ namespace Orchard.Events {
|
||||
|
||||
var eventHandlers = _eventHandlers();
|
||||
foreach (var eventHandler in eventHandlers) {
|
||||
try {
|
||||
TryInvoke(eventHandler, interfaceName, methodName, eventData);
|
||||
}
|
||||
catch(Exception ex) {
|
||||
Logger.Error(ex, "{2} thrown from {0} by {1}",
|
||||
messageName,
|
||||
eventHandler.GetType().FullName,
|
||||
ex.GetType().Name);
|
||||
IEnumerable returnValue;
|
||||
if (TryNotifyHandler(eventHandler, messageName, interfaceName, methodName, eventData, out returnValue)) {
|
||||
if (returnValue != null) {
|
||||
foreach (var value in returnValue) {
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryInvoke(IEventHandler eventHandler, string interfaceName, string methodName, IDictionary<string, object> arguments) {
|
||||
private bool TryNotifyHandler(IEventHandler eventHandler, string messageName, string interfaceName, string methodName, Dictionary<string, object> eventData, out IEnumerable returnValue) {
|
||||
try {
|
||||
return TryInvoke(eventHandler, interfaceName, methodName, eventData, out returnValue);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Error(ex, "{2} thrown from {0} by {1}",
|
||||
messageName,
|
||||
eventHandler.GetType().FullName,
|
||||
ex.GetType().Name);
|
||||
|
||||
returnValue = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryInvoke(IEventHandler eventHandler, string interfaceName, string methodName, IDictionary<string, object> arguments, out IEnumerable returnValue) {
|
||||
Type type = eventHandler.GetType();
|
||||
foreach (var interfaceType in type.GetInterfaces()) {
|
||||
if (String.Equals(interfaceType.Name, interfaceName, StringComparison.OrdinalIgnoreCase)) {
|
||||
TryInvokeMethod(eventHandler, interfaceType, methodName, arguments);
|
||||
break;
|
||||
return TryInvokeMethod(eventHandler, interfaceType, methodName, arguments, out returnValue);
|
||||
}
|
||||
}
|
||||
returnValue = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void TryInvokeMethod(IEventHandler eventHandler, Type interfaceType, string methodName, IDictionary<string, object> arguments) {
|
||||
private static bool TryInvokeMethod(IEventHandler eventHandler, Type interfaceType, string methodName, IDictionary<string, object> arguments, out IEnumerable returnValue) {
|
||||
MethodInfo method = GetMatchingMethod(eventHandler, interfaceType, methodName, arguments);
|
||||
if (method != null) {
|
||||
List<object> parameters = new List<object>();
|
||||
foreach (var methodParameter in method.GetParameters()) {
|
||||
parameters.Add(arguments[methodParameter.Name]);
|
||||
}
|
||||
method.Invoke(eventHandler, parameters.ToArray());
|
||||
var result = method.Invoke(eventHandler, parameters.ToArray());
|
||||
returnValue = result as IEnumerable;
|
||||
if (returnValue == null && result != null)
|
||||
returnValue = new[] { result };
|
||||
return true;
|
||||
}
|
||||
returnValue = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static MethodInfo GetMatchingMethod(IEventHandler eventHandler, Type interfaceType, string methodName, IDictionary<string, object> arguments) {
|
||||
private static MethodInfo GetMatchingMethod(IEventHandler eventHandler, Type interfaceType, string methodName, IDictionary<string, object> arguments) {
|
||||
List<MethodInfo> allMethods = new List<MethodInfo>(interfaceType.GetMethods());
|
||||
List<MethodInfo> candidates = new List<MethodInfo>(allMethods);
|
||||
|
||||
@@ -89,6 +115,5 @@ namespace Orchard.Events {
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,8 @@
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Castle.Core.Interceptor;
|
||||
|
||||
namespace Orchard.Events {
|
||||
@@ -17,7 +21,36 @@ namespace Orchard.Events {
|
||||
.Select((parameter, index) => new { parameter.Name, Value = invocation.Arguments[index] })
|
||||
.ToDictionary(kv => kv.Name, kv => kv.Value);
|
||||
|
||||
_eventBus.Notify(interfaceName + "." + methodName, data);
|
||||
var results = _eventBus.Notify(interfaceName + "." + methodName, data);
|
||||
|
||||
invocation.ReturnValue = Adjust(results, invocation.Method.ReturnType);
|
||||
}
|
||||
|
||||
public static object Adjust(IEnumerable results, Type returnType) {
|
||||
if (returnType == typeof(void) ||
|
||||
results == null ||
|
||||
results.GetType() == returnType) {
|
||||
return results;
|
||||
}
|
||||
|
||||
// acquire method:
|
||||
// static IEnumerable<T> IEnumerable.OfType<T>(this IEnumerable source)
|
||||
// where T is from returnType's IEnumerable<T>
|
||||
var enumerableOfTypeT = typeof(Enumerable).GetGenericMethod("OfType", returnType.GetGenericArguments(), new[] { typeof(IEnumerable) }, typeof(IEnumerable<>));
|
||||
return enumerableOfTypeT.Invoke(null, new[] { results });
|
||||
}
|
||||
}
|
||||
|
||||
public static class Extensions {
|
||||
public static MethodInfo GetGenericMethod(this Type t, string name, Type[] genericArgTypes, Type[] argTypes, Type returnType) {
|
||||
return (from m in t.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||
where m.Name == name &&
|
||||
m.GetGenericArguments().Length == genericArgTypes.Length &&
|
||||
m.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(argTypes) &&
|
||||
(m.ReturnType.IsGenericType && !m.ReturnType.IsGenericTypeDefinition ? returnType.GetGenericTypeDefinition() : m.ReturnType) == returnType
|
||||
select m).Single().MakeGenericMethod(genericArgTypes);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,7 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.Events {
|
||||
public interface IEventBus : IDependency {
|
||||
void Notify(string messageName, Dictionary<string, object> eventData);
|
||||
IEnumerable Notify(string messageName, Dictionary<string, object> eventData);
|
||||
}
|
||||
}
|
||||
|
34
src/Orchard/Mvc/Html/TemplateViewModelExtensions.cs
Normal file
34
src/Orchard/Mvc/Html/TemplateViewModelExtensions.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Mvc.Html;
|
||||
using Orchard.ContentManagement.ViewModels;
|
||||
|
||||
namespace Orchard.Mvc.Html {
|
||||
public static class TemplateViewModelExtensions {
|
||||
public static void RenderTemplates(this HtmlHelper html, IEnumerable<TemplateViewModel> templates) {
|
||||
if (templates == null)
|
||||
return;
|
||||
|
||||
foreach (var template in templates) {
|
||||
html.RenderTemplates(template);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RenderTemplates(this HtmlHelper html, TemplateViewModel template) {
|
||||
if (template.WasUsed)
|
||||
return;
|
||||
|
||||
template.WasUsed = true;
|
||||
|
||||
var templateInfo = html.ViewContext.ViewData.TemplateInfo;
|
||||
var htmlFieldPrefix = templateInfo.HtmlFieldPrefix;
|
||||
try {
|
||||
templateInfo.HtmlFieldPrefix = templateInfo.GetFullHtmlFieldName(template.Prefix);
|
||||
html.RenderPartial(template.TemplateName, template.Model);
|
||||
}
|
||||
finally {
|
||||
templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -310,6 +310,7 @@
|
||||
<Compile Include="ContentManagement\MetaData\ContentPartInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ContentManagement\MetaData\IContentDefinitionEditorEvents.cs" />
|
||||
<Compile Include="ContentManagement\MetaData\Models\SettingsDictionary.cs" />
|
||||
<Compile Include="ContentManagement\MetaData\Services\ContentDefinitionWriter.cs" />
|
||||
<Compile Include="ContentManagement\MetaData\IContentDefinitionManager.cs" />
|
||||
@@ -445,6 +446,7 @@
|
||||
<Compile Include="Localization\Services\IResourceManager.cs" />
|
||||
<Compile Include="Localization\Records\CultureRecord.cs" />
|
||||
<Compile Include="Localization\Services\SiteCultureSelector.cs" />
|
||||
<Compile Include="Mvc\Html\TemplateViewModelExtensions.cs" />
|
||||
<Compile Include="Mvc\Html\ThemeExtensions.cs" />
|
||||
<Compile Include="Mvc\Routes\IRoutePublisher.cs" />
|
||||
<Compile Include="Mvc\Routes\IRouteProvider.cs" />
|
||||
|
Reference in New Issue
Block a user