mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-08-01 20:18:37 +08:00
Implementing admin placement for content type editors
--HG-- branch : 1.x
This commit is contained in:
parent
6d3bf98c8a
commit
e519781033
@ -222,6 +222,7 @@
|
||||
<Compile Include="Settings\Migrations.cs" />
|
||||
<Compile Include="Settings\Drivers\SiteSettingsPartDriver.cs" />
|
||||
<Compile Include="Settings\Routes.cs" />
|
||||
<Compile Include="Settings\Tokens\SettingsTokens.cs" />
|
||||
<Compile Include="Settings\ViewModels\SiteCulturesViewModel.cs" />
|
||||
<Compile Include="Settings\Metadata\ContentDefinitionManager.cs" />
|
||||
<Compile Include="Settings\Metadata\Records\ContentFieldDefinitionRecord.cs" />
|
||||
|
@ -1,14 +1,20 @@
|
||||
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.ContentTypes.Extensions;
|
||||
using Orchard.ContentTypes.Services;
|
||||
using Orchard.ContentTypes.Settings;
|
||||
using Orchard.ContentTypes.ViewModels;
|
||||
using Orchard.Core.Contents.Controllers;
|
||||
using Orchard.Core.Contents.Settings;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.UI;
|
||||
using Orchard.UI.Notify;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
@ -16,17 +22,30 @@ namespace Orchard.ContentTypes.Controllers {
|
||||
public class AdminController : Controller, IUpdateModel {
|
||||
private readonly IContentDefinitionService _contentDefinitionService;
|
||||
private readonly IContentDefinitionManager _contentDefinitionManager;
|
||||
private readonly IPlacementService _placementService;
|
||||
private readonly Lazy<IEnumerable<IShellSettingsManagerEventHandler>> _settingsManagerEventHandlers;
|
||||
private readonly ShellSettings _settings;
|
||||
|
||||
public AdminController(IOrchardServices orchardServices, IContentDefinitionService contentDefinitionService, IContentDefinitionManager contentDefinitionManager) {
|
||||
public AdminController(
|
||||
IOrchardServices orchardServices,
|
||||
IContentDefinitionService contentDefinitionService,
|
||||
IContentDefinitionManager contentDefinitionManager,
|
||||
IPlacementService placementService,
|
||||
Lazy<IEnumerable<IShellSettingsManagerEventHandler>> settingsManagerEventHandlers,
|
||||
ShellSettings settings
|
||||
) {
|
||||
Services = orchardServices;
|
||||
_contentDefinitionService = contentDefinitionService;
|
||||
_contentDefinitionManager = contentDefinitionManager;
|
||||
_placementService = placementService;
|
||||
_settingsManagerEventHandlers = settingsManagerEventHandlers;
|
||||
_settings = settings;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public IOrchardServices Services { get; private set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
public ActionResult Index() { return List(); }
|
||||
|
||||
#region Types
|
||||
@ -116,6 +135,89 @@ namespace Orchard.ContentTypes.Controllers {
|
||||
return View(typeViewModel);
|
||||
}
|
||||
|
||||
public ActionResult EditPlacement(string id) {
|
||||
if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(id);
|
||||
|
||||
if (contentTypeDefinition == null)
|
||||
return HttpNotFound();
|
||||
|
||||
var content = Services.ContentManager.New(id);
|
||||
var shape = Services.ContentManager.BuildEditor(content);
|
||||
|
||||
var placementModel = new EditPlacementViewModel {
|
||||
PlacementSettings = contentTypeDefinition.GetPlacement(PlacementType.Editor),
|
||||
AllPlacements = _placementService.GetEditorPlacement(id).OrderBy(x => x.PlacementSettings.Position, new FlatPositionComparer()).ThenBy(x => x.PlacementSettings.ShapeType).ToList(),
|
||||
ContentTypeDefinition = contentTypeDefinition,
|
||||
};
|
||||
|
||||
return View(placementModel);
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("EditPlacement")]
|
||||
[FormValueRequired("submit.Save")]
|
||||
public ActionResult EditPlacementPost(string id, EditPlacementViewModel viewModel) {
|
||||
if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(id);
|
||||
|
||||
if (contentTypeDefinition == null)
|
||||
return HttpNotFound();
|
||||
|
||||
var allPlacements = _placementService.GetEditorPlacement(id).ToList();
|
||||
var result = new List<PlacementSettings>(contentTypeDefinition.GetPlacement(PlacementType.Editor));
|
||||
|
||||
contentTypeDefinition.ResetPlacement(PlacementType.Editor);
|
||||
|
||||
foreach(var driverPlacement in viewModel.AllPlacements) {
|
||||
// if the placement has changed, persist it
|
||||
if (!allPlacements.Any(x => x.PlacementSettings.Equals(driverPlacement.PlacementSettings))) {
|
||||
result = result.Where(x => !x.IsSameAs(driverPlacement.PlacementSettings)).ToList();
|
||||
result.Add(driverPlacement.PlacementSettings);
|
||||
}
|
||||
}
|
||||
|
||||
foreach(var placementSetting in result) {
|
||||
contentTypeDefinition.Placement(PlacementType.Editor,
|
||||
placementSetting.ShapeType,
|
||||
placementSetting.Differentiator,
|
||||
placementSetting.Zone,
|
||||
placementSetting.Position);
|
||||
|
||||
}
|
||||
|
||||
// persist changes
|
||||
_contentDefinitionManager.StoreTypeDefinition(contentTypeDefinition);
|
||||
|
||||
_settingsManagerEventHandlers.Value.Invoke(x => x.Saved(_settings), Logger);
|
||||
|
||||
return RedirectToAction("EditPlacement", new {id});
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("EditPlacement")]
|
||||
[FormValueRequired("submit.Restore")]
|
||||
public ActionResult EditPlacementRestorePost(string id, EditPlacementViewModel viewModel) {
|
||||
if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(id);
|
||||
|
||||
if (contentTypeDefinition == null)
|
||||
return HttpNotFound();
|
||||
|
||||
contentTypeDefinition.ResetPlacement(PlacementType.Editor);
|
||||
|
||||
// persist changes
|
||||
_contentDefinitionManager.StoreTypeDefinition(contentTypeDefinition);
|
||||
|
||||
_settingsManagerEventHandlers.Value.Invoke(x => x.Saved(_settings), Logger);
|
||||
|
||||
return RedirectToAction("EditPlacement", new { id });
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("Edit")]
|
||||
[FormValueRequired("submit.Save")]
|
||||
public ActionResult EditPOST(string id) {
|
||||
|
@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web.Script.Serialization;
|
||||
using Orchard.ContentManagement.MetaData.Builders;
|
||||
using Orchard.ContentManagement.MetaData.Models;
|
||||
using Orchard.ContentTypes.Settings;
|
||||
|
||||
namespace Orchard.ContentTypes.Extensions {
|
||||
public enum PlacementType {
|
||||
Display,
|
||||
Editor
|
||||
}
|
||||
|
||||
public static class MetadataExtensions {
|
||||
/// <summary>
|
||||
/// Removes any custom placement for a specific shape
|
||||
/// </summary>
|
||||
public static ContentTypeDefinitionBuilder ResetPlacement(this ContentTypeDefinitionBuilder builder, PlacementType placementType, string shapeType, string differentiator) {
|
||||
var serializer = new JavaScriptSerializer();
|
||||
var placementSettings = GetPlacement(builder.Build(), placementType).ToList();
|
||||
|
||||
placementSettings = placementSettings.Where(x => x.ShapeType != shapeType && x.Differentiator != differentiator).ToList();
|
||||
|
||||
var placement = serializer.Serialize(placementSettings.ToArray());
|
||||
|
||||
return builder.WithSetting("ContentTypeSettings.Placement." + placementType, placement);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any custom placement
|
||||
/// </summary>
|
||||
public static ContentTypeDefinitionBuilder ResetPlacement(this ContentTypeDefinitionBuilder builder, PlacementType placementType) {
|
||||
return builder.WithSetting("ContentTypeSettings.Placement." + placementType, String.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines a custom placement
|
||||
/// </summary>
|
||||
public static ContentTypeDefinitionBuilder Placement(this ContentTypeDefinitionBuilder builder, PlacementType placementType, string shapeType, string differentiator, string zone, string position) {
|
||||
var serializer = new JavaScriptSerializer();
|
||||
var placementSettings = GetPlacement(builder.Build(), placementType).ToList();
|
||||
|
||||
placementSettings = placementSettings.Where(x => x.ShapeType != shapeType && x.Differentiator != differentiator).ToList();
|
||||
|
||||
placementSettings.Add(new PlacementSettings {
|
||||
ShapeType = shapeType,
|
||||
Differentiator = differentiator,
|
||||
Zone = zone,
|
||||
Position = position
|
||||
});
|
||||
|
||||
var placement = serializer.Serialize(placementSettings.ToArray());
|
||||
|
||||
return builder.WithSetting("ContentTypeSettings.Placement." + placementType, placement);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a placement the string representation of a placement
|
||||
/// </summary>
|
||||
public static ContentTypeDefinition Placement(this ContentTypeDefinition builder, PlacementType placementType, string shapeType, string differentiator, string zone, string position) {
|
||||
var serializer = new JavaScriptSerializer();
|
||||
var placementSettings = GetPlacement(builder, placementType).ToList();
|
||||
|
||||
placementSettings = placementSettings.Where(x => !x.IsSameAs(new PlacementSettings { ShapeType = shapeType, Differentiator = differentiator })).ToList();
|
||||
|
||||
placementSettings.Add(new PlacementSettings {
|
||||
ShapeType = shapeType,
|
||||
Differentiator = differentiator,
|
||||
Zone = zone,
|
||||
Position = position
|
||||
});
|
||||
|
||||
var placement = serializer.Serialize(placementSettings.ToArray());
|
||||
builder.Settings["ContentTypeSettings.Placement." + placementType] = placement;
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a placement the string representation of a placement
|
||||
/// </summary>
|
||||
public static ContentTypeDefinition ResetPlacement(this ContentTypeDefinition builder, PlacementType placementType) {
|
||||
builder.Settings["ContentTypeSettings.Placement." + placementType] = String.Empty;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static PlacementSettings[] GetPlacement(this ContentTypeDefinition contentTypeDefinition, PlacementType placementType) {
|
||||
var currentSettings = contentTypeDefinition.Settings;
|
||||
string placement;
|
||||
var serializer = new JavaScriptSerializer();
|
||||
|
||||
currentSettings.TryGetValue("ContentTypeSettings.Placement." + placementType, out placement);
|
||||
|
||||
return String.IsNullOrEmpty(placement) ? new PlacementSettings[0] : serializer.Deserialize<PlacementSettings[]>(placement);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -7,3 +7,4 @@ OrchardVersion: 1.4.1
|
||||
Description: ContentTypes modules enables the creation and alteration of content types not based on code.
|
||||
Dependencies: Contents
|
||||
Category: Content
|
||||
Priority: -10
|
||||
|
@ -35,12 +35,14 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ClaySharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
|
||||
@ -49,9 +51,14 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Extensions\MetadataExtensions.cs" />
|
||||
<Compile Include="ResourceManifest.cs" />
|
||||
<Compile Include="Extensions\StringExtensions.cs" />
|
||||
<Compile Include="Services\ContentTypePlacementStrategy.cs" />
|
||||
<Compile Include="Services\IPlacementService.cs" />
|
||||
<Compile Include="Services\PlacementService.cs" />
|
||||
<Compile Include="Settings\EditorEvents.cs" />
|
||||
<Compile Include="Settings\PlacementSettings.cs" />
|
||||
<Compile Include="ViewModels\AddPartsViewModel.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Permissions.cs" />
|
||||
@ -65,6 +72,7 @@
|
||||
<Compile Include="ViewModels\EditFieldViewModel.cs" />
|
||||
<Compile Include="ViewModels\EditPartFieldViewModel.cs" />
|
||||
<Compile Include="ViewModels\EditPartViewModel.cs" />
|
||||
<Compile Include="ViewModels\EditPlacementViewModel.cs" />
|
||||
<Compile Include="ViewModels\EditTypePartViewModel.cs" />
|
||||
<Compile Include="ViewModels\ListContentPartsViewModel.cs" />
|
||||
<Compile Include="ViewModels\RemoveFieldViewModel.cs" />
|
||||
@ -76,6 +84,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Module.txt" />
|
||||
<Content Include="Styles\Images\move.gif" />
|
||||
<Content Include="Styles\orchard-contenttypes-admin.css" />
|
||||
<Content Include="Views\Admin\AddFieldTo.cshtml" />
|
||||
<Content Include="Views\Admin\AddPartsTo.cshtml" />
|
||||
@ -115,6 +124,10 @@
|
||||
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
|
||||
<Name>Orchard.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Themes\Orchard.Themes.csproj">
|
||||
<Project>{CDE24A24-01D3-403C-84B9-37722E18DFB7}</Project>
|
||||
<Name>Orchard.Themes</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="web.config" />
|
||||
@ -125,6 +138,9 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Views\DefinitionTemplates\ContentTypeSettingsViewModel.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\EditPlacement.cshtml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.ContentTypes.Settings;
|
||||
using Orchard.DisplayManagement.Descriptors;
|
||||
using Orchard.ContentTypes.Extensions;
|
||||
using Orchard.Environment;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Environment.Extensions.Models;
|
||||
|
||||
namespace Orchard.ContentTypes.Services {
|
||||
public class TypePlacement {
|
||||
public PlacementSettings Placement { get; set; }
|
||||
public string ContentType { get; set; }
|
||||
}
|
||||
|
||||
public class ContentTypePlacementStrategy : IShapeTableEventHandler {
|
||||
private readonly Work<IContentDefinitionManager> _contentDefinitionManager;
|
||||
|
||||
public ContentTypePlacementStrategy(Work<IContentDefinitionManager> contentDefinitionManager) {
|
||||
_contentDefinitionManager = contentDefinitionManager;
|
||||
}
|
||||
|
||||
public virtual Feature Feature { get; set; }
|
||||
|
||||
public void ShapeTableCreated(ShapeTable shapeTable) {
|
||||
|
||||
var typeDefinitions = _contentDefinitionManager.Value.ListTypeDefinitions();
|
||||
var allPlacements = typeDefinitions.SelectMany(td => td.GetPlacement(PlacementType.Editor).Select(p => new TypePlacement { Placement = p, ContentType = td.Name }) );
|
||||
|
||||
// group all placement settings by shape type
|
||||
var shapePlacements = allPlacements.GroupBy(x => x.Placement.ShapeType).ToDictionary(x => x.Key, y=> y.ToList());
|
||||
|
||||
// create a new predicate in a ShapeTableDescriptor has a custom placement
|
||||
foreach(var shapeType in shapeTable.Descriptors.Keys) {
|
||||
List<TypePlacement> customPlacements;
|
||||
if(shapePlacements.TryGetValue(shapeType, out customPlacements)) {
|
||||
var descriptor = shapeTable.Descriptors[shapeType];
|
||||
// there are some custom placements, build a predicate
|
||||
var placement = descriptor.Placement;
|
||||
|
||||
foreach(var customPlacement in customPlacements) {
|
||||
// create local variables to prevent LINQ issues
|
||||
var placementShapeType = customPlacement.Placement.ShapeType;
|
||||
var type = customPlacement.ContentType;
|
||||
var differentiator = customPlacement.Placement.Differentiator;
|
||||
var location = customPlacement.Placement.Zone;
|
||||
if(!String.IsNullOrEmpty(customPlacement.Placement.Position)) {
|
||||
location = String.Concat(location, ":", customPlacement.Placement.Position);
|
||||
}
|
||||
|
||||
descriptor.Placement = ctx => {
|
||||
var nextPlacement = placement(ctx);
|
||||
if(ctx.DisplayType == null && ((ctx.Differentiator ?? "") == (differentiator ?? "")) && ctx.ContentType == type) {
|
||||
nextPlacement.Location = location;
|
||||
}
|
||||
|
||||
return nextPlacement;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentTypes.Settings;
|
||||
|
||||
namespace Orchard.ContentTypes.Services {
|
||||
public interface IPlacementService : IDependency {
|
||||
IEnumerable<DriverResultPlacement> GetDisplayPlacement(string contentType);
|
||||
IEnumerable<DriverResultPlacement> GetEditorPlacement(string contentType);
|
||||
IEnumerable<string> GetZones();
|
||||
}
|
||||
}
|
@ -0,0 +1,254 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Routing;
|
||||
using ClaySharp.Implementation;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Drivers;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.ContentTypes.Settings;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.DisplayManagement.Descriptors;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.FileSystems.VirtualPath;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Themes.Services;
|
||||
using Orchard.UI.Zones;
|
||||
|
||||
namespace Orchard.ContentTypes.Services {
|
||||
|
||||
public class DriverResultPlacement {
|
||||
public PlacementSettings PlacementSettings { get; set; }
|
||||
public DriverResult ShapeResult { get; set; }
|
||||
public dynamic Shape { get; set; }
|
||||
}
|
||||
|
||||
public class PlacementService : IPlacementService {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly ISiteThemeService _siteThemeService;
|
||||
private readonly IExtensionManager _extensionManager;
|
||||
private readonly IShapeFactory _shapeFactory;
|
||||
private readonly IShapeTableLocator _shapeTableLocator;
|
||||
private readonly RequestContext _requestContext;
|
||||
private readonly IEnumerable<IContentPartDriver> _contentPartDrivers;
|
||||
private readonly IEnumerable<IContentFieldDriver> _contentFieldDrivers;
|
||||
private readonly IVirtualPathProvider _virtualPathProvider;
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
|
||||
public PlacementService(
|
||||
IContentManager contentManager,
|
||||
ISiteThemeService siteThemeService,
|
||||
IExtensionManager extensionManager,
|
||||
IShapeFactory shapeFactory,
|
||||
IShapeTableLocator shapeTableLocator,
|
||||
RequestContext requestContext,
|
||||
IEnumerable<IContentPartDriver> contentPartDrivers,
|
||||
IEnumerable<IContentFieldDriver> contentFieldDrivers,
|
||||
IVirtualPathProvider virtualPathProvider,
|
||||
IWorkContextAccessor workContextAccessor
|
||||
)
|
||||
{
|
||||
_contentManager = contentManager;
|
||||
_siteThemeService = siteThemeService;
|
||||
_extensionManager = extensionManager;
|
||||
_shapeFactory = shapeFactory;
|
||||
_shapeTableLocator = shapeTableLocator;
|
||||
_requestContext = requestContext;
|
||||
_contentPartDrivers = contentPartDrivers;
|
||||
_contentFieldDrivers = contentFieldDrivers;
|
||||
_virtualPathProvider = virtualPathProvider;
|
||||
_workContextAccessor = workContextAccessor;
|
||||
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public IEnumerable<DriverResultPlacement> GetDisplayPlacement(string contentType) {
|
||||
var content = _contentManager.New(contentType);
|
||||
const string actualDisplayType = "Detail";
|
||||
|
||||
dynamic itemShape = CreateItemShape("Content");
|
||||
itemShape.ContentItem = content;
|
||||
itemShape.Metadata.DisplayType = actualDisplayType;
|
||||
|
||||
var context = new BuildDisplayContext(itemShape, content, actualDisplayType, String.Empty, _shapeFactory);
|
||||
BindPlacement(context, actualDisplayType, "Content");
|
||||
|
||||
var placementSettings = new List<DriverResultPlacement>();
|
||||
|
||||
_contentPartDrivers.Invoke(driver => {
|
||||
var result = driver.BuildDisplay(context);
|
||||
if (result != null) {
|
||||
placementSettings.AddRange(ExtractPlacement(result, context));
|
||||
}
|
||||
}, Logger);
|
||||
|
||||
_contentFieldDrivers.Invoke(driver => {
|
||||
var result = driver.BuildDisplayShape(context);
|
||||
if (result != null) {
|
||||
placementSettings.AddRange(ExtractPlacement(result, context));
|
||||
}
|
||||
}, Logger);
|
||||
|
||||
foreach (var placementSetting in placementSettings) {
|
||||
yield return placementSetting;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<DriverResultPlacement> GetEditorPlacement(string contentType) {
|
||||
var content = _contentManager.New(contentType);
|
||||
|
||||
dynamic itemShape = CreateItemShape("Content_Edit");
|
||||
itemShape.ContentItem = content;
|
||||
|
||||
var context = new BuildEditorContext(itemShape, content, String.Empty, _shapeFactory);
|
||||
BindPlacement(context, null, "Content");
|
||||
|
||||
var placementSettings = new List<DriverResultPlacement>();
|
||||
|
||||
_contentPartDrivers.Invoke(driver => {
|
||||
var result = driver.BuildEditor(context);
|
||||
if (result != null) {
|
||||
placementSettings.AddRange(ExtractPlacement(result, context));
|
||||
}
|
||||
}, Logger);
|
||||
|
||||
_contentFieldDrivers.Invoke(driver => {
|
||||
var result = driver.BuildEditorShape(context);
|
||||
if (result != null) {
|
||||
placementSettings.AddRange(ExtractPlacement(result, context));
|
||||
}
|
||||
}, Logger);
|
||||
|
||||
foreach (var placementSetting in placementSettings) {
|
||||
yield return placementSetting;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetZones() {
|
||||
var theme = _siteThemeService.GetSiteTheme();
|
||||
IEnumerable<string> zones = new List<string>();
|
||||
|
||||
// get the zones for this theme
|
||||
if (theme.Zones != null)
|
||||
zones = theme.Zones.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim())
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
// if this theme has no zones defined then walk the BaseTheme chain until we hit a theme which defines zones
|
||||
while (!zones.Any() && theme != null && !string.IsNullOrWhiteSpace(theme.BaseTheme)) {
|
||||
string baseTheme = theme.BaseTheme;
|
||||
theme = _extensionManager.GetExtension(baseTheme);
|
||||
if (theme != null && theme.Zones != null)
|
||||
zones = theme.Zones.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim())
|
||||
.Distinct()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return zones;
|
||||
}
|
||||
|
||||
private IEnumerable<DriverResultPlacement> ExtractPlacement(DriverResult result, BuildShapeContext context) {
|
||||
if (result is CombinedResult) {
|
||||
foreach (var subResult in ((CombinedResult) result).GetResults()) {
|
||||
foreach (var placement in ExtractPlacement(subResult, context)) {
|
||||
yield return placement;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (result is ContentShapeResult) {
|
||||
var contentShapeResult = (ContentShapeResult) result;
|
||||
|
||||
var placement = context.FindPlacement(
|
||||
contentShapeResult.GetShapeType(),
|
||||
contentShapeResult.GetDifferentiator(),
|
||||
contentShapeResult.GetLocation()
|
||||
);
|
||||
|
||||
string zone = placement.Location;
|
||||
string position = String.Empty;
|
||||
|
||||
// if no placement is found, it's hidden, e.g., no placement was found for the specific ContentType/DisplayType
|
||||
if (placement.Location != null) {
|
||||
var delimiterIndex = placement.Location.IndexOf(':');
|
||||
if (delimiterIndex >= 0) {
|
||||
zone = placement.Location.Substring(0, delimiterIndex);
|
||||
position = placement.Location.Substring(delimiterIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
var content = _contentManager.New(context.ContentItem.ContentType);
|
||||
|
||||
dynamic itemShape = CreateItemShape("Content_Edit");
|
||||
itemShape.ContentItem = content;
|
||||
|
||||
if(context is BuildDisplayContext) {
|
||||
var newContext = new BuildDisplayContext(itemShape, content, "Detail", "", context.New);
|
||||
BindPlacement(newContext, "Detail", "Content");
|
||||
contentShapeResult.Apply(newContext);
|
||||
}
|
||||
else {
|
||||
var newContext = new BuildEditorContext(itemShape, content, "", context.New);
|
||||
BindPlacement(newContext, null, "Content");
|
||||
contentShapeResult.Apply(newContext);
|
||||
}
|
||||
|
||||
|
||||
yield return new DriverResultPlacement {
|
||||
Shape = itemShape.Content,
|
||||
ShapeResult = contentShapeResult,
|
||||
PlacementSettings = new PlacementSettings {
|
||||
ShapeType = contentShapeResult.GetShapeType(),
|
||||
Zone = zone,
|
||||
Position = position,
|
||||
Differentiator = contentShapeResult.GetDifferentiator() ?? String.Empty
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private dynamic CreateItemShape(string actualShapeType) {
|
||||
var zoneHoldingBehavior = new ZoneHoldingBehavior((Func<dynamic>)(() => _shapeFactory.Create("ContentZone", Arguments.Empty())), _workContextAccessor.GetContext().Layout);
|
||||
return _shapeFactory.Create(actualShapeType, Arguments.Empty(), new[] { zoneHoldingBehavior });
|
||||
}
|
||||
|
||||
private void BindPlacement(BuildShapeContext context, string displayType, string stereotype) {
|
||||
context.FindPlacement = (partShapeType, differentiator, defaultLocation) => {
|
||||
|
||||
var theme = _siteThemeService.GetSiteTheme();
|
||||
var shapeTable = _shapeTableLocator.Lookup(theme.Id);
|
||||
|
||||
var request = _requestContext.HttpContext.Request;
|
||||
|
||||
ShapeDescriptor descriptor;
|
||||
if (shapeTable.Descriptors.TryGetValue(partShapeType, out descriptor)) {
|
||||
var placementContext = new ShapePlacementContext {
|
||||
ContentType = context.ContentItem.ContentType,
|
||||
Stereotype = stereotype,
|
||||
DisplayType = displayType,
|
||||
Differentiator = differentiator,
|
||||
Path = VirtualPathUtility.AppendTrailingSlash(_virtualPathProvider.ToAppRelative(request.Path)) // get the current app-relative path, i.e. ~/my-blog/foo
|
||||
};
|
||||
|
||||
// define which location should be used if none placement is hit
|
||||
descriptor.DefaultPlacement = defaultLocation;
|
||||
|
||||
var placement = descriptor.Placement(placementContext);
|
||||
if (placement != null) {
|
||||
placement.Source = placementContext.Source;
|
||||
return placement;
|
||||
}
|
||||
}
|
||||
|
||||
return new PlacementInfo {
|
||||
Location = defaultLocation,
|
||||
Source = String.Empty
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
|
||||
namespace Orchard.ContentTypes.Settings {
|
||||
public class PlacementSettings : IEquatable<PlacementSettings> {
|
||||
/// <summary>
|
||||
/// e.g., Parts_Title_Summary
|
||||
/// </summary>
|
||||
public string ShapeType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// e.g., Header, /Navigation
|
||||
/// </summary>
|
||||
public string Zone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// e.g, 5, after.7
|
||||
/// </summary>
|
||||
public string Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// e.g, 5, MyTextField
|
||||
/// </summary>
|
||||
public string Differentiator { get; set; }
|
||||
|
||||
public bool IsSameAs(PlacementSettings other) {
|
||||
return (ShapeType ?? String.Empty) == (other.ShapeType ?? String.Empty)
|
||||
&& (Differentiator ?? String.Empty) == (other.Differentiator ?? String.Empty);
|
||||
}
|
||||
|
||||
public bool Equals(PlacementSettings other) {
|
||||
if(other == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(other == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (ShapeType ?? String.Empty) == (other.ShapeType ?? String.Empty)
|
||||
&& (Zone ?? String.Empty) == (other.Zone ?? String.Empty)
|
||||
&& (Position ?? String.Empty) == (other.Position ?? String.Empty)
|
||||
&& (Differentiator ?? String.Empty) == (other.Differentiator ?? String.Empty);
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 897 B |
@ -109,4 +109,40 @@ fieldset.action {
|
||||
.manage-part dd dd {
|
||||
float:left;
|
||||
padding-left:.5em;
|
||||
}
|
||||
|
||||
|
||||
/* PLACEMENT EDITOR */
|
||||
|
||||
#placement li {
|
||||
margin-bottom: 10px;
|
||||
padding: 0px;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
|
||||
#placement li .shape-type {
|
||||
cursor: move;
|
||||
background-color: #eee;
|
||||
background: #EEE url(images/move.gif) no-repeat 10px 15px;
|
||||
height: 30px;
|
||||
padding: 10px 0px 0px 30px;
|
||||
}
|
||||
|
||||
#placement li .shape-editor {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#save-message {
|
||||
display: none;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.overlay{
|
||||
background:red repeat top left;
|
||||
position:fixed;
|
||||
top:0px;
|
||||
bottom:0px;
|
||||
left:0px;
|
||||
right:0px;
|
||||
z-index:100;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement.MetaData.Models;
|
||||
using Orchard.ContentTypes.Services;
|
||||
using Orchard.ContentTypes.Settings;
|
||||
|
||||
namespace Orchard.ContentTypes.ViewModels {
|
||||
public class EditPlacementViewModel {
|
||||
public ContentTypeDefinition ContentTypeDefinition { get; set; }
|
||||
public PlacementSettings[] PlacementSettings { get; set; }
|
||||
public List<DriverResultPlacement> AllPlacements { get; set; }
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement.MetaData.Models;
|
||||
using Orchard.ContentManagement.ViewModels;
|
||||
|
@ -2,12 +2,15 @@
|
||||
@{
|
||||
Style.Require("ContentTypesAdmin");
|
||||
Script.Require("jQuery");
|
||||
Layout.Title = T("Edit Content Type").ToString();
|
||||
Layout.Title = T("Edit Content Type - {0}", Model.DisplayName).ToString();
|
||||
}
|
||||
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
// todo: come up with real itemtype definitions and locations for said definitions
|
||||
<div itemscope="itemscope" itemid="@Model.Name" itemtype="http://orchardproject.net/data/ContentType">
|
||||
<div class="manage add-to-type">
|
||||
@Html.ActionLink(T("Edit Placement").Text, "EditPlacement", new { area = "Orchard.ContentTypes", id = Model.Name }, new { @class = "button" })
|
||||
</div>
|
||||
@Html.ValidationSummary()
|
||||
<fieldset>
|
||||
<label for="DisplayName">@T("Display Name")</label>
|
||||
@ -19,7 +22,7 @@
|
||||
<meta itemprop="Id" content="@Model.Name" />
|
||||
@Html.HiddenFor(m => m.Name)
|
||||
</fieldset>
|
||||
@{ Html.RenderTemplates(Model.Templates); }
|
||||
@{ Html.RenderTemplates(Model.Templates); }
|
||||
<div class="manage-type">
|
||||
<h2>@T("Fields")</h2>
|
||||
<div class="manage add-to-type">
|
||||
@ -31,6 +34,8 @@
|
||||
@Html.ActionLink(T("Add Parts").Text, "AddPartsTo", new { area = "Orchard.ContentTypes", id = Model.Name }, new { @class = "button" })
|
||||
</div>
|
||||
@Html.EditorFor(m => m.Parts, "TypeParts", "")
|
||||
<h2>@T("Placement")</h2>
|
||||
@Html.EditorFor(m => m.Settings, "Placement", "")
|
||||
</div>
|
||||
<fieldset class="action">
|
||||
<button class="primaryAction" type="submit" name="submit.Save" value="Save">@T("Save")</button>
|
||||
|
@ -0,0 +1,92 @@
|
||||
@model Orchard.ContentTypes.ViewModels.EditPlacementViewModel
|
||||
@{
|
||||
Style.Require("ContentTypesAdmin");
|
||||
Script.Require("jQueryUI_Sortable");
|
||||
Layout.Title = T("Edit Placement - {0}", Model.ContentTypeDefinition.DisplayName).ToString();
|
||||
|
||||
var hiddenShapes = Model.AllPlacements.Where(x => String.IsNullOrEmpty(x.PlacementSettings.Zone) && (String.IsNullOrWhiteSpace(x.PlacementSettings.Position) || x.PlacementSettings.Position == "-"));
|
||||
}
|
||||
|
||||
<div id="save-message" class="message message-Warning">@T("You need to hit \"Save\" in order to save your changes.")</div>
|
||||
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
@Html.ValidationSummary()
|
||||
|
||||
|
||||
<ul id="placement">
|
||||
@for (int i = 0; i < Model.AllPlacements.Count; i++ ) {
|
||||
|
||||
var placement = Model.AllPlacements[i].PlacementSettings;
|
||||
|
||||
<li data-shape-type="@placement.ShapeType" data-shape-differentiator="@placement.Differentiator" data-shape-zone="Content" data-shape-position="@placement.Position">
|
||||
<div class="shape-type"><h3>@placement.ShapeType @placement.Differentiator</h3></div>
|
||||
<div class="shape-editor">@Display(Model.AllPlacements[i].Shape)</div>
|
||||
@* @shape.Position @(Model.PlacementSettings.Any(x => x.Equals(shape)))*@
|
||||
@Html.HiddenFor(m => m.AllPlacements[i].PlacementSettings.ShapeType, new { @class = "type" })
|
||||
@Html.HiddenFor(m => m.AllPlacements[i].PlacementSettings.Differentiator, new { @class = "differentiator" })
|
||||
@Html.HiddenFor(m => m.AllPlacements[i].PlacementSettings.Zone, new { @class = "zone" })
|
||||
@Html.HiddenFor(m => m.AllPlacements[i].PlacementSettings.Position, new { @class = "position" })
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
|
||||
<fieldset class="action">
|
||||
<button class="primaryAction" type="submit" name="submit.Save" value="Save">@T("Save")</button>
|
||||
<button class="primaryAction" type="submit" name="submit.Restore" value="Restore" itemprop="RemoveUrl">@T("Restore")</button>
|
||||
</fieldset>
|
||||
|
||||
}
|
||||
|
||||
@using (Script.Foot()) {
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
(function ($) {
|
||||
|
||||
var startPos;
|
||||
|
||||
$('#placement').sortable({
|
||||
start: function (event, ui) {
|
||||
var self = $(ui.item);
|
||||
startPos = self.prevAll().size();
|
||||
},
|
||||
stop: function (event, ui) {
|
||||
var self = $(ui.item);
|
||||
var stopPos = self.prevAll().size();
|
||||
|
||||
var begin = Math.min(startPos, stopPos);
|
||||
var end = Math.max(startPos, stopPos);
|
||||
|
||||
// get siblings ( .siblings() doesn't include itself)
|
||||
var siblings = self.parent().children();
|
||||
for (var i = begin; i < end; i++) {
|
||||
// get both positions
|
||||
var a = $(siblings[i]).find('.position').val();
|
||||
var b = $(siblings[i + 1]).find('.position').val();
|
||||
|
||||
// swap values
|
||||
var temp = a;
|
||||
$(siblings[i]).find('.position').val(b);
|
||||
$(siblings[i + 1]).find('.position').val(temp);
|
||||
}
|
||||
|
||||
// ensure all successive values are different
|
||||
for (var i = 0; i < siblings.size() - 1; i++) {
|
||||
// get both positions
|
||||
a = $(siblings[i]).find('.position').val();
|
||||
b = $(siblings[i + 1]).find('.position').val();
|
||||
|
||||
if (a == b) {
|
||||
$(siblings[i + 1]).find('.position').val(a + '.5');
|
||||
}
|
||||
}
|
||||
|
||||
$('#save-message').show();
|
||||
}
|
||||
});
|
||||
|
||||
$("#placement").disableSelection();
|
||||
|
||||
})(jQuery);
|
||||
//]]>
|
||||
</script>
|
||||
}
|
@ -47,5 +47,9 @@ namespace Orchard.ContentManagement.Drivers {
|
||||
result.Apply(context);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<DriverResult> GetResults() {
|
||||
return _results;
|
||||
}
|
||||
}
|
||||
}
|
@ -89,5 +89,21 @@ namespace Orchard.ContentManagement.Drivers {
|
||||
_groupId=groupId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public string GetDifferentiator() {
|
||||
return _differentiator;
|
||||
}
|
||||
|
||||
public string GetGroup() {
|
||||
return _groupId;
|
||||
}
|
||||
|
||||
public string GetLocation() {
|
||||
return _defaultLocation;
|
||||
}
|
||||
|
||||
public string GetShapeType() {
|
||||
return _shapeType;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ namespace Orchard.ContentManagement.Drivers {
|
||||
public class DriverResult {
|
||||
public virtual void Apply(BuildDisplayContext context) { }
|
||||
public virtual void Apply(BuildEditorContext context) { }
|
||||
|
||||
|
||||
public ContentPart ContentPart { get; set; }
|
||||
public ContentField ContentField { get; set; }
|
||||
}
|
||||
|
@ -16,15 +16,19 @@ namespace Orchard.DisplayManagement.Descriptors {
|
||||
private readonly IExtensionManager _extensionManager;
|
||||
private readonly ICacheManager _cacheManager;
|
||||
private readonly IParallelCacheContext _parallelCacheContext;
|
||||
private readonly IEnumerable<IShapeTableEventHandler> _shapeTableEventHandlers;
|
||||
|
||||
public DefaultShapeTableManager(
|
||||
IEnumerable<Meta<IShapeTableProvider>> bindingStrategies,
|
||||
IExtensionManager extensionManager,
|
||||
ICacheManager cacheManager,
|
||||
IParallelCacheContext parallelCacheContext) {
|
||||
IParallelCacheContext parallelCacheContext,
|
||||
IEnumerable<IShapeTableEventHandler> shapeTableEventHandlers
|
||||
) {
|
||||
_extensionManager = extensionManager;
|
||||
_cacheManager = cacheManager;
|
||||
_parallelCacheContext = parallelCacheContext;
|
||||
_shapeTableEventHandlers = shapeTableEventHandlers;
|
||||
_bindingStrategies = bindingStrategies;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
@ -72,6 +76,8 @@ namespace Orchard.DisplayManagement.Descriptors {
|
||||
Bindings = descriptors.SelectMany(sd => sd.Bindings).ToDictionary(kv => kv.Key, kv => kv.Value, StringComparer.OrdinalIgnoreCase),
|
||||
};
|
||||
|
||||
_shapeTableEventHandlers.Invoke(ctx => ctx.ShapeTableCreated(result), Logger);
|
||||
|
||||
Logger.Information("Done building shape table");
|
||||
return result;
|
||||
});
|
||||
|
@ -1,4 +1,6 @@
|
||||
namespace Orchard.DisplayManagement.Descriptors {
|
||||
using Orchard.Events;
|
||||
|
||||
namespace Orchard.DisplayManagement.Descriptors {
|
||||
|
||||
public interface IShapeTableManager : ISingletonDependency {
|
||||
ShapeTable GetShapeTable(string themeName);
|
||||
@ -7,4 +9,9 @@
|
||||
public interface IShapeTableProvider : IDependency {
|
||||
void Discover(ShapeTableBuilder builder);
|
||||
}
|
||||
|
||||
public interface IShapeTableEventHandler : IEventHandler {
|
||||
void ShapeTableCreated(ShapeTable shapeTable);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ namespace Orchard.DisplayManagement.Descriptors {
|
||||
descriptor.Placement = ctx => predicate(ctx) ? location : next(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public ShapeAlteration Build() {
|
||||
return new ShapeAlteration(_shapeType, _feature, _configurations.ToArray());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user