Making the use of BaseViewModel optional

Providing BaseViewModel.From(x) static methods to adapt properties from various context objects
Adds DevTools\HomeController actions to verify results
AdaptedViewModel exposes public properties or view data dictionary entries as appropriate
Layout view engine restriction loosened to allow any ViewResult to be themed (not Partial of course)

--HG--
branch : dev
This commit is contained in:
Louis DeJardin
2010-03-01 16:43:51 -08:00
parent 8e5f470fbf
commit b4763aabaf
18 changed files with 160 additions and 50 deletions

View File

@@ -14,7 +14,7 @@ namespace Orchard.Core.Feeds.Services {
} }
public void OnResultExecuting(ResultExecutingContext filterContext) { public void OnResultExecuting(ResultExecutingContext filterContext) {
var model = filterContext.Controller.ViewData.Model as BaseViewModel; var model = BaseViewModel.From(filterContext.Result);
if (model == null) { if (model == null) {
return; return;
} }

View File

@@ -16,11 +16,7 @@ namespace Orchard.Core.Themes.Preview {
} }
public void OnResultExecuting(ResultExecutingContext filterContext) { public void OnResultExecuting(ResultExecutingContext filterContext) {
var viewResult = filterContext.Result as ViewResult; var baseViewModel = BaseViewModel.From(filterContext.Result);
if (viewResult == null)
return;
var baseViewModel = viewResult.ViewData.Model as BaseViewModel;
if (baseViewModel == null) if (baseViewModel == null)
return; return;

View File

@@ -1,19 +1,18 @@
using System.Web.Mvc; using System.Web.Mvc;
using Orchard.DevTools.Models;
using Orchard.Mvc.ViewModels; using Orchard.Mvc.ViewModels;
using Orchard.UI.Notify; using Orchard.UI.Notify;
namespace Orchard.DevTools.Controllers namespace Orchard.DevTools.Controllers {
{ //[Themed]
public class HomeController : Controller public class HomeController : Controller {
{
private readonly INotifier _notifier; private readonly INotifier _notifier;
public HomeController(INotifier notifier) { public HomeController(INotifier notifier) {
_notifier = notifier; _notifier = notifier;
} }
public ActionResult Index() public ActionResult Index() {
{
return View(new BaseViewModel()); return View(new BaseViewModel());
} }
@@ -22,5 +21,12 @@ namespace Orchard.DevTools.Controllers
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
} }
public ActionResult Simple() {
return View(new Simple { Title = "This is a simple text", Quantity = 5 });
}
public ActionResult _RenderableAction() {
return PartialView("_RenderableAction", "This is render action");
}
} }
} }

View File

@@ -0,0 +1,6 @@
namespace Orchard.DevTools.Models {
public class Simple {
public string Title { get; set; }
public int Quantity { get; set; }
}
}

View File

@@ -70,6 +70,7 @@
<Compile Include="Controllers\HomeController.cs" /> <Compile Include="Controllers\HomeController.cs" />
<Compile Include="Models\DebugLinkHandler.cs" /> <Compile Include="Models\DebugLinkHandler.cs" />
<Compile Include="Models\ShowDebugLink.cs" /> <Compile Include="Models\ShowDebugLink.cs" />
<Compile Include="Models\Simple.cs" />
<Compile Include="Permissions.cs" /> <Compile Include="Permissions.cs" />
<Compile Include="Profiler.cs" /> <Compile Include="Profiler.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
@@ -77,6 +78,8 @@
<Compile Include="ViewModels\ContentDetailsViewModel.cs" /> <Compile Include="ViewModels\ContentDetailsViewModel.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Views\Home\_RenderableAction.ascx" />
<Content Include="Views\Home\Simple.aspx" />
<Content Include="_Module.txt" /> <Content Include="_Module.txt" />
<Content Include="Views\Content\Details.aspx" /> <Content Include="Views\Content\Details.aspx" />
<Content Include="Views\Content\Index.aspx" /> <Content Include="Views\Content\Index.aspx" />

View File

@@ -0,0 +1,10 @@
<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage<Simple>" %>
<%@ Import Namespace="Orchard.DevTools.Models" %>
<h1>
<%= H(Model.Title) %></h1>
<p>
Quantity:
<%= Model.Quantity %></p>
<div style="border: solid 1px #ccc;">
<% Html.RenderAction("_RenderableAction"); %></div>

View File

@@ -0,0 +1,2 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<string>" %>
<h3><%=H(Model)%></h3>

View File

@@ -9,9 +9,9 @@
? Html.ActionLink(menuSection.Text, (string)firstSectionItem.RouteValues["action"], firstSectionItem.RouteValues).ToHtmlString() ? Html.ActionLink(menuSection.Text, (string)firstSectionItem.RouteValues["action"], firstSectionItem.RouteValues).ToHtmlString()
: string.Format("<span>{0}</span>", Html.Encode(menuSection.Text)); : string.Format("<span>{0}</span>", Html.Encode(menuSection.Text));
var classification = ""; var classification = "";
if (menuSection == Model.AdminMenu.First()) if (menuSection == Model.Menu.First())
classification = "first "; classification = "first ";
if (menuSection == Model.AdminMenu.Last()) if (menuSection == Model.Menu.Last())
classification += "last "; classification += "last ";
%> %>

View File

@@ -2,8 +2,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Web.Mvc; using System.Web.Mvc;
using Orchard.Mvc.ViewModels;
using Orchard.Security;
using Orchard.UI.Navigation;
using Orchard.UI.Notify;
using Orchard.UI.Zones;
namespace Orchard.Mvc.ViewEngines { namespace Orchard.Mvc.ViewEngines {
public class LayoutView : IView { public class LayoutView : IView {
@@ -20,25 +24,37 @@ namespace Orchard.Mvc.ViewEngines {
var layoutViewContext = LayoutViewContext.From(viewContext); var layoutViewContext = LayoutViewContext.From(viewContext);
for (var index = 0; index != _viewEngineResults.Length; ++index) { for (var index = 0; index != _viewEngineResults.Length; ++index) {
bool isFirst = index == 0;
bool isLast = index == _viewEngineResults.Length - 1;
var effectiveWriter = isLast ? viewContext.Writer : new StringWriter();
var effectiveViewData = isFirst ? viewContext.ViewData : CoerceViewData(viewContext.ViewData);
var viewEngineResult = _viewEngineResults[index]; var viewEngineResult = _viewEngineResults[index];
if (index == _viewEngineResults.Length - 1) {
viewEngineResult.View.Render(viewContext, writer); var effectiveContext = new ViewContext(
}
else {
//TEMP: to be replaced with an efficient spooling writer
var childContext = new ViewContext(
viewContext, viewContext,
viewEngineResult.View, viewEngineResult.View,
viewContext.ViewData, effectiveViewData,
viewContext.TempData, viewContext.TempData,
new StringWriter()); effectiveWriter);
viewEngineResult.View.Render(childContext, childContext.Writer);
layoutViewContext.BodyContent = childContext.Writer.ToString(); viewEngineResult.View.Render(effectiveContext, effectiveWriter);
if (!isLast)
layoutViewContext.BodyContent = effectiveWriter.ToString();
} }
} }
} }
private static ViewDataDictionary CoerceViewData(ViewDataDictionary dictionary) {
if (dictionary.Model is BaseViewModel)
return dictionary;
return new ViewDataDictionary<BaseViewModel>(BaseViewModel.From(dictionary));
} }
public void ReleaseViews(ControllerContext context) { public void ReleaseViews(ControllerContext context) {
foreach (var viewEngineResult in _viewEngineResults) { foreach (var viewEngineResult in _viewEngineResults) {
viewEngineResult.ViewEngine.ReleaseView(context, viewEngineResult.View); viewEngineResult.ViewEngine.ReleaseView(context, viewEngineResult.View);

View File

@@ -24,8 +24,6 @@ namespace Orchard.Mvc.ViewEngines {
var skipLayoutViewEngine = false; var skipLayoutViewEngine = false;
if (string.IsNullOrEmpty(masterName) == false) if (string.IsNullOrEmpty(masterName) == false)
skipLayoutViewEngine = true; skipLayoutViewEngine = true;
if (!(controllerContext.Controller.ViewData.Model is BaseViewModel))
skipLayoutViewEngine = true;
if (_viewEngines == null || _viewEngines.Count == 0) if (_viewEngines == null || _viewEngines.Count == 0)
skipLayoutViewEngine = true; skipLayoutViewEngine = true;
if (skipLayoutViewEngine) if (skipLayoutViewEngine)

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Orchard.Security;
using Orchard.UI.Navigation;
using Orchard.UI.Notify;
using Orchard.UI.Zones;
namespace Orchard.Mvc.ViewModels {
class AdaptedViewModel : BaseViewModel {
private readonly Property<ZoneCollection> _zones;
private readonly Property<IEnumerable<MenuItem>> _menu;
private readonly Property<IList<NotifyEntry>> _messages;
private readonly Property<IUser> _currentUser;
public AdaptedViewModel(ViewDataDictionary original) {
_zones = new Property<ZoneCollection>(original, "Zones", () => new ZoneCollection());
_menu = new Property<IEnumerable<MenuItem>>(original, "Menu", Enumerable.Empty<MenuItem>);
_messages = new Property<IList<NotifyEntry>>(original, "Messages", () => new NotifyEntry[0]);
_currentUser = new Property<IUser>(original, "CurrentUser", () => null);
}
public override ZoneCollection Zones { get { return _zones.Value; } set { _zones.Value = value; } }
public override IEnumerable<MenuItem> Menu { get { return _menu.Value; } set { _menu.Value = value; } }
public override IList<NotifyEntry> Messages { get { return _messages.Value; } set { _messages.Value = value; } }
public override IUser CurrentUser { get { return _currentUser.Value; } set { _currentUser.Value = value; } }
class Property<TProperty> where TProperty : class {
private readonly ViewDataDictionary _original;
private readonly string _expression;
private Func<TProperty> _builder;
private TProperty _value;
public Property(ViewDataDictionary original, string expression, Func<TProperty> builder) {
_original = original;
_expression = expression;
_builder = builder;
}
public TProperty Value {
get {
if (_value == null && _builder != null) {
_value = _original.Eval(_expression) as TProperty;
if (_value == null)
SetValue(_builder());
}
return _value;
}
set { SetValue(value); }
}
private void SetValue(TProperty value) {
_builder = null;
_value = value;
_original[_expression] = _value;
}
}
}
}

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Web.Mvc;
using Orchard.Security; using Orchard.Security;
using Orchard.UI.Navigation; using Orchard.UI.Navigation;
using Orchard.UI.Notify; using Orchard.UI.Notify;
@@ -10,18 +11,20 @@ namespace Orchard.Mvc.ViewModels {
private ZoneCollection _zones = new ZoneCollection(); private ZoneCollection _zones = new ZoneCollection();
private IList<NotifyEntry> _messages = new List<NotifyEntry>(); private IList<NotifyEntry> _messages = new List<NotifyEntry>();
public virtual ZoneCollection Zones { public virtual ZoneCollection Zones { get { return _zones; } set { _zones = value; } }
get { return _zones; } public virtual IList<NotifyEntry> Messages { get { return _messages; } set { _messages = value; } }
set { _zones = value; }
}
public virtual IList<NotifyEntry> Messages {
get { return _messages; }
set { _messages = value; }
}
public virtual IUser CurrentUser { get; set; } public virtual IUser CurrentUser { get; set; }
public virtual IEnumerable<MenuItem> Menu { get; set; } public virtual IEnumerable<MenuItem> Menu { get; set; }
public static BaseViewModel From(ViewDataDictionary viewData) {
var model = viewData.Model as BaseViewModel;
return model ?? new AdaptedViewModel(viewData);
}
public static BaseViewModel From(ActionResult actionResult) {
var viewResult = actionResult as ViewResult;
return viewResult == null ? null : From(viewResult.ViewData);
}
} }
[Obsolete("Please change your code to use BaseViewModel, as AdminViewModel will likely be removed in the near future.")] [Obsolete("Please change your code to use BaseViewModel, as AdminViewModel will likely be removed in the near future.")]

View File

@@ -153,6 +153,7 @@
<Compile Include="Extensions\Loaders\AreaExtensionLoader.cs" /> <Compile Include="Extensions\Loaders\AreaExtensionLoader.cs" />
<Compile Include="Extensions\UriExtensions.cs" /> <Compile Include="Extensions\UriExtensions.cs" />
<Compile Include="Mvc\AntiForgery\ValidateAntiForgeryTokenOrchardAttribute.cs" /> <Compile Include="Mvc\AntiForgery\ValidateAntiForgeryTokenOrchardAttribute.cs" />
<Compile Include="Mvc\ViewModels\AdaptedViewModel.cs" />
<Compile Include="OrchardException.cs" /> <Compile Include="OrchardException.cs" />
<Compile Include="Security\IAuthorizationServiceEvents.cs" /> <Compile Include="Security\IAuthorizationServiceEvents.cs" />
<Compile Include="Security\StandardPermissions.cs" /> <Compile Include="Security\StandardPermissions.cs" />
@@ -224,6 +225,7 @@
<Compile Include="Extensions\ExtensionDescriptor.cs" /> <Compile Include="Extensions\ExtensionDescriptor.cs" />
<Compile Include="Extensions\ExtensionEntry.cs" /> <Compile Include="Extensions\ExtensionEntry.cs" />
<Compile Include="IOrchardServices.cs" /> <Compile Include="IOrchardServices.cs" />
<Compile Include="Themes\ThemedAttribute.cs" />
<Compile Include="UI\Admin\AdminAttribute.cs" /> <Compile Include="UI\Admin\AdminAttribute.cs" />
<Compile Include="UI\Admin\AdminFilter.cs" /> <Compile Include="UI\Admin\AdminFilter.cs" />
<Compile Include="Mvc\AntiForgery\AntiForgeryAuthorizationFilter.cs" /> <Compile Include="Mvc\AntiForgery\AntiForgeryAuthorizationFilter.cs" />

View File

@@ -19,11 +19,7 @@ namespace Orchard.Security {
public ILogger Logger { get; set; } public ILogger Logger { get; set; }
public void OnResultExecuting(ResultExecutingContext filterContext) { public void OnResultExecuting(ResultExecutingContext filterContext) {
var viewResult = filterContext.Result as ViewResultBase; var baseViewModel = BaseViewModel.From(filterContext.Result);
if (viewResult == null)
return;
var baseViewModel = viewResult.ViewData.Model as BaseViewModel;
if (baseViewModel == null) if (baseViewModel == null)
return; return;

View File

@@ -0,0 +1,14 @@
using System;
namespace Orchard.Themes {
public class ThemedAttribute : Attribute {
public ThemedAttribute() {
Enabled = true;
}
public ThemedAttribute(bool enabled) {
Enabled = enabled;
}
public bool Enabled { get; set; }
}
}

View File

@@ -12,11 +12,7 @@ namespace Orchard.UI.Navigation {
} }
public void OnResultExecuting(ResultExecutingContext filterContext) { public void OnResultExecuting(ResultExecutingContext filterContext) {
var viewResult = filterContext.Result as ViewResult; var baseViewModel = BaseViewModel.From(filterContext.Result);
if (viewResult == null)
return;
var baseViewModel = viewResult.ViewData.Model as BaseViewModel;
if (baseViewModel == null) if (baseViewModel == null)
return; return;

View File

@@ -53,7 +53,7 @@ namespace Orchard.UI.Notify {
if (viewResult == null) if (viewResult == null)
return; return;
var baseViewModel = viewResult.ViewData.Model as BaseViewModel; var baseViewModel = BaseViewModel.From(viewResult);
// if it's not a view model that holds messages, don't touch temp data either // if it's not a view model that holds messages, don't touch temp data either
if (baseViewModel == null) if (baseViewModel == null)
return; return;

View File

@@ -13,7 +13,7 @@ namespace Orchard.UI.Resources {
} }
public void OnResultExecuting(ResultExecutingContext filterContext) { public void OnResultExecuting(ResultExecutingContext filterContext) {
var model = filterContext.Controller.ViewData.Model as BaseViewModel; var model = BaseViewModel.From(filterContext.Result);
if (model == null) { if (model == null) {
return; return;
} }