From 3d7a6a517b019688776fff0cce8355a06c344d89 Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Wed, 3 Mar 2010 11:32:00 -0800 Subject: [PATCH] Adding a Futures project to hold Widget concept Conceptual implementation of Widget and HasWidgets parts Adds "HtmlWidget" content type and "Widget" display type WidgetFilter adds widget display view models to named zones (for non-admin requests) --HG-- branch : dev --- .../Controllers/AdminController.cs | 68 ++++++++ .../Controllers/WidgetDriver.cs | 20 +++ .../Controllers/WidgetHandler.cs | 50 ++++++ .../Futures.Widgets/Futures.Widgets.csproj | 118 ++++++++++++++ .../Futures.Widgets/Models/HasWidgets.cs | 14 ++ .../Modules/Futures.Widgets/Models/Widget.cs | 13 ++ .../Modules/Futures.Widgets/Module.txt | 1 + .../Properties/AssemblyInfo.cs | 35 ++++ .../ViewModels/WidgetEditViewModel.cs | 8 + .../Futures.Widgets/Views/Admin/Edit.ascx | 12 ++ .../Modules/Futures.Widgets/Views/Web.config | 34 ++++ .../Modules/Futures.Widgets/Web.config | 154 ++++++++++++++++++ .../Modules/Futures.Widgets/WidgetFilter.cs | 43 +++++ .../Parts/DevTools.ShowDebugLink.ascx | 2 +- src/Orchard.Web/Orchard.Web.csproj | 4 + src/Orchard.sln | 9 + .../UI/Zones/ContentItemDisplayZoneItem.cs | 3 +- .../UI/Zones/ContentPartDisplayZoneItem.cs | 4 +- 18 files changed, 588 insertions(+), 4 deletions(-) create mode 100644 src/Orchard.Web/Modules/Futures.Widgets/Controllers/AdminController.cs create mode 100644 src/Orchard.Web/Modules/Futures.Widgets/Controllers/WidgetDriver.cs create mode 100644 src/Orchard.Web/Modules/Futures.Widgets/Controllers/WidgetHandler.cs create mode 100644 src/Orchard.Web/Modules/Futures.Widgets/Futures.Widgets.csproj create mode 100644 src/Orchard.Web/Modules/Futures.Widgets/Models/HasWidgets.cs create mode 100644 src/Orchard.Web/Modules/Futures.Widgets/Models/Widget.cs create mode 100644 src/Orchard.Web/Modules/Futures.Widgets/Module.txt create mode 100644 src/Orchard.Web/Modules/Futures.Widgets/Properties/AssemblyInfo.cs create mode 100644 src/Orchard.Web/Modules/Futures.Widgets/ViewModels/WidgetEditViewModel.cs create mode 100644 src/Orchard.Web/Modules/Futures.Widgets/Views/Admin/Edit.ascx create mode 100644 src/Orchard.Web/Modules/Futures.Widgets/Views/Web.config create mode 100644 src/Orchard.Web/Modules/Futures.Widgets/Web.config create mode 100644 src/Orchard.Web/Modules/Futures.Widgets/WidgetFilter.cs diff --git a/src/Orchard.Web/Modules/Futures.Widgets/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Futures.Widgets/Controllers/AdminController.cs new file mode 100644 index 000000000..6706ed689 --- /dev/null +++ b/src/Orchard.Web/Modules/Futures.Widgets/Controllers/AdminController.cs @@ -0,0 +1,68 @@ +using System; +using System.Web.Mvc; +using Futures.Widgets.Models; +using Futures.Widgets.ViewModels; +using Orchard; +using Orchard.ContentManagement; +using Orchard.Core.Common.Models; +using Orchard.Localization; +using Orchard.Settings; + +namespace Futures.Widgets.Controllers { + [ValidateInput(false)] + public class AdminController : Controller, IUpdateModel { + public AdminController(IOrchardServices services) { + Services = services; + } + + private IOrchardServices Services { get; set; } + protected virtual ISite CurrentSite { get; set; } + + public ActionResult AddWidget() { + var hasWidgetsRecord = CurrentSite.As().Record; + + var widget = Services.ContentManager.Create("HtmlWidget", init => { + init.Record.Scope = hasWidgetsRecord; + init.Record.Zone = "content"; + init.Record.Position = "after"; + init.As().Text = "Hello world!"; + }); + + return RedirectToAction("Edit", new {widget.ContentItem.Id }); + } + + public ActionResult Edit(int id, string returnUrl) { + var widget = Services.ContentManager.Get(id); + var viewModel = new WidgetEditViewModel { + Widget = Services.ContentManager.BuildEditorModel(widget), + ReturnUrl = returnUrl, + }; + return View(viewModel); + } + + [HttpPost, ActionName("Edit")] + public ActionResult EditPOST(int id, string returnUrl) { + var widget = Services.ContentManager.Get(id); + var viewModel = new WidgetEditViewModel { + Widget = Services.ContentManager.UpdateEditorModel(widget, this), + ReturnUrl = returnUrl, + }; + if (ModelState.IsValid == false) { + return View(viewModel); + } + if (string.IsNullOrEmpty(returnUrl)) { + return RedirectToAction("Edit", new { id }); + } + return Redirect(returnUrl); + } + + + bool IUpdateModel.TryUpdateModel(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) { + return TryUpdateModel(model, prefix, includeProperties, excludeProperties); + } + + void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) { + ModelState.AddModelError(key, errorMessage.ToString()); + } + } +} diff --git a/src/Orchard.Web/Modules/Futures.Widgets/Controllers/WidgetDriver.cs b/src/Orchard.Web/Modules/Futures.Widgets/Controllers/WidgetDriver.cs new file mode 100644 index 000000000..226c08f12 --- /dev/null +++ b/src/Orchard.Web/Modules/Futures.Widgets/Controllers/WidgetDriver.cs @@ -0,0 +1,20 @@ +using System.Web.Routing; +using Futures.Widgets.Models; +using Orchard.ContentManagement.Drivers; + +namespace Futures.Widgets.Controllers { + public class WidgetDriver : ContentItemDriver { + protected override RouteValueDictionary GetEditorRouteValues(Widget item) { + return new RouteValueDictionary { + {"Area", "Futures.Widgets"}, + {"Controller", "Admin"}, + {"Action", "Edit"}, + {"Id", item.ContentItem.Id} + }; + } + + protected override bool UseDefaultTemplate { + get { return true; } + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Futures.Widgets/Controllers/WidgetHandler.cs b/src/Orchard.Web/Modules/Futures.Widgets/Controllers/WidgetHandler.cs new file mode 100644 index 000000000..84ffd1181 --- /dev/null +++ b/src/Orchard.Web/Modules/Futures.Widgets/Controllers/WidgetHandler.cs @@ -0,0 +1,50 @@ +using System.Linq; +using System.Web.Routing; +using Futures.Widgets.Models; +using Orchard.ContentManagement; +using Orchard.ContentManagement.Drivers; +using Orchard.ContentManagement.Handlers; +using Orchard.Core.Common.Models; +using Orchard.Data; + +namespace Futures.Widgets.Controllers { + public class WidgetHandler : ContentHandler { + public WidgetHandler( + IRepository hasWidgetRepository, + IRepository widgetRepository) { + + // marking the "site" content type as a widget container + Filters.Add(new ActivatingFilter("site")); + + // adding parts to the "HtmlWidget" content type + Filters.Add(new ActivatingFilter("HtmlWidget")); + Filters.Add(new ActivatingFilter("HtmlWidget")); + + // providing standard storage support for widget records + Filters.Add(StorageFilter.For(hasWidgetRepository)); + Filters.Add(StorageFilter.For(widgetRepository)); + + OnLoaded( + (ctx, part) => part.WidgetField.Loader( + () => ctx.ContentManager + .Query() + .Where(x => x.Scope == part.Record) + .List().ToList())); + } + } + + public class WidgetDriver : ContentItemDriver { + protected override RouteValueDictionary GetEditorRouteValues(Widget item) { + return new RouteValueDictionary { + {"Area", "Futures.Widgets"}, + {"Controller", "Admin"}, + {"Action", "Edit"}, + {"Id", item.ContentItem.Id} + }; + } + + protected override bool UseDefaultTemplate { + get { return true; } + } + } +} diff --git a/src/Orchard.Web/Modules/Futures.Widgets/Futures.Widgets.csproj b/src/Orchard.Web/Modules/Futures.Widgets/Futures.Widgets.csproj new file mode 100644 index 000000000..8ee420c90 --- /dev/null +++ b/src/Orchard.Web/Modules/Futures.Widgets/Futures.Widgets.csproj @@ -0,0 +1,118 @@ + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {E65E5633-C0FF-453C-A906-481C14F969D6} + {F85E285D-A4E0-4152-9332-AB1D724D3325};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Futures.Widgets + Futures.Widgets + v3.5 + false + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + + + 3.5 + + + 3.5 + + + 3.5 + + + False + ..\..\..\..\..\..\Program Files\Microsoft ASP.NET\ASP.NET MVC 2\\Assemblies\System.Web.Mvc.dll + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} + Orchard + + + {9916839C-39FC-4CEB-A5AF-89CA7E87119F} + Orchard.Core + + + + + + + + + + + + + + + + False + True + 17593 + / + + + False + True + http://orchard.codeplex.com + False + + + + + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Futures.Widgets/Models/HasWidgets.cs b/src/Orchard.Web/Modules/Futures.Widgets/Models/HasWidgets.cs new file mode 100644 index 000000000..b30dde572 --- /dev/null +++ b/src/Orchard.Web/Modules/Futures.Widgets/Models/HasWidgets.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Orchard.ContentManagement; +using Orchard.ContentManagement.Records; +using Orchard.Core.Common.Utilities; + +namespace Futures.Widgets.Models { + public class HasWidgets : ContentPart { + public LazyField> WidgetField = new LazyField>(); + public IList Widgets { get { return WidgetField.Value; } set { WidgetField.Value = value; } } + } + + public class HasWidgetsRecord : ContentPartRecord { + } +} diff --git a/src/Orchard.Web/Modules/Futures.Widgets/Models/Widget.cs b/src/Orchard.Web/Modules/Futures.Widgets/Models/Widget.cs new file mode 100644 index 000000000..c2c693eec --- /dev/null +++ b/src/Orchard.Web/Modules/Futures.Widgets/Models/Widget.cs @@ -0,0 +1,13 @@ +using Orchard.ContentManagement; +using Orchard.ContentManagement.Records; + +namespace Futures.Widgets.Models { + public class Widget : ContentPart { + } + + public class WidgetRecord : ContentPartRecord { + public virtual HasWidgetsRecord Scope { get; set; } + public virtual string Zone { get; set; } + public virtual string Position { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Futures.Widgets/Module.txt b/src/Orchard.Web/Modules/Futures.Widgets/Module.txt new file mode 100644 index 000000000..b38d568c2 --- /dev/null +++ b/src/Orchard.Web/Modules/Futures.Widgets/Module.txt @@ -0,0 +1 @@ +name: Widgets diff --git a/src/Orchard.Web/Modules/Futures.Widgets/Properties/AssemblyInfo.cs b/src/Orchard.Web/Modules/Futures.Widgets/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..b0a50137d --- /dev/null +++ b/src/Orchard.Web/Modules/Futures.Widgets/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Futures.Widgets")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft IT")] +[assembly: AssemblyProduct("Futures.Widgets")] +[assembly: AssemblyCopyright("Copyright © Microsoft IT 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8c179868-e814-4277-a0af-b71707c3bff4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Orchard.Web/Modules/Futures.Widgets/ViewModels/WidgetEditViewModel.cs b/src/Orchard.Web/Modules/Futures.Widgets/ViewModels/WidgetEditViewModel.cs new file mode 100644 index 000000000..1f288285e --- /dev/null +++ b/src/Orchard.Web/Modules/Futures.Widgets/ViewModels/WidgetEditViewModel.cs @@ -0,0 +1,8 @@ +using Orchard.Mvc.ViewModels; + +namespace Futures.Widgets.ViewModels { + public class WidgetEditViewModel : BaseViewModel { + public ContentItemViewModel Widget { get; set; } + public string ReturnUrl { get; set;} + } +} diff --git a/src/Orchard.Web/Modules/Futures.Widgets/Views/Admin/Edit.ascx b/src/Orchard.Web/Modules/Futures.Widgets/Views/Admin/Edit.ascx new file mode 100644 index 000000000..f42d7c0cf --- /dev/null +++ b/src/Orchard.Web/Modules/Futures.Widgets/Views/Admin/Edit.ascx @@ -0,0 +1,12 @@ +<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> +<%@ Import Namespace="Futures.Widgets.ViewModels" %> +<%@ Import Namespace="Orchard.Mvc.Html" %> +

<%=Html.TitleForPage(T("Edit Widget").ToString()) %>

+<% using (Html.BeginFormAntiForgeryPost()) { %> + <%= Html.ValidationSummary() %> + <%= Html.EditorForItem(m => m.Widget) %> +
+ <%= Html.HiddenFor(m => m.ReturnUrl) %> + +
+<%} %> diff --git a/src/Orchard.Web/Modules/Futures.Widgets/Views/Web.config b/src/Orchard.Web/Modules/Futures.Widgets/Views/Web.config new file mode 100644 index 000000000..7022197d4 --- /dev/null +++ b/src/Orchard.Web/Modules/Futures.Widgets/Views/Web.config @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Futures.Widgets/Web.config b/src/Orchard.Web/Modules/Futures.Widgets/Web.config new file mode 100644 index 000000000..b1c399e16 --- /dev/null +++ b/src/Orchard.Web/Modules/Futures.Widgets/Web.config @@ -0,0 +1,154 @@ + + + + + + + +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Futures.Widgets/WidgetFilter.cs b/src/Orchard.Web/Modules/Futures.Widgets/WidgetFilter.cs new file mode 100644 index 000000000..dd6f14fc3 --- /dev/null +++ b/src/Orchard.Web/Modules/Futures.Widgets/WidgetFilter.cs @@ -0,0 +1,43 @@ +using System; +using System.Linq; +using System.Web.Mvc; +using Futures.Widgets.Models; +using Orchard.ContentManagement; +using Orchard.Mvc.Filters; +using Orchard.Mvc.ViewModels; +using Orchard.Settings; +using Orchard.UI.Admin; + +namespace Futures.Widgets { + public class WidgetFilter : FilterProvider, IActionFilter { + private readonly IContentManager _contentManager; + + public WidgetFilter(IContentManager contentManager) { + _contentManager = contentManager; + } + + public virtual ISite CurrentSite { get; set; } + + public void OnActionExecuting(ActionExecutingContext filterContext) { + } + + public void OnActionExecuted(ActionExecutedContext filterContext) { + var model = BaseViewModel.From(filterContext.Result); + if (model == null || AdminFilter.IsApplied(filterContext.RequestContext)) { + return; + } + + var siteWidgets = CurrentSite.As(); + if (siteWidgets == null) { + return; + } + + var zones = model.Zones; + foreach (var widget in siteWidgets.Widgets) { + zones.AddDisplayItem( + widget.Record.Zone + ":" + widget.Record.Position, + _contentManager.BuildDisplayModel(widget, "Widget")); + } + } + } +} diff --git a/src/Orchard.Web/Modules/Orchard.DevTools/Views/DisplayTemplates/Parts/DevTools.ShowDebugLink.ascx b/src/Orchard.Web/Modules/Orchard.DevTools/Views/DisplayTemplates/Parts/DevTools.ShowDebugLink.ascx index b5ced4224..0ff4e2ef6 100644 --- a/src/Orchard.Web/Modules/Orchard.DevTools/Views/DisplayTemplates/Parts/DevTools.ShowDebugLink.ascx +++ b/src/Orchard.Web/Modules/Orchard.DevTools/Views/DisplayTemplates/Parts/DevTools.ShowDebugLink.ascx @@ -1,6 +1,6 @@ <%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> <%@ Import Namespace="Orchard.DevTools.Models" %>
<%=T( - "DevTools: editing {0}", + "DevTools: displaying {0}", Html.ActionLink(T("{0} #{1} v{2}", Model.ContentItem.ContentType, Model.ContentItem.Id, Model.ContentItem.Version).ToString(), "details", "content", new { area = "Orchard.DevTools", Model.ContentItem.Id, Model.ContentItem.Version }, new { }) ) %>
diff --git a/src/Orchard.Web/Orchard.Web.csproj b/src/Orchard.Web/Orchard.Web.csproj index 184187747..260d326dd 100644 --- a/src/Orchard.Web/Orchard.Web.csproj +++ b/src/Orchard.Web/Orchard.Web.csproj @@ -102,6 +102,10 @@ {9916839C-39FC-4CEB-A5AF-89CA7E87119F} Orchard.Core + + {E65E5633-C0FF-453C-A906-481C14F969D6} + Futures.Widgets + {63FBD4D9-E1DA-4A7B-AA6A-D6074FE50867} Orchard.Blogs diff --git a/src/Orchard.sln b/src/Orchard.sln index f0297539d..6035c7e86 100644 --- a/src/Orchard.sln +++ b/src/Orchard.sln @@ -47,6 +47,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSBuild.Orchard.Tasks", "To EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSBuild.Orchard.Tasks.Tests", "Tools\MSBuild.Orchard.Tasks.Tests\MSBuild.Orchard.Tasks.Tests.csproj", "{4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Futures", "Futures", "{E75A4CE4-CAA6-41E4-B951-33ACC60DC77C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Futures.Widgets", "Orchard.Web\Modules\Futures.Widgets\Futures.Widgets.csproj", "{E65E5633-C0FF-453C-A906-481C14F969D6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -133,6 +137,10 @@ Global {4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56}.Debug|Any CPU.Build.0 = Debug|Any CPU {4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56}.Release|Any CPU.ActiveCfg = Release|Any CPU {4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56}.Release|Any CPU.Build.0 = Release|Any CPU + {E65E5633-C0FF-453C-A906-481C14F969D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E65E5633-C0FF-453C-A906-481C14F969D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E65E5633-C0FF-453C-A906-481C14F969D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E65E5633-C0FF-453C-A906-481C14F969D6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -155,5 +163,6 @@ Global {8C7FCBC2-E6E1-405E-BFB5-D8D9E67A09C4} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {5E5E7A21-C7B2-44D8-8593-2F9541AE041D} = {383DBA32-4A3E-48D1-AAC3-75377A694452} {4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56} = {383DBA32-4A3E-48D1-AAC3-75377A694452} + {E65E5633-C0FF-453C-A906-481C14F969D6} = {E75A4CE4-CAA6-41E4-B951-33ACC60DC77C} EndGlobalSection EndGlobal diff --git a/src/Orchard/UI/Zones/ContentItemDisplayZoneItem.cs b/src/Orchard/UI/Zones/ContentItemDisplayZoneItem.cs index 604b3aba6..5e5857f84 100644 --- a/src/Orchard/UI/Zones/ContentItemDisplayZoneItem.cs +++ b/src/Orchard/UI/Zones/ContentItemDisplayZoneItem.cs @@ -7,7 +7,8 @@ namespace Orchard.UI.Zones { public ContentItemViewModel ViewModel { get; set; } public override void Execute(HtmlHelper html) { - html.DisplayForItem(ViewModel); + var htmlString = html.DisplayForItem(ViewModel); + html.ViewContext.Writer.Write(htmlString); } } } \ No newline at end of file diff --git a/src/Orchard/UI/Zones/ContentPartDisplayZoneItem.cs b/src/Orchard/UI/Zones/ContentPartDisplayZoneItem.cs index 91f587c41..f9e8ea408 100644 --- a/src/Orchard/UI/Zones/ContentPartDisplayZoneItem.cs +++ b/src/Orchard/UI/Zones/ContentPartDisplayZoneItem.cs @@ -8,8 +8,8 @@ namespace Orchard.UI.Zones { public string Prefix { get; set; } public override void Execute(HtmlHelper html) { - html.ViewContext.Writer.Write( - html.DisplayFor(m => Model, TemplateName, Prefix)); + var htmlString = html.DisplayFor(m => Model, TemplateName, Prefix); + html.ViewContext.Writer.Write(htmlString); } } } \ No newline at end of file