Adding support for editor groups. Still need to hook up dedup and ordering of groupinfos in GetEditorGroupInfos and GetDisplayGroupInfos.

--HG--
branch : dev
This commit is contained in:
Nathan Heskew
2011-02-22 14:51:04 -08:00
parent ec0dfec53f
commit c5766465d3
19 changed files with 263 additions and 35 deletions

View File

@@ -189,6 +189,7 @@
<Compile Include="Settings\ResourceManifest.cs" />
<Compile Include="Settings\Migrations.cs" />
<Compile Include="Settings\Drivers\SiteSettingsPartDriver.cs" />
<Compile Include="Settings\Routes.cs" />
<Compile Include="Settings\ViewModels\SiteCulturesViewModel.cs" />
<Compile Include="Settings\Metadata\ContentDefinitionManager.cs" />
<Compile Include="Settings\Metadata\Records\ContentFieldDefinitionRecord.cs" />

View File

@@ -1,16 +1,38 @@
using Orchard.Localization;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.Localization;
using Orchard.Security;
using Orchard.Settings;
using Orchard.UI.Navigation;
namespace Orchard.Core.Settings {
public class AdminMenu : INavigationProvider {
private readonly ISiteService _siteService;
public AdminMenu(ISiteService siteService, IOrchardServices orchardServices) {
_siteService = siteService;
Services = orchardServices;
}
public Localizer T { get; set; }
public string MenuName { get { return "admin"; } }
public IOrchardServices Services { get; private set; }
public void GetNavigation(NavigationBuilder builder) {
builder.Add(T("Configuration"), "50",
menu => menu.Add(T("Settings"), "10", item => item.Action("Index", "Admin", new { area = "Settings" })
builder.Add(T("Settings"), "50",
menu => menu.Add(T("General"), "0", item => item.Action("Index", "Admin", new { area = "Settings", groupInfoId = "Index" })
.Permission(StandardPermissions.SiteOwner)));
var site = _siteService.GetSiteSettings();
if (site == null)
return;
foreach (var groupInfo in Services.ContentManager.GetEditorGroupInfos(site.ContentItem)) {
GroupInfo info = groupInfo;
builder.Add(T("Settings"), "50",
menu => menu.Add(info.Name, info.Position, item => item.Action("Index", "Admin", new { area = "Settings", groupInfoId = info.Id })
.Permission(StandardPermissions.SiteOwner)));
}
}
}
}

View File

@@ -1,6 +1,8 @@
using System.Globalization;
using System;
using System.Globalization;
using System.Linq;
using System.Web.Mvc;
using Orchard.ContentManagement.Handlers;
using Orchard.Core.Settings.ViewModels;
using Orchard.Localization;
using Orchard.ContentManagement;
@@ -28,25 +30,55 @@ namespace Orchard.Core.Settings.Controllers {
public Localizer T { get; set; }
public ActionResult Index(string tabName) {
public ActionResult Index(string groupInfoId) {
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage settings")))
return new HttpUnauthorizedResult();
dynamic model = Services.ContentManager.BuildEditor(_siteService.GetSiteSettings());
dynamic model;
var site = _siteService.GetSiteSettings();
if (!string.IsNullOrWhiteSpace(groupInfoId)) {
model = Services.ContentManager.BuildEditor(site, groupInfoId);
if (model == null)
return HttpNotFound();
var groupInfo = Services.ContentManager.GetEditorGroupInfo(site, groupInfoId);
if (groupInfo == null)
return HttpNotFound();
model.GroupInfo = groupInfo;
}
else {
model = Services.ContentManager.BuildEditor(site);
}
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
[HttpPost, ActionName("Index")]
public ActionResult IndexPOST(string tabName) {
public ActionResult IndexPOST(string groupInfoId) {
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage settings")))
return new HttpUnauthorizedResult();
var site = _siteService.GetSiteSettings();
dynamic model = Services.ContentManager.UpdateEditor(site, this);
if (model == null) {
Services.TransactionManager.Cancel();
return HttpNotFound();
}
var groupInfo = Services.ContentManager.GetEditorGroupInfo(site, groupInfoId);
if (groupInfo == null) {
Services.TransactionManager.Cancel();
return HttpNotFound();
}
if (!ModelState.IsValid) {
Services.TransactionManager.Cancel();
model.GroupInfo = groupInfo;
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.Core.Settings.Controllers;
using Orchard.Mvc.Routes;
namespace Orchard.Core.Settings {
public class Routes : IRouteProvider {
public void GetRoutes(ICollection<RouteDescriptor> routes) {
foreach (var routeDescriptor in GetRoutes())
routes.Add(routeDescriptor);
}
public IEnumerable<RouteDescriptor> GetRoutes() {
return new[] {
new RouteDescriptor {
Route = new Route(
"Admin/Settings/{groupInfoId}",
new RouteValueDictionary {
{"area", "Settings"},
{"controller", "Admin"},
{"action", "Index"}
},
new RouteValueDictionary {
{"groupInfoId", new SettingsActionConstraint()}
},
new RouteValueDictionary {
{"area", "Settings"},
{"groupInfoId", ""}
},
new MvcRouteHandler())
}
};
}
}
public class SettingsActionConstraint : IRouteConstraint {
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
if (routeDirection == RouteDirection.UrlGeneration)
return true;
if (!values.ContainsKey(parameterName))
return false;
// just hard-coding to know action name strings for now
var potentialActionName = values[parameterName] as string;
return !string.IsNullOrWhiteSpace(potentialActionName)
&& potentialActionName != "Index"
&& potentialActionName != "Culture"
&& potentialActionName != "AddCulture"
&& potentialActionName != "DeleteCulture";
}
}
}

View File

@@ -1,4 +1,8 @@
@{ Layout.Title = T("Manage Settings").ToString(); }
@using Orchard.ContentManagement
@{
GroupInfo groupInfo = Model.GroupInfo;
Layout.Title = (groupInfo != null ? T("Manage Settings for {0}", groupInfo.Name) : T("Manage Settings")).ToString();
}
@using (Html.BeginFormAntiForgeryPost()) {
@Html.ValidationSummary()

View File

@@ -1,4 +1,5 @@
using Orchard.Comments.Models;
using System;
using Orchard.Comments.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.Localization;
@@ -13,12 +14,18 @@ namespace Orchard.Comments.Drivers {
protected override string Prefix { get { return "CommentSettings"; } }
protected override DriverResult Editor(CommentSettingsPart part, dynamic shapeHelper) {
protected override DriverResult Editor(CommentSettingsPart part, string groupInfoId, dynamic shapeHelper) {
if (!string.Equals(groupInfoId, "comments", StringComparison.OrdinalIgnoreCase))
return null;
return ContentShape("Parts_Comments_SiteSettings",
() => shapeHelper.EditorTemplate(TemplateName: "Parts.Comments.SiteSettings", Model: part.Record, Prefix: Prefix));
}
protected override DriverResult Editor(CommentSettingsPart part, IUpdateModel updater, dynamic shapeHelper) {
protected override DriverResult Editor(CommentSettingsPart part, IUpdateModel updater, string groupInfoId, dynamic shapeHelper) {
if (!string.Equals(groupInfoId, "comments", StringComparison.OrdinalIgnoreCase))
return null;
updater.TryUpdateModel(part.Record, Prefix, null, null);
return Editor(part, shapeHelper);
}

View File

@@ -1,14 +1,24 @@
using JetBrains.Annotations;
using Orchard.Comments.Models;
using Orchard.ContentManagement;
using Orchard.Data;
using Orchard.ContentManagement.Handlers;
using Orchard.Localization;
namespace Orchard.Comments.Handlers {
[UsedImplicitly]
public class CommentSettingsPartHandler : ContentHandler {
public CommentSettingsPartHandler(IRepository<CommentSettingsPartRecord> repository) {
T = NullLocalizer.Instance;
Filters.Add(new ActivatingFilter<CommentSettingsPart>("Site"));
Filters.Add(StorageFilter.For(repository));
}
public Localizer T { get; set; }
protected override void GetItemMetadata(GetContentItemMetadataContext context) {
base.GetItemMetadata(context);
context.Metadata.EditorGroupInfo.Add(new GroupInfo(T("Comments")));
}
}
}

View File

@@ -1,15 +1,25 @@
using JetBrains.Annotations;
using Orchard.ContentManagement;
using Orchard.Data;
using Orchard.ContentManagement.Handlers;
using Orchard.Localization;
using Orchard.Media.Models;
namespace Orchard.Media.Handlers {
[UsedImplicitly]
public class MediaSettingsPartHandler : ContentHandler {
public MediaSettingsPartHandler(IRepository<MediaSettingsPartRecord> repository) {
T = NullLocalizer.Instance;
Filters.Add(new ActivatingFilter<MediaSettingsPart>("Site"));
Filters.Add(StorageFilter.For(repository));
Filters.Add(new TemplateFilterForRecord<MediaSettingsPartRecord>("MediaSettings", "Parts/Media.MediaSettings"));
Filters.Add(new TemplateFilterForRecord<MediaSettingsPartRecord>("MediaSettings", "Parts/Media.MediaSettings", "media"));
}
public Localizer T { get; set; }
protected override void GetItemMetadata(GetContentItemMetadataContext context) {
base.GetItemMetadata(context);
context.Metadata.EditorGroupInfo.Add(new GroupInfo(T("Media")));
}
}
}

View File

@@ -1,15 +1,25 @@
using JetBrains.Annotations;
using Orchard.ContentManagement;
using Orchard.Data;
using Orchard.ContentManagement.Handlers;
using Orchard.Localization;
using Orchard.Users.Models;
namespace Orchard.Users.Handlers {
[UsedImplicitly]
public class RegistrationSettingsPartHandler : ContentHandler {
public RegistrationSettingsPartHandler(IRepository<RegistrationSettingsPartRecord> repository) {
T = NullLocalizer.Instance;
Filters.Add(new ActivatingFilter<RegistrationSettingsPart>("Site"));
Filters.Add(StorageFilter.For(repository));
Filters.Add(new TemplateFilterForRecord<RegistrationSettingsPartRecord>("RegistrationSettings", "Parts/Users.RegistrationSettings"));
Filters.Add(new TemplateFilterForRecord<RegistrationSettingsPartRecord>("RegistrationSettings", "Parts/Users.RegistrationSettings", "users"));
}
public Localizer T { get; set; }
protected override void GetItemMetadata(GetContentItemMetadataContext context) {
base.GetItemMetadata(context);
context.Metadata.EditorGroupInfo.Add(new GroupInfo(T("Users")));
}
}
}

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Web.Routing;
namespace Orchard.ContentManagement {
@@ -7,5 +8,7 @@ namespace Orchard.ContentManagement {
public RouteValueDictionary EditorRouteValues { get; set; }
public RouteValueDictionary CreateRouteValues { get; set; }
public RouteValueDictionary RemoveRouteValues { get; set; }
public readonly IList<GroupInfo> DisplayGroupInfo = new List<GroupInfo>();
public readonly IList<GroupInfo> EditorGroupInfo = new List<GroupInfo>();
}
}

View File

@@ -54,7 +54,7 @@ namespace Orchard.ContentManagement {
return context.Shape;
}
public dynamic BuildEditor(IContent content) {
public dynamic BuildEditor(IContent content, string groupInfoId) {
var contentTypeDefinition = content.ContentItem.TypeDefinition;
string stereotype;
if (!contentTypeDefinition.Settings.TryGetValue("Stereotype", out stereotype))
@@ -65,14 +65,15 @@ namespace Orchard.ContentManagement {
dynamic itemShape = CreateItemShape(actualShapeType);
itemShape.ContentItem = content.ContentItem;
var context = new BuildEditorContext(itemShape, content, _shapeFactory);
var context = new BuildEditorContext(itemShape, content, groupInfoId, _shapeFactory);
BindPlacement(context, null);
_handlers.Value.Invoke(handler => handler.BuildEditor(context), Logger);
return context.Shape;
}
public dynamic UpdateEditor(IContent content, IUpdateModel updater) {
public dynamic UpdateEditor(IContent content, IUpdateModel updater, string groupInfoId) {
var contentTypeDefinition = content.ContentItem.TypeDefinition;
string stereotype;
if (!contentTypeDefinition.Settings.TryGetValue("Stereotype", out stereotype))
@@ -83,10 +84,11 @@ namespace Orchard.ContentManagement {
dynamic itemShape = CreateItemShape(actualShapeType);
itemShape.ContentItem = content.ContentItem;
var context = new UpdateEditorContext(itemShape, content, updater, _shapeFactory);
var context = new UpdateEditorContext(itemShape, content, updater, groupInfoId, _shapeFactory);
BindPlacement(context, null);
_handlers.Value.Invoke(handler => handler.UpdateEditor(context), Logger);
return context.Shape;
}

View File

@@ -12,6 +12,7 @@ using Orchard.ContentManagement.Records;
using Orchard.Data;
using Orchard.Indexing;
using Orchard.Logging;
using Orchard.UI;
namespace Orchard.ContentManagement {
public class DefaultContentManager : IContentManager {
@@ -357,17 +358,36 @@ namespace Orchard.ContentManagement {
return context.Metadata;
}
public IList<GroupInfo> GetEditorGroupInfos(IContent content) {
var metadata = GetItemMetadata(content);
// todo: (heskew) dedup and order
return metadata.EditorGroupInfo;
}
public IList<GroupInfo> GetDisplayGroupInfos(IContent content) {
var metadata = GetItemMetadata(content);
// todo: (heskew) dedup and order
return metadata.DisplayGroupInfo;
}
public GroupInfo GetEditorGroupInfo(IContent content, string groupInfoId) {
return GetEditorGroupInfos(content).FirstOrDefault(gi => string.Equals(gi.Id, groupInfoId, StringComparison.OrdinalIgnoreCase));
}
public GroupInfo GetDisplayGroupInfo(IContent content, string groupInfoId) {
return GetDisplayGroupInfos(content).FirstOrDefault(gi => string.Equals(gi.Id, groupInfoId, StringComparison.OrdinalIgnoreCase));
}
public dynamic BuildDisplay(IContent content, string displayType = "") {
return _contentDisplay.Value.BuildDisplay(content, displayType);
}
public dynamic BuildEditor(IContent content) {
return _contentDisplay.Value.BuildEditor(content);
public dynamic BuildEditor(IContent content, string groupInfoId = "") {
return _contentDisplay.Value.BuildEditor(content, groupInfoId);
}
public dynamic UpdateEditor(IContent content, IUpdateModel updater) {
return _contentDisplay.Value.UpdateEditor(content, updater);
public dynamic UpdateEditor(IContent content, IUpdateModel updater, string groupInfoId = "") {
return _contentDisplay.Value.UpdateEditor(content, updater, groupInfoId);
}
public IContentQuery<ContentItem> Query() {

View File

@@ -15,17 +15,23 @@ namespace Orchard.ContentManagement.Drivers {
DriverResult IContentPartDriver.BuildEditor(BuildEditorContext context) {
var part = context.ContentItem.As<TContent>();
return part == null ? null : Editor(part, context.New);
return part == null
? null
: !string.IsNullOrWhiteSpace(context.GroupInfoId) ? Editor(part, context.GroupInfoId, context.New) : Editor(part, context.New);
}
DriverResult IContentPartDriver.UpdateEditor(UpdateEditorContext context) {
var part = context.ContentItem.As<TContent>();
return part == null ? null : Editor(part, context.Updater, context.New);
return part == null
? null
: !string.IsNullOrWhiteSpace(context.GroupInfoId) ? Editor(part, context.Updater, context.GroupInfoId, context.New) : Editor(part, context.Updater, context.New);
}
protected virtual DriverResult Display(TContent part, string displayType, dynamic shapeHelper) { return null; }
protected virtual DriverResult Editor(TContent part, dynamic shapeHelper) { return null; }
protected virtual DriverResult Editor(TContent part, string groupInfoId, dynamic shapeHelper) { return null; }
protected virtual DriverResult Editor(TContent part, IUpdateModel updater, dynamic shapeHelper) { return null; }
protected virtual DriverResult Editor(TContent part, IUpdateModel updater, string groupInfoId, dynamic shapeHelper) { return null; }
[Obsolete("Provided while transitioning to factory variations")]
public ContentShapeResult ContentShape(IShape shape) {

View File

@@ -0,0 +1,19 @@
using Orchard.Localization;
namespace Orchard.ContentManagement {
public class GroupInfo {
private string _position = "5";
public GroupInfo(LocalizedString name) {
Id = name.TextHint;
Name = name;
}
public string Id { get; set; }
public LocalizedString Name { get; set; }
public string Position {
get { return _position; }
set { _position = value; }
}
}
}

View File

@@ -2,8 +2,11 @@ using Orchard.DisplayManagement;
namespace Orchard.ContentManagement.Handlers {
public class BuildEditorContext : BuildShapeContext {
public BuildEditorContext(IShape model, IContent content, IShapeFactory shapeFactory)
public BuildEditorContext(IShape model, IContent content, string groupInfoId, IShapeFactory shapeFactory)
: base(model, content, shapeFactory) {
GroupInfoId = groupInfoId;
}
public string GroupInfoId { get; private set; }
}
}

View File

@@ -1,17 +1,23 @@
using Orchard.ContentManagement.Records;
using System;
using Orchard.ContentManagement.Records;
namespace Orchard.ContentManagement.Handlers {
public class TemplateFilterForRecord<TRecord> : TemplateFilterBase<ContentPart<TRecord>> where TRecord : ContentPartRecord, new() {
private readonly string _prefix;
private string _location;
private string _position;
private string _location = "Content";
private string _position = "5";
private readonly string _templateName;
private string _groupId;
public TemplateFilterForRecord(string prefix, string templateName) {
_prefix = prefix;
_templateName = templateName;
_location = "Content";
_position = "5";
}
public TemplateFilterForRecord(string prefix, string templateName, string groupId) {
_prefix = prefix;
_templateName = templateName;
_groupId = groupId;
}
public TemplateFilterForRecord<TRecord> Location(string location) {
@@ -24,12 +30,23 @@ namespace Orchard.ContentManagement.Handlers {
return this;
}
public TemplateFilterForRecord<TRecord> Group(string groupId) {
_groupId = groupId;
return this;
}
protected override void BuildEditorShape(BuildEditorContext context, ContentPart<TRecord> part) {
if (!string.IsNullOrWhiteSpace(_groupId) && !string.Equals(_groupId, context.GroupInfoId, StringComparison.OrdinalIgnoreCase))
return;
var templateShape = context.New.EditorTemplate(TemplateName: _templateName, Model: part.Record, Prefix: _prefix);
context.Shape.Zones[_location].Add(templateShape, _position);
}
protected override void UpdateEditorShape(UpdateEditorContext context, ContentPart<TRecord> part) {
if (!string.IsNullOrWhiteSpace(_groupId) && !string.Equals(_groupId, context.GroupInfoId, StringComparison.OrdinalIgnoreCase))
return;
context.Updater.TryUpdateModel(part.Record, _prefix, null, null);
BuildEditorShape(context, part);
}

View File

@@ -2,8 +2,8 @@ using Orchard.DisplayManagement;
namespace Orchard.ContentManagement.Handlers {
public class UpdateEditorContext : BuildEditorContext {
public UpdateEditorContext(IShape model, IContent content, IUpdateModel updater, IShapeFactory shapeFactory)
: base(model, content, shapeFactory) {
public UpdateEditorContext(IShape model, IContent content, IUpdateModel updater, string groupInfoId, IShapeFactory shapeFactory)
: base(model, content, groupInfoId, shapeFactory) {
Updater = updater;
}

View File

@@ -25,16 +25,20 @@ namespace Orchard.ContentManagement {
IContentQuery<ContentItem> Query();
ContentItemMetadata GetItemMetadata(IContent contentItem);
IList<GroupInfo> GetEditorGroupInfos(IContent contentItem);
IList<GroupInfo> GetDisplayGroupInfos(IContent contentItem);
GroupInfo GetEditorGroupInfo(IContent contentItem, string groupInfoId);
GroupInfo GetDisplayGroupInfo(IContent contentItem, string groupInfoId);
dynamic BuildDisplay(IContent content, string displayType = "");
dynamic BuildEditor(IContent content);
dynamic UpdateEditor(IContent content, IUpdateModel updater);
dynamic BuildEditor(IContent content, string groupInfoId = "");
dynamic UpdateEditor(IContent content, IUpdateModel updater, string groupInfoId = "");
}
public interface IContentDisplay : IDependency {
dynamic BuildDisplay(IContent content, string displayType = "");
dynamic BuildEditor(IContent content);
dynamic UpdateEditor(IContent content, IUpdateModel updater);
dynamic BuildEditor(IContent content, string groupInfoId = "");
dynamic UpdateEditor(IContent content, IUpdateModel updater, string groupInfoId = "");
}
public class VersionOptions {

View File

@@ -154,6 +154,7 @@
<Compile Include="ContentManagement\ContentPartBehavior.cs" />
<Compile Include="ContentManagement\DefaultContentDisplay.cs" />
<Compile Include="ContentManagement\Drivers\ContentShapeResult.cs" />
<Compile Include="ContentManagement\GroupInfo.cs" />
<Compile Include="ContentManagement\Handlers\BuildShapeContext.cs" />
<Compile Include="ContentManagement\IContentBehavior.cs" />
<Compile Include="ContentManagement\Utilities\ComputedField.cs" />