mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Layouts: Refactoring the way data is transferred between the layout editor and modal element editor dialog.
This change makes implementing element editors more intuitive, since they no longer rely on the initial postback from layout editor to the element editor dialog. The ObjectStore is a simple database table that serves as a robust data transfer mechanism to share element state without the need for the host (content) to be persisted. We could have used SessionState, but that would have added configuration requirements when hosting in a web farm.
This commit is contained in:
@@ -216,7 +216,7 @@ namespace Orchard.Layouts.Controllers {
|
||||
var context = new ElementEditorContext {
|
||||
Element = element,
|
||||
Updater = this,
|
||||
ValueProvider = new DictionaryValueProvider<string>(elementState, _cultureAccessor.CurrentCulture),
|
||||
ValueProvider = elementState.ToValueProvider(_cultureAccessor.CurrentCulture),
|
||||
ShapeFactory = _shapeFactory
|
||||
};
|
||||
ValueProvider = context.ValueProvider;
|
||||
|
@@ -22,39 +22,39 @@ namespace Orchard.Layouts.Controllers {
|
||||
private readonly ILayoutSerializer _layoutSerializer;
|
||||
private readonly IElementManager _elementManager;
|
||||
private readonly IShapeFactory _shapeFactory;
|
||||
private readonly IWorkContextAccessor _wca;
|
||||
private readonly ITransactionManager _transactionManager;
|
||||
private readonly ICultureAccessor _cultureAccessor;
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IObjectStore _objectStore;
|
||||
|
||||
public ElementController(
|
||||
IElementDisplay elementDisplay,
|
||||
ILayoutSerializer layoutSerializer,
|
||||
IElementManager elementManager,
|
||||
IShapeFactory shapeFactory,
|
||||
IWorkContextAccessor wca,
|
||||
ITransactionManager transactionManager,
|
||||
ICultureAccessor cultureAccessor,
|
||||
IContentManager contentManager) {
|
||||
IContentManager contentManager, IObjectStore objectStore) {
|
||||
|
||||
_elementDisplay = elementDisplay;
|
||||
_layoutSerializer = layoutSerializer;
|
||||
_elementManager = elementManager;
|
||||
_shapeFactory = shapeFactory;
|
||||
_wca = wca;
|
||||
_transactionManager = transactionManager;
|
||||
_cultureAccessor = cultureAccessor;
|
||||
_contentManager = contentManager;
|
||||
_objectStore = objectStore;
|
||||
}
|
||||
|
||||
[Admin]
|
||||
public ViewResult Browse(int? layoutId = null, string contentType = null) {
|
||||
public ViewResult Browse(string session, int? layoutId = null, string contentType = null) {
|
||||
var context = CreateDescribeContext(layoutId, contentType);
|
||||
var categories = _elementManager.GetCategories(context).ToArray();
|
||||
var viewModel = new BrowseElementsViewModel {
|
||||
Categories = categories,
|
||||
ContentId = layoutId,
|
||||
ContentType = contentType
|
||||
ContentType = contentType,
|
||||
Session = session
|
||||
};
|
||||
return View(viewModel);
|
||||
}
|
||||
@@ -70,13 +70,23 @@ namespace Orchard.Layouts.Controllers {
|
||||
}
|
||||
|
||||
[Admin]
|
||||
public ViewResult Create(string id, int? contentId = null, string contentType = null) {
|
||||
public ViewResult Create(string id, string session, int? contentId = null, string contentType = null) {
|
||||
var sessionState = new ElementSessionState {
|
||||
TypeName = id,
|
||||
State = null,
|
||||
ContentId = contentId,
|
||||
ContentType = contentType
|
||||
};
|
||||
|
||||
_objectStore.Set(session, sessionState);
|
||||
|
||||
var describeContext = CreateDescribeContext(contentId, contentType);
|
||||
var descriptor = _elementManager.GetElementDescriptorByTypeName(describeContext, id);
|
||||
var element = _elementManager.ActivateElement(descriptor);
|
||||
var context = CreateEditorContext(describeContext.Content, element);
|
||||
var context = CreateEditorContext(session, describeContext.Content, element);
|
||||
var editorResult = _elementManager.BuildEditor(context);
|
||||
var viewModel = new EditElementViewModel {
|
||||
SessionKey = session,
|
||||
Layout = describeContext.Content.As<ILayoutAspect>(),
|
||||
EditorResult = editorResult,
|
||||
TypeName = id,
|
||||
@@ -92,14 +102,18 @@ namespace Orchard.Layouts.Controllers {
|
||||
[Admin]
|
||||
[HttpPost]
|
||||
[ValidateInput(false)]
|
||||
public ViewResult Create(ElementStateViewModel model, int? contentId = null, string contentType = null) {
|
||||
public ViewResult Create(ElementStateViewModel model, string session) {
|
||||
var sessionState = _objectStore.Get<ElementSessionState>(session);
|
||||
var contentId = sessionState.ContentId;
|
||||
var contentType = sessionState.ContentType;
|
||||
var describeContext = CreateDescribeContext(contentId, contentType);
|
||||
var descriptor = _elementManager.GetElementDescriptorByTypeName(describeContext, model.TypeName);
|
||||
var state = ElementStateHelper.Deserialize(model.ElementState).Combine(Request.Form.ToDictionary());
|
||||
var element = _elementManager.ActivateElement(descriptor, new ActivateElementArgs { State = state });
|
||||
var context = CreateEditorContext(describeContext.Content, element, elementState: state);
|
||||
var context = CreateEditorContext(session, describeContext.Content, element, elementState: state, updater: this);
|
||||
var editorResult = _elementManager.UpdateEditor(context);
|
||||
var viewModel = new EditElementViewModel {
|
||||
SessionKey = session,
|
||||
Layout = describeContext.Content.As<ILayoutAspect>(),
|
||||
EditorResult = editorResult,
|
||||
TypeName = model.TypeName,
|
||||
@@ -120,12 +134,30 @@ namespace Orchard.Layouts.Controllers {
|
||||
[Admin]
|
||||
[HttpPost]
|
||||
[ValidateInput(false)]
|
||||
public ViewResult Edit(string typeName, string elementState, int? contentId = null, string contentType = null) {
|
||||
public RedirectToRouteResult Edit(string session, string typeName, string elementState, int? contentId = null, string contentType = null) {
|
||||
var state = new ElementSessionState {
|
||||
TypeName = typeName,
|
||||
State = elementState,
|
||||
ContentId = contentId,
|
||||
ContentType = contentType
|
||||
};
|
||||
|
||||
_objectStore.Set(session, state);
|
||||
return RedirectToAction("Edit", new {session = session});
|
||||
}
|
||||
|
||||
[Admin]
|
||||
public ViewResult Edit(string session) {
|
||||
var sessionState = _objectStore.Get<ElementSessionState>(session);
|
||||
var contentId = sessionState.ContentId;
|
||||
var contentType = sessionState.ContentType;
|
||||
var typeName = sessionState.TypeName;
|
||||
var elementState = sessionState.State;
|
||||
var describeContext = CreateDescribeContext(contentId, contentType);
|
||||
var descriptor = _elementManager.GetElementDescriptorByTypeName(describeContext, typeName);
|
||||
var state = ElementStateHelper.Deserialize(elementState);
|
||||
var element = _elementManager.ActivateElement(descriptor, new ActivateElementArgs { State = state });
|
||||
var context = CreateEditorContext(describeContext.Content, element, elementState: state);
|
||||
var context = CreateEditorContext(session, describeContext.Content, element, elementState: state);
|
||||
var editorResult = _elementManager.BuildEditor(context);
|
||||
|
||||
var viewModel = new EditElementViewModel {
|
||||
@@ -134,7 +166,8 @@ namespace Orchard.Layouts.Controllers {
|
||||
TypeName = typeName,
|
||||
DisplayText = descriptor.DisplayText,
|
||||
ElementState = element.State.Serialize(),
|
||||
Tabs = editorResult.CollectTabs().ToArray()
|
||||
Tabs = editorResult.CollectTabs().ToArray(),
|
||||
SessionKey = session
|
||||
};
|
||||
|
||||
return View(viewModel);
|
||||
@@ -143,12 +176,15 @@ namespace Orchard.Layouts.Controllers {
|
||||
[Admin]
|
||||
[HttpPost]
|
||||
[ValidateInput(false)]
|
||||
public ViewResult Update(ElementStateViewModel model, int? contentId = null, string contentType = null) {
|
||||
public ViewResult Update(ElementStateViewModel model, string session) {
|
||||
var sessionState = _objectStore.Get<ElementSessionState>(session);
|
||||
var contentId = sessionState.ContentId;
|
||||
var contentType = sessionState.ContentType;
|
||||
var describeContext = CreateDescribeContext(contentId, contentType);
|
||||
var descriptor = _elementManager.GetElementDescriptorByTypeName(describeContext, model.TypeName);
|
||||
var state = ElementStateHelper.Deserialize(model.ElementState).Combine(Request.Form.ToDictionary(), removeNonExistingItems: true);
|
||||
var state = ElementStateHelper.Deserialize(model.ElementState).Combine(Request.Form.ToDictionary());
|
||||
var element = _elementManager.ActivateElement(descriptor, new ActivateElementArgs { State = state });
|
||||
var context = CreateEditorContext(describeContext.Content, element, state);
|
||||
var context = CreateEditorContext(session, describeContext.Content, element, state, updater: this);
|
||||
var editorResult = _elementManager.UpdateEditor(context);
|
||||
var viewModel = new EditElementViewModel {
|
||||
Layout = describeContext.Content.As<ILayoutAspect>(),
|
||||
@@ -156,7 +192,8 @@ namespace Orchard.Layouts.Controllers {
|
||||
TypeName = model.TypeName,
|
||||
DisplayText = descriptor.DisplayText,
|
||||
ElementState = element.State.Serialize(),
|
||||
Tabs = editorResult.CollectTabs().ToArray()
|
||||
Tabs = editorResult.CollectTabs().ToArray(),
|
||||
SessionKey = session
|
||||
};
|
||||
|
||||
if (!ModelState.IsValid) {
|
||||
@@ -169,21 +206,24 @@ namespace Orchard.Layouts.Controllers {
|
||||
}
|
||||
|
||||
protected override void OnActionExecuting(ActionExecutingContext filterContext) {
|
||||
var workContext = _wca.GetContext();
|
||||
var workContext = filterContext.GetWorkContext();
|
||||
workContext.Layout.Metadata.Alternates.Add("Layout__Dialog");
|
||||
}
|
||||
|
||||
private ElementEditorContext CreateEditorContext(
|
||||
IContent content,
|
||||
IElement element,
|
||||
StateDictionary elementState = null) {
|
||||
string session,
|
||||
IContent content,
|
||||
IElement element,
|
||||
StateDictionary elementState = null,
|
||||
IUpdateModel updater = null) {
|
||||
|
||||
elementState = elementState ?? new StateDictionary();
|
||||
var context = new ElementEditorContext {
|
||||
Session = session,
|
||||
Content = content,
|
||||
Element = element,
|
||||
Updater = this,
|
||||
ValueProvider = new DictionaryValueProvider<string>(elementState, _cultureAccessor.CurrentCulture),
|
||||
Updater = updater,
|
||||
ValueProvider = elementState.ToValueProvider(_cultureAccessor.CurrentCulture),
|
||||
ShapeFactory = _shapeFactory
|
||||
};
|
||||
ValueProvider = context.ValueProvider;
|
||||
|
@@ -23,9 +23,9 @@ namespace Orchard.Layouts.Controllers {
|
||||
|
||||
public LayoutController(
|
||||
IContentManager contentManager,
|
||||
IWorkContextAccessor wca,
|
||||
IWorkContextAccessor wca,
|
||||
IShapeDisplay shapeDisplay,
|
||||
ILayoutManager layoutManager,
|
||||
ILayoutManager layoutManager,
|
||||
ILayoutSerializer serializer) {
|
||||
|
||||
_contentManager = contentManager;
|
||||
@@ -36,7 +36,7 @@ namespace Orchard.Layouts.Controllers {
|
||||
}
|
||||
|
||||
[Admin]
|
||||
public ViewResult Edit(string contentType = null, int? id = null, string state = null) {
|
||||
public ViewResult Edit(string session, string contentType = null, int? id = null, string state = null) {
|
||||
var describeContext = CreateDescribeElementsContext(id, contentType);
|
||||
var layoutPart = describeContext.Content.As<LayoutPart>();
|
||||
|
||||
@@ -52,7 +52,8 @@ namespace Orchard.Layouts.Controllers {
|
||||
SelectedTemplateId = layoutPart.TemplateId,
|
||||
State = state,
|
||||
LayoutRoot = _layoutManager.RenderLayout(state, displayType: "Design", content: layoutPart),
|
||||
Content = layoutPart
|
||||
Content = layoutPart,
|
||||
SessionKey = session
|
||||
};
|
||||
|
||||
var workContext = _wca.GetContext();
|
||||
|
@@ -4,6 +4,7 @@ using Orchard.Data;
|
||||
using Orchard.Layouts.Elements;
|
||||
using Orchard.Layouts.Framework.Display;
|
||||
using Orchard.Layouts.Framework.Drivers;
|
||||
using Orchard.Layouts.Helpers;
|
||||
using Orchard.Layouts.Services;
|
||||
|
||||
namespace Orchard.Layouts.Drivers {
|
||||
@@ -28,7 +29,7 @@ namespace Orchard.Layouts.Drivers {
|
||||
var controller = (Controller)context.Updater;
|
||||
var oldValueProvider = controller.ValueProvider;
|
||||
|
||||
controller.ValueProvider = new DictionaryValueProvider<string>(context.Element.State, _cultureAccessor.CurrentCulture);
|
||||
controller.ValueProvider = context.Element.State.ToValueProvider(_cultureAccessor.CurrentCulture);
|
||||
_contentPartDisplay.UpdateEditor(contentPart, context.Updater);
|
||||
_transactionManager.Cancel();
|
||||
controller.ValueProvider = oldValueProvider;
|
||||
|
@@ -60,7 +60,8 @@ namespace Orchard.Layouts.Drivers {
|
||||
var viewModel = new LayoutPartViewModel {
|
||||
State = part.LayoutState,
|
||||
TemplateId = part.TemplateId,
|
||||
Content = part
|
||||
Content = part,
|
||||
SessionKey = part.SessionKey
|
||||
};
|
||||
|
||||
if (updater != null) {
|
||||
@@ -80,6 +81,7 @@ namespace Orchard.Layouts.Drivers {
|
||||
|
||||
part.LayoutState = _serializer.Serialize(elementInstances);
|
||||
part.TemplateId = viewModel.TemplateId;
|
||||
part.SessionKey = viewModel.SessionKey;
|
||||
}
|
||||
|
||||
return shapeHelper.EditorTemplate(TemplateName: "Parts.Layout", Model: viewModel, Prefix: Prefix);
|
||||
|
@@ -15,5 +15,6 @@ namespace Orchard.Layouts.Framework.Drivers {
|
||||
public IContent Content { get; set; }
|
||||
public string Prefix { get; set; }
|
||||
public EditorResult EditorResult { get; set; }
|
||||
public string Session { get; set; }
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.Layouts.Helpers;
|
||||
|
||||
namespace Orchard.Layouts.Framework.Elements {
|
||||
public class StateDictionary : Dictionary<string, string> {
|
||||
@@ -12,7 +13,7 @@ namespace Orchard.Layouts.Framework.Elements {
|
||||
var context = new ModelBindingContext {
|
||||
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(T)),
|
||||
ModelName = key,
|
||||
ValueProvider = new DictionaryValueProvider<string>(this, null)
|
||||
ValueProvider = this.ToValueProvider(null)
|
||||
};
|
||||
return (T)binder.BindModel(controllerContext, context);
|
||||
|
||||
|
@@ -1,13 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.Layouts.Framework.Elements;
|
||||
|
||||
namespace Orchard.Layouts.Helpers {
|
||||
public static class ElementStateHelper {
|
||||
private static readonly string[] _elementStateBlackList = {"ElementState", "__RequestVerificationToken"};
|
||||
private static readonly string[] _elementStateBlackList = { "ElementState", "__RequestVerificationToken" };
|
||||
|
||||
public static string Get(this StateDictionary state, string key, string defaultValue = null) {
|
||||
return state != null ? state.ContainsKey(key) ? state[key] : defaultValue : defaultValue;
|
||||
@@ -19,7 +21,7 @@ namespace Orchard.Layouts.Helpers {
|
||||
|
||||
public static StateDictionary Combine(this StateDictionary target, StateDictionary input, bool removeNonExistingItems = false) {
|
||||
var combined = new StateDictionary(target);
|
||||
|
||||
|
||||
foreach (var item in input) {
|
||||
combined[item.Key] = item.Value;
|
||||
}
|
||||
@@ -62,12 +64,12 @@ namespace Orchard.Layouts.Helpers {
|
||||
|
||||
foreach (var key in _elementStateBlackList) {
|
||||
copy.Remove(key);
|
||||
|
||||
}
|
||||
|
||||
var dictionary = new StateDictionary();
|
||||
|
||||
foreach (string key in copy) {
|
||||
dictionary[key] = copy[key];
|
||||
dictionary[key] = String.Join(",", copy.GetValues(key).Select(HttpUtility.UrlEncode));
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
@@ -80,13 +82,32 @@ namespace Orchard.Layouts.Helpers {
|
||||
copy.Remove(key);
|
||||
|
||||
}
|
||||
|
||||
var dictionary = new Dictionary<string, object>();
|
||||
|
||||
foreach (string key in copy) {
|
||||
dictionary[key] = copy[key];
|
||||
dictionary[key] = copy.GetValues(key);
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
public static NameValueCollection ToNameValueCollection(this StateDictionary dictionary) {
|
||||
var collection = new NameValueCollection();
|
||||
|
||||
foreach (var entry in dictionary) {
|
||||
var values = entry.Value != null ? entry.Value.Split(new []{','}, StringSplitOptions.RemoveEmptyEntries) : new string[0];
|
||||
|
||||
foreach (var value in values) {
|
||||
collection.Add(entry.Key, HttpUtility.UrlDecode(value));
|
||||
}
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
public static IValueProvider ToValueProvider(this StateDictionary dictionary, CultureInfo culture) {
|
||||
return new NameValueCollectionValueProvider(dictionary.ToNameValueCollection(), culture);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using System;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.Core.Contents.Extensions;
|
||||
using Orchard.Data.Migration;
|
||||
using Orchard.Layouts.Helpers;
|
||||
@@ -6,6 +7,14 @@ using Orchard.Layouts.Helpers;
|
||||
namespace Orchard.Layouts {
|
||||
public class Migrations : DataMigrationImpl {
|
||||
public int Create() {
|
||||
SchemaBuilder.CreateTable("ObjectStoreEntry", table => table
|
||||
.Column<int>("Id", c => c.PrimaryKey().Identity())
|
||||
.Column<string>("EntryKey", c => c.WithLength(64))
|
||||
.Column<string>("Data", c => c.Unlimited())
|
||||
.Column<int>("UserId")
|
||||
.Column<DateTime>("CreatedUtc")
|
||||
.Column<DateTime>("LastModifiedUtc"));
|
||||
|
||||
SchemaBuilder.CreateTable("ElementBlueprint", table => table
|
||||
.Column<int>("Id", c => c.PrimaryKey().Identity())
|
||||
.Column<string>("BaseElementTypeName", c => c.WithLength(256))
|
||||
@@ -60,7 +69,19 @@ namespace Orchard.Layouts {
|
||||
.WithPart("LayoutPart", p => p
|
||||
.WithSetting("LayoutTypePartSettings.IsTemplate", "False")));
|
||||
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
public int UpdateFrom1() {
|
||||
SchemaBuilder.CreateTable("ObjectStoreEntry", table => table
|
||||
.Column<int>("Id", c => c.PrimaryKey().Identity())
|
||||
.Column<string>("EntryKey", c => c.WithLength(64))
|
||||
.Column<string>("Data", c => c.Unlimited())
|
||||
.Column<int>("UserId")
|
||||
.Column<DateTime>("CreatedUtc")
|
||||
.Column<DateTime>("LastModifiedUtc"));
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
private void DefineElementWidget(string widgetTypeName, string widgetDisplayedAs, string elementTypeName) {
|
||||
|
@@ -0,0 +1,8 @@
|
||||
namespace Orchard.Layouts.Models {
|
||||
public class ElementSessionState {
|
||||
public string TypeName { get; set; }
|
||||
public string State { get; set; }
|
||||
public int? ContentId { get; set; }
|
||||
public string ContentType { get; set; }
|
||||
}
|
||||
}
|
@@ -1,8 +1,23 @@
|
||||
using Orchard.ContentManagement;
|
||||
using System;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Layouts.Settings;
|
||||
|
||||
namespace Orchard.Layouts.Models {
|
||||
public class LayoutPart : ContentPart<LayoutPartRecord>, ILayoutAspect {
|
||||
|
||||
public string SessionKey {
|
||||
get {
|
||||
var key = this.Retrieve(x => x.SessionKey);
|
||||
|
||||
if (String.IsNullOrEmpty(key)) {
|
||||
SessionKey = key = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
set { this.Store(x => x.SessionKey, value); }
|
||||
}
|
||||
|
||||
public string LayoutState {
|
||||
get { return this.Retrieve(x => x.LayoutState, versioned: true); }
|
||||
set { this.Store(x => x.LayoutState, value, versioned: true); }
|
||||
|
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using Orchard.Data.Conventions;
|
||||
|
||||
namespace Orchard.Layouts.Models {
|
||||
public class ObjectStoreEntry {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual string EntryKey { get; set; }
|
||||
[StringLengthMax]
|
||||
public virtual string Data { get; set; }
|
||||
public virtual int UserId { get; set; }
|
||||
public virtual DateTime CreatedUtc { get; set; }
|
||||
public virtual DateTime LastModifiedUtc { get; set; }
|
||||
}
|
||||
}
|
@@ -58,6 +58,10 @@
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.DynamicData" />
|
||||
<Reference Include="System.Web.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\aspnetwebapi\System.Web.Http.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Mvc, Version=5.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
|
||||
@@ -237,14 +241,18 @@
|
||||
<Compile Include="Helpers\DictionaryExtensions.cs" />
|
||||
<Compile Include="Helpers\EditorResultExtensions.cs" />
|
||||
<Compile Include="Helpers\PrefixHelper.cs" />
|
||||
<Compile Include="Models\ObjectStoreEntry.cs" />
|
||||
<Compile Include="Providers\BlueprintElementHarvester.cs" />
|
||||
<Compile Include="Services\CurrentControllerAccessor.cs" />
|
||||
<Compile Include="Services\ElementCreatedContext.cs" />
|
||||
<Compile Include="Services\ElementCreatingContext.cs" />
|
||||
<Compile Include="Services\ElementEventContext.cs" />
|
||||
<Compile Include="Services\ElementEventHandlerBase.cs" />
|
||||
<Compile Include="Models\ElementSessionState.cs" />
|
||||
<Compile Include="Services\ICurrentControllerAccessor.cs" />
|
||||
<Compile Include="Services\IElementEventHandler.cs" />
|
||||
<Compile Include="Services\IObjectStore.cs" />
|
||||
<Compile Include="Services\ObjectStore.cs" />
|
||||
<Compile Include="Signals.cs" />
|
||||
<Compile Include="Tokens\ElementTokens.cs" />
|
||||
<Compile Include="ViewModels\ElementBlueprintPropertiesViewModel.cs" />
|
||||
|
@@ -77,7 +77,7 @@ namespace Orchard.Layouts.Providers {
|
||||
var controller = (Controller)context.Updater;
|
||||
var oldValueProvider = controller.ValueProvider;
|
||||
|
||||
controller.ValueProvider = new DictionaryValueProvider<string>(context.Element.State, _cultureAccessor.Value.CurrentCulture);
|
||||
controller.ValueProvider = context.Element.State.ToValueProvider(_cultureAccessor.Value.CurrentCulture);
|
||||
_contentFieldDisplay.Value.UpdateEditor(contentItem, contentField, context.Updater);
|
||||
_transactionManager.Value.Cancel();
|
||||
controller.ValueProvider = oldValueProvider;
|
||||
|
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using Orchard.Layouts.Models;
|
||||
|
||||
namespace Orchard.Layouts.Services {
|
||||
/// <summary>
|
||||
/// A simple object store to store temporary data into. It is used to transfer layout element state between the canvas and the element editor.
|
||||
/// </summary>
|
||||
public interface IObjectStore : IDependency {
|
||||
ObjectStoreEntry GetEntry(string key);
|
||||
ObjectStoreEntry GetOrCreateEntry(string key);
|
||||
ObjectStoreEntry GetOrCreateEntry();
|
||||
void Set<T>(string key, T value);
|
||||
string Set<T>(T value);
|
||||
T Get<T>(string key, Func<T> defaultValue = null);
|
||||
string GenerateKey();
|
||||
bool Remove(string key);
|
||||
}
|
||||
}
|
102
src/Orchard.Web/Modules/Orchard.Layouts/Services/ObjectStore.cs
Normal file
102
src/Orchard.Web/Modules/Orchard.Layouts/Services/ObjectStore.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Orchard.Data;
|
||||
using Orchard.Layouts.Models;
|
||||
using Orchard.Services;
|
||||
using Orchard.Validation;
|
||||
|
||||
namespace Orchard.Layouts.Services {
|
||||
public class ObjectStore : IObjectStore {
|
||||
private readonly IRepository<ObjectStoreEntry> _repository;
|
||||
private readonly IClock _clock;
|
||||
private readonly IWorkContextAccessor _wca;
|
||||
private readonly Lazy<int> _currentUserIdField;
|
||||
|
||||
public ObjectStore(IRepository<ObjectStoreEntry> repository, IClock clock, IWorkContextAccessor wca) {
|
||||
_repository = repository;
|
||||
_clock = clock;
|
||||
_wca = wca;
|
||||
|
||||
_currentUserIdField = new Lazy<int>(() => {
|
||||
var currentUser = _wca.GetContext().CurrentUser;
|
||||
return currentUser != null ? currentUser.Id : 0;
|
||||
});
|
||||
}
|
||||
|
||||
private int CurrentUserId {
|
||||
get { return _currentUserIdField.Value; }
|
||||
}
|
||||
|
||||
public ObjectStoreEntry GetEntry(string key) {
|
||||
Argument.ThrowIfNull(key, "key");
|
||||
return _repository.Get(x => x.EntryKey == key && x.UserId == CurrentUserId);
|
||||
}
|
||||
|
||||
public ObjectStoreEntry GetOrCreateEntry(string key) {
|
||||
Argument.ThrowIfNull(key, "key");
|
||||
|
||||
var entry = GetEntry(key);
|
||||
|
||||
if (entry == null) {
|
||||
entry = new ObjectStoreEntry {
|
||||
EntryKey = key,
|
||||
UserId = CurrentUserId,
|
||||
CreatedUtc = _clock.UtcNow,
|
||||
LastModifiedUtc = _clock.UtcNow
|
||||
};
|
||||
|
||||
_repository.Create(entry);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
public ObjectStoreEntry GetOrCreateEntry() {
|
||||
var key = GenerateKey();
|
||||
return GetOrCreateEntry(key);
|
||||
}
|
||||
|
||||
public void Set<T>(string key, T value) {
|
||||
Argument.ThrowIfNull(key, "key");
|
||||
|
||||
var entry = GetOrCreateEntry(key);
|
||||
var json = JsonConvert.SerializeObject(value);
|
||||
|
||||
entry.Data = json;
|
||||
entry.LastModifiedUtc = _clock.UtcNow;
|
||||
}
|
||||
|
||||
public string Set<T>(T value) {
|
||||
var key = GenerateKey();
|
||||
Set(key, value);
|
||||
return key;
|
||||
}
|
||||
|
||||
public T Get<T>(string key, Func<T> defaultValue = null) {
|
||||
Argument.ThrowIfNull(key, "key");
|
||||
|
||||
var entry = GetOrCreateEntry(key);
|
||||
var json = entry.Data;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(json))
|
||||
return defaultValue != null ? defaultValue() : default(T);
|
||||
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
}
|
||||
|
||||
public string GenerateKey() {
|
||||
return Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
public bool Remove(string key) {
|
||||
var entry = GetEntry(key);
|
||||
|
||||
if (entry == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_repository.Delete(entry);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,5 +6,6 @@ namespace Orchard.Layouts.ViewModels {
|
||||
public IList<CategoryDescriptor> Categories { get; set; }
|
||||
public int? ContentId { get; set; }
|
||||
public string ContentType { get; set; }
|
||||
public string Session { get; set; }
|
||||
}
|
||||
}
|
@@ -16,5 +16,6 @@ namespace Orchard.Layouts.ViewModels {
|
||||
public ILayoutAspect Layout { get; set; }
|
||||
public IList<string> Tabs { get; set; }
|
||||
public bool Submitted { get; set; }
|
||||
public string SessionKey { get; set; }
|
||||
}
|
||||
}
|
@@ -9,5 +9,6 @@ namespace Orchard.Layouts.ViewModels {
|
||||
public IList<LayoutPart> Templates { get; set; }
|
||||
public int? SelectedTemplateId { get; set; }
|
||||
public IContent Content { get; set; }
|
||||
public string SessionKey { get; set; }
|
||||
}
|
||||
}
|
@@ -6,5 +6,6 @@ namespace Orchard.Layouts.ViewModels {
|
||||
public string State { get; set; }
|
||||
public string Trash { get; set; }
|
||||
public int? TemplateId { get; set; }
|
||||
public string SessionKey { get; set; }
|
||||
}
|
||||
}
|
@@ -13,7 +13,7 @@
|
||||
@{
|
||||
var contentId = Model.Content != null ? Model.Content.Id : default(int?);
|
||||
var contentType = Model.Content != null ? Model.Content.ContentItem.ContentType : default(string);
|
||||
var url = Url.Action("Edit", "Layout", new { id = contentId, contentType = contentType, area = "Orchard.Layouts" });
|
||||
var url = Url.Action("Edit", "Layout", new { id = contentId, contentType = contentType, session = Model.SessionKey, area = "Orchard.Layouts" });
|
||||
var frameUrl = ViewData.ModelState.IsValid ? url : default(string);
|
||||
}
|
||||
<div class="layout-designer"
|
||||
@@ -22,9 +22,11 @@
|
||||
data-template-picker-name="@Html.FieldNameFor(m => m.TemplateId)"
|
||||
data-modelstate-valid="@ViewData.ModelState.IsValid.ToString().ToLower()"
|
||||
data-frame-url="@url"
|
||||
data-anti-forgery-token="@Html.AntiForgeryTokenValueOrchard()">
|
||||
data-anti-forgery-token="@Html.AntiForgeryTokenValueOrchard()"
|
||||
data-session-key="@Model.SessionKey">
|
||||
|
||||
@Html.HiddenFor(m => m.TemplateId)
|
||||
@Html.HiddenFor(m => m.SessionKey)
|
||||
<fieldset>
|
||||
<label>@T("Layout")</label>
|
||||
@Html.HiddenFor(m => m.State)
|
||||
|
@@ -19,7 +19,7 @@
|
||||
continue;
|
||||
}
|
||||
<li>
|
||||
<a class="element" href="@Url.Action("Create", "Element", new { layoutId = Model.ContentId, contentType = Model.ContentType, id = element.TypeName, area = "Orchard.Layouts" })">
|
||||
<a class="element" href="@Url.Action("Create", "Element", new { session = Model.Session, layoutId = Model.ContentId, contentType = Model.ContentType, id = element.TypeName, area = "Orchard.Layouts" })">
|
||||
<h2>@element.DisplayText</h2>
|
||||
</a>
|
||||
</li>
|
||||
|
@@ -11,7 +11,7 @@
|
||||
|
||||
}
|
||||
@Html.ValidationSummary()
|
||||
@using (Html.BeginFormAntiForgeryPost(Url.Action("Update", "Element", new { layoutId = Model.Layout.Id, contentType = Model.Layout.ContentItem.ContentType, area = "Orchard.Layouts" }))) {
|
||||
@using (Html.BeginFormAntiForgeryPost(Url.Action("Update", "Element", new { session = Model.SessionKey, area = "Orchard.Layouts" }))) {
|
||||
@Html.HiddenFor(m => m.TypeName)
|
||||
@Html.HiddenFor(m => m.ElementState)
|
||||
foreach (var tab in Model.Tabs) {
|
||||
|
@@ -27,12 +27,13 @@
|
||||
data-anti-forgery-token="@Html.AntiForgeryTokenValueOrchard()"
|
||||
data-render-url="@Url.Action("Render", "Element", new { contentId = contentId, contentType = contentType, area = "Orchard.Layouts" })"
|
||||
data-display-type="Design"
|
||||
data-element-browser-url="@Url.Action("Browse", "Element", new { contentId = contentId, contentType = contentType, area = "Orchard.Layouts" })"
|
||||
data-edit-url="@Url.Action("Edit", "Element", new { contentId = contentId, contentType = contentType, area = "Orchard.Layouts" })"
|
||||
data-element-browser-url="@Url.Action("Browse", "Element", new { session = Model.SessionKey, contentId = contentId, contentType = contentType, area = "Orchard.Layouts" })"
|
||||
data-edit-url="@Url.Action("Edit", "Element", new { session = Model.SessionKey, contentId = contentId, contentType = contentType, area = "Orchard.Layouts" })"
|
||||
data-apply-template-url="@Url.Action("ApplyTemplate", "Layout", new { contentId = contentId, contentType = contentType, area = "Orchard.Layouts" })"
|
||||
data-confirm-delete-prompt="@T("Are you sure you want to delete this element?")"
|
||||
data-editor-dialog-title-format="@T("$1 Properties")"
|
||||
data-editor-dialog-name="Layout">
|
||||
data-editor-dialog-name="Layout"
|
||||
data-session-key="@Model.SessionKey">
|
||||
<div class="group canvas-toolbar">
|
||||
<div class="pull-right">
|
||||
<ol class="group">
|
||||
|
Reference in New Issue
Block a user