From c748ee447cd3a9f8203e21dee11df8e99a91028a Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Wed, 4 Dec 2013 17:09:03 -0800 Subject: [PATCH] Reviving Orchard.Lists --- .../Drivers/ContainablePartDriver.cs | 37 +- .../Containers/Drivers/ContainerPartDriver.cs | 171 +++--- .../Drivers/ContainerWidgetPartDriver.cs | 50 -- .../Drivers/CustomPropertiesDriver.cs | 5 +- .../ContentDefinitionManagerExtensions.cs | 22 + .../Extensions/ContentQueryExtensions.cs | 5 +- .../Handlers/ContainablePartHandler.cs | 30 +- .../Handlers/ContainerPartHandler.cs | 71 ++- .../Handlers/ContainerWidgetPartHandler.cs | 15 + src/Orchard.Web/Core/Containers/Migrations.cs | 126 ++-- .../Core/Containers/Models/ContainablePart.cs | 8 +- .../Core/Containers/Models/ContainerPart.cs | 69 ++- .../Containers/Models/CustomPropertiesPart.cs | 5 +- .../Containers/Models/OrderByDirection.cs | 6 - .../Core/Containers/Placement.info | 2 +- .../Containers/Services/ContainerService.cs | 237 ++++++++ .../Containers/Services/ListViewProvider.cs | 17 + .../Containers/Services/ListViewService.cs | 41 ++ .../Core/Containers/Services/Randomizer.cs | 22 + .../Settings/ContainableSettings.cs | 34 ++ .../Containers/Settings/ContainerSettings.cs | 47 +- .../Containers/Settings/LocationSettings.cs | 6 - .../ViewModels/ContainableViewModel.cs | 4 +- .../ContainerTypePartSettingsViewModel.cs | 19 + .../ViewModels/ContainerViewModel.cs | 32 +- .../ContainableTypePartSettings.cshtml | 11 + .../ContainerTypePartSettings.cshtml | 9 - .../ContainerTypePartSettingsViewModel.cshtml | 39 ++ .../Views/EditorTemplates/Containable.cshtml | 20 +- .../Views/EditorTemplates/Container.cshtml | 75 ++- .../EditorTemplates/ListViewPicker.cshtml | 7 + src/Orchard.Web/Core/Orchard.Core.csproj | 19 +- .../Controllers/AdminController.cs | 19 +- .../Scripts/ContentPicker.js | 13 +- .../Views/ContentPicker.SummaryAdmin.cshtml | 9 +- .../Views/ContentPicker.cshtml | 11 +- .../Modules/Orchard.Lists/AdminMenu.cs | 74 ++- .../Controllers/AdminController.cs | 555 +++++++++++------- .../Drivers/ContainablePartDriver.cs | 18 + .../Drivers/ContainerPartDriver.cs | 6 +- .../Handlers/ContainerPartHandler.cs | 7 +- .../Orchard.Lists/Helpers/StringExtensions.cs | 26 + .../ListViews/CondensedListView.cs | 26 + .../ListViews/DefaultListView.cs | 26 + .../Modules/Orchard.Lists/Migrations.cs | 27 +- .../Modules/Orchard.Lists/Module.txt | 4 +- .../Orchard.Lists/Orchard.Lists.csproj | 94 ++- .../Modules/Orchard.Lists/Placement.info | 23 +- .../Modules/Orchard.Lists/Routes.cs | 16 + .../Orchard.Lists/Scripts/common-admin.js | 8 + .../Orchard.Lists/Scripts/common-admin.min.js | 2 + .../Scripts/common-admin.min.js.map | 8 + .../Orchard.Lists/Scripts/nprogress.js | 279 +++++++++ .../Orchard.Lists/Scripts/nprogress.min.js | 2 + .../Scripts/nprogress.min.js.map | 8 + .../Scripts/orchard-lists-admin.js | 193 +++++- .../Scripts/orchard-lists-admin.min.js | 2 + .../Scripts/orchard-lists-admin.min.js.map | 8 + .../Modules/Orchard.Lists/Shapes.cs | 35 ++ .../Orchard.Lists/Styles/common-admin.css | 44 ++ .../Orchard.Lists/Styles/common-admin.min.css | 1 + .../Orchard.Lists/Styles/images/icons.png | Bin 0 -> 2510 bytes .../Styles/images/menu.list-definition.png | Bin 0 -> 1098 bytes .../Orchard.Lists/Styles/images/move.gif | Bin 0 -> 897 bytes .../Orchard.Lists/Styles/images/offline.gif | Bin 0 -> 293 bytes .../Orchard.Lists/Styles/images/online.gif | Bin 0 -> 405 bytes .../Styles/images/view.condensed.png | Bin 0 -> 1889 bytes .../Styles/images/view.default.png | Bin 0 -> 1906 bytes .../Orchard.Lists/Styles/list-admin.css | 125 ++++ .../Orchard.Lists/Styles/list-admin.min.css | 1 + .../Orchard.Lists/Styles/menu.list-admin.css | 23 +- .../Orchard.Lists/Styles/nprogress.css | 64 ++ .../ViewModels/ChooseContentsViewModel.cs | 21 - .../ViewModels/ListContentsViewModel.cs | 27 +- .../Orchard.Lists/Views/Admin/Choose.cshtml | 64 -- .../Orchard.Lists/Views/Admin/Index.cshtml | 43 ++ .../Orchard.Lists/Views/Admin/List.cshtml | 105 ++-- .../Views/Admin/SelectType.cshtml | 5 + .../Views/BreadcrumbItem.Last.cshtml | 1 + .../Orchard.Lists/Views/BreadcrumbItem.cshtml | 1 + .../Views/Breadcrumbs.ContentItem.cshtml | 13 + .../Orchard.Lists/Views/Breadcrumbs.cshtml | 20 + .../Content.SummaryAdminCondensed.cshtml | 28 + .../Views/Dialog.CloseButton.cshtml | 8 + .../Orchard.Lists/Views/ListNavigation.cshtml | 11 + .../Views/ListView.Condensed.cshtml | 49 ++ .../Views/ListView.Default.cshtml | 49 ++ .../Views/ListViewButtons.cshtml | 14 + .../Views/Parts.Container.BulkActions.cshtml | 63 ++ ...ts.Container.Contained.SummaryAdmin.cshtml | 3 +- ...ts.Container.ItemCount.SummaryAdmin.cshtml | 1 + .../Views/Parts.Container.Manage.cshtml | 26 +- .../Styles/dialog-mode.css | 5 +- .../Upgrade/Controllers/RouteController.cs | 8 - .../Themes/TheAdmin/Styles/images/error.gif | Bin 0 -> 276 bytes .../Themes/TheAdmin/Styles/site.css | 7 +- .../Themes/TheAdmin/Views/Pager.cshtml | 54 +- src/Orchard.sln | 3 +- src/Orchard/Mvc/Html/ContentItemExtensions.cs | 7 +- 99 files changed, 2836 insertions(+), 818 deletions(-) create mode 100644 src/Orchard.Web/Core/Containers/Extensions/ContentDefinitionManagerExtensions.cs create mode 100644 src/Orchard.Web/Core/Containers/Handlers/ContainerWidgetPartHandler.cs delete mode 100644 src/Orchard.Web/Core/Containers/Models/OrderByDirection.cs create mode 100644 src/Orchard.Web/Core/Containers/Services/ContainerService.cs create mode 100644 src/Orchard.Web/Core/Containers/Services/ListViewProvider.cs create mode 100644 src/Orchard.Web/Core/Containers/Services/ListViewService.cs create mode 100644 src/Orchard.Web/Core/Containers/Services/Randomizer.cs create mode 100644 src/Orchard.Web/Core/Containers/Settings/ContainableSettings.cs delete mode 100644 src/Orchard.Web/Core/Containers/Settings/LocationSettings.cs create mode 100644 src/Orchard.Web/Core/Containers/ViewModels/ContainerTypePartSettingsViewModel.cs create mode 100644 src/Orchard.Web/Core/Containers/Views/DefinitionTemplates/ContainableTypePartSettings.cshtml delete mode 100644 src/Orchard.Web/Core/Containers/Views/DefinitionTemplates/ContainerTypePartSettings.cshtml create mode 100644 src/Orchard.Web/Core/Containers/Views/DefinitionTemplates/ContainerTypePartSettingsViewModel.cshtml create mode 100644 src/Orchard.Web/Core/Containers/Views/EditorTemplates/ListViewPicker.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Drivers/ContainablePartDriver.cs create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Helpers/StringExtensions.cs create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/ListViews/CondensedListView.cs create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/ListViews/DefaultListView.cs create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Scripts/common-admin.js create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Scripts/common-admin.min.js create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Scripts/common-admin.min.js.map create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Scripts/nprogress.js create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Scripts/nprogress.min.js create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Scripts/nprogress.min.js.map create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Scripts/orchard-lists-admin.min.js create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Scripts/orchard-lists-admin.min.js.map create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Shapes.cs create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Styles/common-admin.css create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Styles/common-admin.min.css create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Styles/images/icons.png create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Styles/images/menu.list-definition.png create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Styles/images/move.gif create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Styles/images/offline.gif create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Styles/images/online.gif create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Styles/images/view.condensed.png create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Styles/images/view.default.png create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Styles/list-admin.css create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Styles/list-admin.min.css create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Styles/nprogress.css delete mode 100644 src/Orchard.Web/Modules/Orchard.Lists/ViewModels/ChooseContentsViewModel.cs delete mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/Choose.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/Index.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/SelectType.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/BreadcrumbItem.Last.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/BreadcrumbItem.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/Breadcrumbs.ContentItem.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/Breadcrumbs.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/Content.SummaryAdminCondensed.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/Dialog.CloseButton.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/ListNavigation.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/ListView.Condensed.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/ListView.Default.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/ListViewButtons.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/Parts.Container.BulkActions.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Lists/Views/Parts.Container.ItemCount.SummaryAdmin.cshtml create mode 100644 src/Orchard.Web/Themes/TheAdmin/Styles/images/error.gif diff --git a/src/Orchard.Web/Core/Containers/Drivers/ContainablePartDriver.cs b/src/Orchard.Web/Core/Containers/Drivers/ContainablePartDriver.cs index 0feaddea9..b0a682792 100644 --- a/src/Orchard.Web/Core/Containers/Drivers/ContainablePartDriver.cs +++ b/src/Orchard.Web/Core/Containers/Drivers/ContainablePartDriver.cs @@ -1,10 +1,13 @@ using System; using System.Linq; using System.Web.Mvc; +using System.Xml; using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; using Orchard.Core.Common.Models; using Orchard.Core.Containers.Models; +using Orchard.Core.Containers.Services; +using Orchard.Core.Containers.Settings; using Orchard.Core.Containers.ViewModels; using Orchard.Localization; using Orchard.ContentManagement.Handlers; @@ -12,9 +15,11 @@ using Orchard.ContentManagement.Handlers; namespace Orchard.Core.Containers.Drivers { public class ContainablePartDriver : ContentPartDriver { private readonly IContentManager _contentManager; + private readonly IContainerService _containerService; - public ContainablePartDriver(IContentManager contentManager) { + public ContainablePartDriver(IContentManager contentManager, IContainerService containerService) { _contentManager = contentManager; + _containerService = containerService; T = NullLocalizer.Instance; } @@ -28,28 +33,37 @@ namespace Orchard.Core.Containers.Drivers { return ContentShape( "Parts_Containable_Edit", () => { + var settings = part.TypePartDefinition.Settings.GetModel(); var commonPart = part.As(); + var model = new ContainableViewModel { + ShowContainerPicker = settings.ShowContainerPicker, + ShowPositionEditor = settings.ShowPositionEditor + }; - var model = new ContainableViewModel(); if (commonPart != null && commonPart.Container != null) { model.ContainerId = commonPart.Container.Id; } + if (part.Id == 0 && commonPart != null && commonPart.Container != null) { + part.Position = _containerService.GetFirstPosition(commonPart.Container.Id) + 1; + } + if (updater != null) { var oldContainerId = model.ContainerId; - updater.TryUpdateModel(model, "Containable", null, null); + updater.TryUpdateModel(model, "Containable", null, new[] { "ShowContainerPicker", "ShowPositionEditor" }); if (oldContainerId != model.ContainerId) { if (commonPart != null) { var containerItem = _contentManager.Get(model.ContainerId, VersionOptions.Latest); commonPart.Container = containerItem; } } - part.Weight = model.Weight; + part.Position = model.Position; } - // note: string.isnullorempty not being recognized by linq-to-nhibernate hence the inline or - var containers = _contentManager.Query(VersionOptions.Latest) - .Where(ctr => ctr.ItemContentType == null || ctr.ItemContentType == "" || ctr.ItemContentType == part.ContentItem.ContentType).List(); + var containers = _contentManager + .Query(VersionOptions.Latest) + .List() + .Where(container => container.ItemContentTypes.Any(type => type.Name == part.TypeDefinition.Name)); var listItems = new[] { new SelectListItem { Text = T("(None)").Text, Value = "0" } } .Concat(containers.Select(x => new SelectListItem { @@ -60,21 +74,18 @@ namespace Orchard.Core.Containers.Drivers { .ToList(); model.AvailableContainers = new SelectList(listItems, "Value", "Text", model.ContainerId); - model.Weight = part.Weight; + model.Position = part.Position; return shapeHelper.EditorTemplate(TemplateName: "Containable", Model: model, Prefix: "Containable"); }); } protected override void Importing(ContainablePart part, ImportContentContext context) { - var weight = context.Attribute(part.PartDefinition.Name, "Weight"); - if (weight != null) { - part.Weight = Convert.ToInt32(weight); - } + context.ImportAttribute(part.PartDefinition.Name, "Position", s => part.Position = XmlConvert.ToInt32(s)); } protected override void Exporting(ContainablePart part, ExportContentContext context) { - context.Element(part.PartDefinition.Name).SetAttributeValue("Weight", part.Weight); + context.Element(part.PartDefinition.Name).SetAttributeValue("Position", part.Position); } } } diff --git a/src/Orchard.Web/Core/Containers/Drivers/ContainerPartDriver.cs b/src/Orchard.Web/Core/Containers/Drivers/ContainerPartDriver.cs index c530fc5c5..5794cefa5 100644 --- a/src/Orchard.Web/Core/Containers/Drivers/ContainerPartDriver.cs +++ b/src/Orchard.Web/Core/Containers/Drivers/ContainerPartDriver.cs @@ -1,16 +1,17 @@ -using System; +using System.Collections.Generic; using System.Linq; -using System.Web.Mvc; +using System.Xml; using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; using Orchard.ContentManagement.Handlers; using Orchard.ContentManagement.MetaData; +using Orchard.ContentManagement.MetaData.Models; using Orchard.Core.Common.Models; using Orchard.Core.Containers.Models; +using Orchard.Core.Containers.Services; using Orchard.Core.Containers.ViewModels; using Orchard.Localization; using Orchard.UI.Notify; -using Orchard.Core.Containers.Extensions; using System.Web.Routing; using Orchard.Settings; using Orchard.Core.Feeds; @@ -23,17 +24,19 @@ namespace Orchard.Core.Containers.Drivers { private readonly IContentManager _contentManager; private readonly ISiteService _siteService; private readonly IFeedManager _feedManager; + private readonly IContainerService _containerService; public ContainerPartDriver( IContentDefinitionManager contentDefinitionManager, IOrchardServices orchardServices, ISiteService siteService, - IFeedManager feedManager) { + IFeedManager feedManager, IContainerService containerService) { _contentDefinitionManager = contentDefinitionManager; _orchardServices = orchardServices; _contentManager = orchardServices.ContentManager; _siteService = siteService; _feedManager = feedManager; + _containerService = containerService; T = NullLocalizer.Instance; } @@ -44,117 +47,115 @@ namespace Orchard.Core.Containers.Drivers { if (!part.ItemsShown) return null; - return ContentShape("Parts_Container_Contained", - () => { - var container = part.ContentItem; + return ContentShape("Parts_Container_Contained", () => { + var container = part.ContentItem; + var query = _contentManager + .Query(VersionOptions.Published) + .Join().Where(x => x.Container.Id == container.Id) + .Join().OrderByDescending(x => x.Position); - IContentQuery query = _contentManager - .Query(VersionOptions.Published) - .Join().Where(cr => cr.Container.Id == container.Id); + var metadata = container.ContentManager.GetItemMetadata(container); + if (metadata!=null) + _feedManager.Register(metadata.DisplayText, "rss", new RouteValueDictionary { { "containerid", container.Id } }); - var descendingOrder = part.OrderByDirection == (int)OrderByDirection.Descending; - query = query.OrderBy(part.OrderByProperty, descendingOrder); - var metadata = container.ContentManager.GetItemMetadata(container); - if (metadata!=null) - _feedManager.Register(metadata.DisplayText, "rss", new RouteValueDictionary { { "containerid", container.Id } }); + var pager = new Pager(_siteService.GetSiteSettings(), part.PagerParameters); + pager.PageSize = part.PagerParameters.PageSize != null && part.Paginated + ? pager.PageSize + : part.PageSize; - var pager = new Pager(_siteService.GetSiteSettings(), part.PagerParameters); - pager.PageSize = part.PagerParameters.PageSize != null && part.Paginated - ? pager.PageSize - : part.PageSize; + var pagerShape = shapeHelper.Pager(pager).TotalItemCount(query.Count()); + var startIndex = part.Paginated ? pager.GetStartIndex() : 0; + var pageOfItems = query.Slice(startIndex, pager.PageSize).ToList(); - var pagerShape = shapeHelper.Pager(pager).TotalItemCount(query.Count()); + var listShape = shapeHelper.List(); + listShape.AddRange(pageOfItems.Select(item => _contentManager.BuildDisplay(item, "Summary"))); + listShape.Classes.Add("content-items"); + listShape.Classes.Add("list-items"); - var startIndex = part.Paginated ? pager.GetStartIndex() : 0; - var pageOfItems = query.Slice(startIndex, pager.PageSize).ToList(); - - var listShape = shapeHelper.List(); - listShape.AddRange(pageOfItems.Select(item => _contentManager.BuildDisplay(item, "Summary"))); - listShape.Classes.Add("content-items"); - listShape.Classes.Add("list-items"); - - return shapeHelper.Parts_Container_Contained( - List: listShape, - Pager: part.Paginated ? pagerShape : null - ); - }); + return shapeHelper.Parts_Container_Contained( + List: listShape, + Pager: part.Paginated ? pagerShape : null + ); + }); } protected override DriverResult Editor(ContainerPart part, dynamic shapeHelper) { - // if there are no containable items then show a nice little warning if (!_contentDefinitionManager.ListTypeDefinitions().Any(typeDefinition => typeDefinition.Parts.Any(partDefinition => partDefinition.PartDefinition.Name == "ContainablePart"))) { _orchardServices.Notifier.Warning(T("There are no content types in the system with a Containable part attached. Consider adding a Containable part to some content type, existing or new, in order to relate items to this (Container enabled) item.")); } - return Editor(part, (IUpdateModel)null, shapeHelper); } protected override DriverResult Editor(ContainerPart part, IUpdateModel updater, dynamic shapeHelper) { - return ContentShape( - "Parts_Container_Edit", - () => { - var model = new ContainerViewModel { Part = part }; - // todo: is there a non-string comparison way to find ContainableParts? - var containables = _contentDefinitionManager.ListTypeDefinitions().Where(td => td.Parts.Any(p => p.PartDefinition.Name == "ContainablePart")).ToList(); - var listItems = new[] { new SelectListItem { Text = T("(Any)").Text, Value = "" } } - .Concat(containables.Select(x => new SelectListItem { - Value = Convert.ToString(x.Name), - Text = x.DisplayName, - Selected = x.Name == model.Part.Record.ItemContentType, - })) - .ToList(); + return ContentShape("Parts_Container_Edit", () => { + var containables = !part.ContainerSettings.RestrictItemContentTypes ? _containerService.GetContainableTypes().ToList() : new List(0); + var model = new ContainerViewModel { + AdminMenuPosition = part.AdminMenuPosition, + AdminMenuText = part.AdminMenuText, + AdminMenuImageSet = part.AdminMenuImageSet, + ItemsShown = part.ItemsShown, + PageSize = part.PageSize, + Paginated = part.Paginated, + SelectedItemContentTypes = part.ItemContentTypes.Select(x => x.Name).ToList(), + ShowOnAdminMenu = part.ShowOnAdminMenu, + AvailableItemContentTypes = containables, + RestrictItemContentTypes = part.ContainerSettings.RestrictItemContentTypes, + EnablePositioning = part.Record.EnablePositioning, + OverrideEnablePositioning = part.ContainerSettings.EnablePositioning == null + }; + + if (updater != null) { + if (updater.TryUpdateModel(model, "Container", null, new[] { "OverrideEnablePositioning" })) { + part.AdminMenuPosition = model.AdminMenuPosition; + part.AdminMenuText = model.AdminMenuText; + part.AdminMenuImageSet = model.AdminMenuImageSet; + part.ItemsShown = model.ItemsShown; + part.PageSize = model.PageSize; + part.Paginated = model.Paginated; + part.ShowOnAdminMenu = model.ShowOnAdminMenu; - model.AvailableContainables = new SelectList(listItems, "Value", "Text", model.Part.ItemContentType); + if (!part.ContainerSettings.RestrictItemContentTypes) { + part.ItemContentTypes = _contentDefinitionManager.ListTypeDefinitions().Where(x => model.SelectedItemContentTypes.Contains(x.Name)); + } - if (updater != null) { - updater.TryUpdateModel(model, "Container", null, null); + if (model.OverrideEnablePositioning) { + part.Record.EnablePositioning = model.EnablePositioning; + } } + } - return shapeHelper.EditorTemplate(TemplateName: "Container", Model: model, Prefix: "Container"); - }); + return shapeHelper.EditorTemplate(TemplateName: "Container", Model: model, Prefix: "Container"); + }); } protected override void Importing(ContainerPart part, ImportContentContext context) { - var itemContentType = context.Attribute(part.PartDefinition.Name, "ItemContentType"); + var itemContentType = context.Attribute(part.PartDefinition.Name, "ItemContentTypes"); if (itemContentType != null) { if (_contentDefinitionManager.GetTypeDefinition(itemContentType) != null) { - part.Record.ItemContentType = itemContentType; + part.Record.ItemContentTypes = itemContentType; } } - var itemsShown = context.Attribute(part.PartDefinition.Name, "ItemsShown"); - if (itemsShown != null) { - part.Record.ItemsShown = Convert.ToBoolean(itemsShown); - } - - var paginated = context.Attribute(part.PartDefinition.Name, "Paginated"); - if (paginated != null) { - part.Record.Paginated = Convert.ToBoolean(paginated); - } - - var pageSize = context.Attribute(part.PartDefinition.Name, "PageSize"); - if (pageSize != null) { - part.Record.PageSize = Convert.ToInt32(pageSize); - } - - var orderByProperty = context.Attribute(part.PartDefinition.Name, "OrderByProperty"); - if (orderByProperty != null) { - part.Record.OrderByProperty = orderByProperty; - } - - var orderByDirection = context.Attribute(part.PartDefinition.Name, "OrderByDirection"); - if (orderByDirection != null) { - part.Record.OrderByDirection = Convert.ToInt32(orderByDirection); - } + context.ImportAttribute(part.PartDefinition.Name, "ItemsShown", s => part.ItemsShown = XmlConvert.ToBoolean(s)); + context.ImportAttribute(part.PartDefinition.Name, "Paginated", s => part.Paginated = XmlConvert.ToBoolean(s)); + context.ImportAttribute(part.PartDefinition.Name, "PageSize", s => part.PageSize = XmlConvert.ToInt32(s)); + context.ImportAttribute(part.PartDefinition.Name, "ShowOnAdminMenu", s => part.ShowOnAdminMenu = XmlConvert.ToBoolean(s)); + context.ImportAttribute(part.PartDefinition.Name, "AdminMenuText", s => part.AdminMenuText = s); + context.ImportAttribute(part.PartDefinition.Name, "AdminMenuPosition", s => part.AdminMenuPosition = s); + context.ImportAttribute(part.PartDefinition.Name, "AdminMenuImageSet", s => part.AdminMenuImageSet = s); + context.ImportAttribute(part.PartDefinition.Name, "ItemCount", s => part.ItemCount = XmlConvert.ToInt32(s)); } protected override void Exporting(ContainerPart part, ExportContentContext context) { - context.Element(part.PartDefinition.Name).SetAttributeValue("ItemContentType", part.Record.ItemContentType); - context.Element(part.PartDefinition.Name).SetAttributeValue("ItemsShown", part.Record.ItemsShown); - context.Element(part.PartDefinition.Name).SetAttributeValue("Paginated", part.Record.Paginated); - context.Element(part.PartDefinition.Name).SetAttributeValue("PageSize", part.Record.PageSize); - context.Element(part.PartDefinition.Name).SetAttributeValue("OrderByProperty", part.Record.OrderByProperty); - context.Element(part.PartDefinition.Name).SetAttributeValue("OrderByDirection", part.Record.OrderByDirection); + context.Element(part.PartDefinition.Name).SetAttributeValue("ItemContentTypes", part.Record.ItemContentTypes); + context.Element(part.PartDefinition.Name).SetAttributeValue("ItemsShown", part.ItemsShown); + context.Element(part.PartDefinition.Name).SetAttributeValue("Paginated", part.Paginated); + context.Element(part.PartDefinition.Name).SetAttributeValue("PageSize", part.PageSize); + context.Element(part.PartDefinition.Name).SetAttributeValue("ShowOnAdminMenu", part.ShowOnAdminMenu); + context.Element(part.PartDefinition.Name).SetAttributeValue("AdminMenuText", part.AdminMenuText); + context.Element(part.PartDefinition.Name).SetAttributeValue("AdminMenuPosition", part.AdminMenuPosition); + context.Element(part.PartDefinition.Name).SetAttributeValue("AdminMenuImageSet", part.AdminMenuImageSet); + context.Element(part.PartDefinition.Name).SetAttributeValue("ItemCount", part.ItemCount); } } } diff --git a/src/Orchard.Web/Core/Containers/Drivers/ContainerWidgetPartDriver.cs b/src/Orchard.Web/Core/Containers/Drivers/ContainerWidgetPartDriver.cs index e0b418fbb..f0de8de21 100644 --- a/src/Orchard.Web/Core/Containers/Drivers/ContainerWidgetPartDriver.cs +++ b/src/Orchard.Web/Core/Containers/Drivers/ContainerWidgetPartDriver.cs @@ -2,14 +2,12 @@ using System.Linq; using System.Web.Mvc; using Orchard.ContentManagement; -using Orchard.ContentManagement.Aspects; using Orchard.ContentManagement.Drivers; using Orchard.ContentManagement.Handlers; using Orchard.Core.Common.Models; using Orchard.Core.Containers.Extensions; using Orchard.Core.Containers.Models; using Orchard.Core.Containers.ViewModels; -using Orchard.Data; using Orchard.Localization; namespace Orchard.Core.Containers.Drivers { @@ -33,9 +31,6 @@ namespace Orchard.Core.Containers.Drivers { .Query(VersionOptions.Published) .Join().Where(cr => cr.Container.Id == container.Id); - var descendingOrder = part.Record.OrderByDirection == (int)OrderByDirection.Descending; - query = query.OrderBy(part.Record.OrderByProperty, descendingOrder); - if (part.Record.ApplyFilter) query = query.Where(part.Record.FilterByProperty, part.Record.FilterByOperator, part.Record.FilterByValue); @@ -91,31 +86,6 @@ namespace Orchard.Core.Containers.Drivers { part.Record.PageSize = Convert.ToInt32(pageSize); } - var orderByProperty = context.Attribute(part.PartDefinition.Name, "OrderByProperty"); - if (orderByProperty != null) { - part.Record.OrderByProperty = orderByProperty; - } - - var orderByDirection = context.Attribute(part.PartDefinition.Name, "OrderByDirection"); - if (orderByDirection != null) { - part.Record.OrderByDirection = Convert.ToInt32(orderByDirection); - } - - var applyByFilter = context.Attribute(part.PartDefinition.Name, "ApplyFilter"); - if (applyByFilter != null) { - part.Record.ApplyFilter = Convert.ToBoolean(applyByFilter); - } - - var filterByProperty = context.Attribute(part.PartDefinition.Name, "FilterByProperty"); - if (filterByProperty != null) { - part.Record.FilterByProperty = filterByProperty; - } - - var filterByOperator = context.Attribute(part.PartDefinition.Name, "FilterByOperator"); - if (filterByOperator != null) { - part.Record.FilterByOperator = filterByOperator; - } - var filterByValue = context.Attribute(part.PartDefinition.Name, "FilterByValue"); if (filterByValue != null) { part.Record.FilterByValue = filterByValue; @@ -130,26 +100,6 @@ namespace Orchard.Core.Containers.Drivers { } context.Element(part.PartDefinition.Name).SetAttributeValue("PageSize", part.Record.PageSize); - context.Element(part.PartDefinition.Name).SetAttributeValue("OrderByProperty", part.Record.OrderByProperty); - context.Element(part.PartDefinition.Name).SetAttributeValue("OrderByDirection", part.Record.OrderByDirection); - context.Element(part.PartDefinition.Name).SetAttributeValue("ApplyFilter", part.Record.ApplyFilter); - context.Element(part.PartDefinition.Name).SetAttributeValue("FilterByProperty", part.Record.FilterByProperty); - context.Element(part.PartDefinition.Name).SetAttributeValue("FilterByOperator", part.Record.FilterByOperator); - context.Element(part.PartDefinition.Name).SetAttributeValue("FilterByValue", part.Record.FilterByValue); - } - } - - public class ContainerWidgetPartHandler : ContentHandler { - public ContainerWidgetPartHandler(IRepository repository, IOrchardServices orchardServices) { - Filters.Add(StorageFilter.For(repository)); - OnInitializing((context, part) => { - part.Record.ContainerId = 0; - part.Record.PageSize = 5; - part.Record.OrderByProperty = part.Is() ? "CommonPart.CreatedUtc" : string.Empty; - part.Record.OrderByDirection = (int)OrderByDirection.Descending; - part.Record.FilterByProperty = "CustomPropertiesPart.CustomOne"; - part.Record.FilterByOperator = "="; - }); } } } diff --git a/src/Orchard.Web/Core/Containers/Drivers/CustomPropertiesDriver.cs b/src/Orchard.Web/Core/Containers/Drivers/CustomPropertiesDriver.cs index f2d9e4840..0386a0669 100644 --- a/src/Orchard.Web/Core/Containers/Drivers/CustomPropertiesDriver.cs +++ b/src/Orchard.Web/Core/Containers/Drivers/CustomPropertiesDriver.cs @@ -1,10 +1,12 @@ -using Orchard.ContentManagement; +using System; +using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; using Orchard.ContentManagement.Handlers; using Orchard.Core.Containers.Models; using Orchard.Data; namespace Orchard.Core.Containers.Drivers { + [Obsolete("Use Fields instead.")] public class CustomPropertiesPartDriver : ContentPartDriver { protected override DriverResult Editor(CustomPropertiesPart part, dynamic shapeHelper) { return Editor(part, (IUpdateModel)null, shapeHelper); @@ -45,6 +47,7 @@ namespace Orchard.Core.Containers.Drivers { } } + [Obsolete("Use Fields instead.")] public class CustomPropertiesPartHandler : ContentHandler { public CustomPropertiesPartHandler(IRepository repository) { Filters.Add(StorageFilter.For(repository)); diff --git a/src/Orchard.Web/Core/Containers/Extensions/ContentDefinitionManagerExtensions.cs b/src/Orchard.Web/Core/Containers/Extensions/ContentDefinitionManagerExtensions.cs new file mode 100644 index 000000000..ed38bf9d0 --- /dev/null +++ b/src/Orchard.Web/Core/Containers/Extensions/ContentDefinitionManagerExtensions.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Orchard.ContentManagement.MetaData; +using Orchard.ContentManagement.MetaData.Models; + +namespace Orchard.Core.Containers.Extensions { + public static class ContentDefinitionManagerExtensions { + public static IEnumerable ParseContentTypeDefinitions(this IContentDefinitionManager contentDefinitionManager, string contentTypes) { + if (contentTypes == null) + return Enumerable.Empty(); + + var typeNames = contentTypes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + var typeDefinitions = contentDefinitionManager.ListTypeDefinitions().ToDictionary(x => x.Name, x => x); + return typeNames.Select(x => x.Trim()).Where(typeDefinitions.ContainsKey).Select(x => typeDefinitions[x]); + } + + public static string JoinContentTypeDefinitions(this IContentDefinitionManager contentDefinitionManager, IEnumerable contentTypes) { + return contentTypes == null ? null : String.Join(",", contentTypes.Select(x => x.Name)); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Extensions/ContentQueryExtensions.cs b/src/Orchard.Web/Core/Containers/Extensions/ContentQueryExtensions.cs index 02582c766..9a5ef0f28 100644 --- a/src/Orchard.Web/Core/Containers/Extensions/ContentQueryExtensions.cs +++ b/src/Orchard.Web/Core/Containers/Extensions/ContentQueryExtensions.cs @@ -6,14 +6,15 @@ using Orchard.Core.Containers.Models; using Orchard.Core.Title.Models; namespace Orchard.Core.Containers.Extensions { + [Obsolete] public static class ContentQueryExtensions { public static IContentQuery OrderBy(this IContentQuery query, string partAndProperty, bool descendingOrder) where T : IContent { //todo: (heskew) order by custom part properties switch (partAndProperty) { case "ContainablePart.Weight": query = descendingOrder - ? query.OrderByDescending(record => record.Weight) - : query.OrderBy(record => record.Weight); + ? query.OrderByDescending(record => record.Position) + : query.OrderBy(record => record.Position); break; case "TitlePart.Title": query = descendingOrder diff --git a/src/Orchard.Web/Core/Containers/Handlers/ContainablePartHandler.cs b/src/Orchard.Web/Core/Containers/Handlers/ContainablePartHandler.cs index 2b8e9a0e7..142307889 100644 --- a/src/Orchard.Web/Core/Containers/Handlers/ContainablePartHandler.cs +++ b/src/Orchard.Web/Core/Containers/Handlers/ContainablePartHandler.cs @@ -1,15 +1,35 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; +using Orchard.ContentManagement; using Orchard.ContentManagement.Handlers; +using Orchard.Core.Common.Models; +using Orchard.Core.Containers.Services; using Orchard.Data; using Orchard.Core.Containers.Models; namespace Orchard.Core.Containers.Handlers { public class ContainablePartHandler : ContentHandler { - public ContainablePartHandler(IRepository repository) { + private readonly IContainerService _containerService; + + public ContainablePartHandler(IRepository repository, IContainerService containerService) { + _containerService = containerService; Filters.Add(StorageFilter.For(repository)); + + OnCreated((context, part) => UpdateItemCount(part)); + OnPublished((context, part) => UpdateItemCount(part)); + OnUnpublished((context, part) => UpdateItemCount(part)); + OnVersioned((context, part, newVersionPart) => UpdateItemCount(newVersionPart)); + OnRemoved((context, part) => UpdateItemCount(part)); + } + + private void UpdateItemCount(ContainablePart part) { + var commonPart = part.As(); + if (commonPart == null || commonPart.Container == null) + return; + + var containerPart = commonPart.Container.As(); + if (containerPart == null) + return; + + _containerService.UpdateItemCount(containerPart); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Handlers/ContainerPartHandler.cs b/src/Orchard.Web/Core/Containers/Handlers/ContainerPartHandler.cs index 34a3e3460..48b68ea37 100644 --- a/src/Orchard.Web/Core/Containers/Handlers/ContainerPartHandler.cs +++ b/src/Orchard.Web/Core/Containers/Handlers/ContainerPartHandler.cs @@ -1,19 +1,29 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Web; using Orchard.ContentManagement.Handlers; -using Orchard.Core.Containers.Models; -using Orchard.Core.Containers.Settings; -using Orchard.Core.Common.Models; -using Orchard.Data; +using Orchard.ContentManagement.MetaData; using Orchard.Core.Containers.Extensions; -using Orchard.ContentManagement; +using Orchard.Core.Containers.Models; +using Orchard.Core.Containers.Services; +using Orchard.Core.Containers.Settings; +using Orchard.Data; using System.Web.Routing; namespace Orchard.Core.Containers.Handlers { public class ContainerPartHandler : ContentHandler { - public ContainerPartHandler(IRepository repository) { + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IListViewService _listViewService; + private readonly IContainerService _containerService; + + public ContainerPartHandler( + IRepository repository, + IContentDefinitionManager contentDefinitionManager, + IListViewService listViewService, + IContainerService containerService) { + + _contentDefinitionManager = contentDefinitionManager; + _listViewService = listViewService; + _containerService = containerService; Filters.Add(StorageFilter.For(repository)); OnInitializing((context, part) => { part.Record.ItemsShown = true; @@ -22,17 +32,44 @@ namespace Orchard.Core.Containers.Handlers { part.Record.Paginated = part.Settings.GetModel().PaginatedDefault ?? part.PartDefinition.Settings.GetModel().PaginatedDefault; - // hard-coded defaults for ordering - part.Record.OrderByProperty = part.Is() ? "CommonPart.CreatedUtc" : string.Empty; - part.Record.OrderByDirection = (int)OrderByDirection.Descending; }); + OnGetContentItemMetadata((context, part) => { - context.Metadata.DisplayRouteValues = new RouteValueDictionary { - {"Area", "Containers"}, - {"Controller", "Item"}, - {"Action", "Display"}, - {"id", context.ContentItem.Id} - };}); + context.Metadata.DisplayRouteValues = new RouteValueDictionary { + {"Area", "Containers"}, + {"Controller", "Item"}, + {"Action", "Display"}, + {"id", context.ContentItem.Id} + }; + }); + + OnActivated((context, part) => { + part.ContainerSettingsField.Loader(() => part.Settings.GetModel()); + + part.ItemContentTypesField.Loader(() => { + var settings = part.ContainerSettings; + var types = settings.RestrictItemContentTypes ? settings.RestrictedItemContentTypes : part.Record.ItemContentTypes; + return _contentDefinitionManager.ParseContentTypeDefinitions(types); + }); + + part.ItemContentTypesField.Setter(value => { + part.Record.ItemContentTypes = _contentDefinitionManager.JoinContentTypeDefinitions(value); + return value; + }); + + part.AdminListViewField.Loader(() => { + var providers = _listViewService.Providers.ToList(); + var listViewProviderName = !String.IsNullOrWhiteSpace(part.Record.AdminListViewName) + ? part.Record.AdminListViewName + : !String.IsNullOrWhiteSpace(part.ContainerSettings.AdminListViewName) + ? part.ContainerSettings.AdminListViewName + : providers.First().Name; + + return _listViewService.GetProvider(listViewProviderName) ?? _listViewService.GetDefaultProvider(); + }); + }); + + OnPublished((context, part) => _containerService.UpdateItemCount(part)); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Handlers/ContainerWidgetPartHandler.cs b/src/Orchard.Web/Core/Containers/Handlers/ContainerWidgetPartHandler.cs new file mode 100644 index 000000000..eedd6dfdf --- /dev/null +++ b/src/Orchard.Web/Core/Containers/Handlers/ContainerWidgetPartHandler.cs @@ -0,0 +1,15 @@ +using Orchard.ContentManagement.Handlers; +using Orchard.Core.Containers.Models; +using Orchard.Data; + +namespace Orchard.Core.Containers.Handlers { + public class ContainerWidgetPartHandler : ContentHandler { + public ContainerWidgetPartHandler(IRepository repository) { + Filters.Add(StorageFilter.For(repository)); + OnInitializing((context, part) => { + part.Record.ContainerId = 0; + part.Record.PageSize = 5; + }); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Migrations.cs b/src/Orchard.Web/Core/Containers/Migrations.cs index 198f8c565..b3d630693 100644 --- a/src/Orchard.Web/Core/Containers/Migrations.cs +++ b/src/Orchard.Web/Core/Containers/Migrations.cs @@ -5,60 +5,44 @@ using Orchard.Data.Migration; namespace Orchard.Core.Containers { public class Migrations : DataMigrationImpl { public int Create() { - SchemaBuilder.CreateTable("ContainerPartRecord", - table => table - .ContentPartRecord() - .Column("Paginated") - .Column("PageSize") - .Column("OrderByProperty") - .Column("OrderByDirection") - .Column("ItemContentType") - .Column("ItemsShown", column => column.WithDefault(true))); + SchemaBuilder.CreateTable("ContainerPartRecord", table => table + .ContentPartRecord() + .Column("Paginated") + .Column("PageSize") + .Column("ItemContentTypes") + .Column("ItemsShown", c => c.NotNull()) + .Column("ShowOnAdminMenu", c => c.NotNull()) + .Column("AdminMenuText", c => c.WithLength(50)) + .Column("AdminMenuPosition", c => c.WithLength(50)) + .Column("AdminMenuImageSet", c => c.WithLength(50)) + .Column("EnablePositioning") + .Column("AdminListViewName", c => c.WithLength(50)) + .Column("ItemCount", c => c.NotNull())); - SchemaBuilder.CreateTable("ContainerWidgetPartRecord", - table => table - .ContentPartRecord() - .Column("ContainerId") - .Column("PageSize") - .Column("OrderByProperty") - .Column("OrderByDirection") - .Column("ApplyFilter") - .Column("FilterByProperty") - .Column("FilterByOperator") - .Column("FilterByValue")); + SchemaBuilder.CreateTable("ContainerWidgetPartRecord", table => table + .ContentPartRecord() + .Column("ContainerId") + .Column("PageSize")); - SchemaBuilder.CreateTable("CustomPropertiesPartRecord", - table => table - .ContentPartRecord() - .Column("CustomOne") - .Column("CustomTwo") - .Column("CustomThree")); + SchemaBuilder.CreateTable("ContainablePartRecord", table => table + .ContentPartRecord() + .Column("Position")); - SchemaBuilder.CreateTable("ContainablePartRecord", - table => table - .ContentPartRecord() - .Column("Weight")); + ContentDefinitionManager.AlterTypeDefinition("ContainerWidget", type => type + .WithPart("CommonPart") + .WithPart("WidgetPart") + .WithPart("ContainerWidgetPart") + .WithSetting("Stereotype", "Widget")); - ContentDefinitionManager.AlterTypeDefinition("ContainerWidget", - cfg => cfg - .WithPart("CommonPart") - .WithPart("WidgetPart") - .WithPart("ContainerWidgetPart") - .WithSetting("Stereotype", "Widget")); - - ContentDefinitionManager.AlterPartDefinition("ContainerPart", builder => builder + ContentDefinitionManager.AlterPartDefinition("ContainerPart", part => part .Attachable() .WithDescription("Turns your content item into a container that is capable of containing content items that have the ContainablePart attached.")); - ContentDefinitionManager.AlterPartDefinition("ContainablePart", builder => builder + ContentDefinitionManager.AlterPartDefinition("ContainablePart", part => part .Attachable() - .WithDescription("Allows your content item to be contained by a content item that has the ContainerPart attached")); + .WithDescription("Allows your content item to be contained by a content item that has the ContainerPart attached.")); - ContentDefinitionManager.AlterPartDefinition("CustomPropertiesPart", builder => builder - .Attachable() - .WithDescription("Adds 3 custom properties to your content item.")); - - return 4; + return 5; } public int UpdateFrom1() { @@ -67,27 +51,59 @@ namespace Orchard.Core.Containers { } public int UpdateFrom2() { - SchemaBuilder.AlterTable("ContainerPartRecord", - table => table.AddColumn("ItemsShown", column => column.WithDefault(true))); + SchemaBuilder.AlterTable("ContainerPartRecord", table => table + .AddColumn("ItemsShown", column => column.WithDefault(true))); - SchemaBuilder.CreateTable("ContainablePartRecord", - table => table - .ContentPartRecord() - .Column("Weight")); + SchemaBuilder.CreateTable("ContainablePartRecord", table => table + .ContentPartRecord() + .Column("Weight")); return 3; } public int UpdateFrom3() { - ContentDefinitionManager.AlterPartDefinition("ContainerPart", builder => builder + ContentDefinitionManager.AlterPartDefinition("ContainerPart", part => part .WithDescription("Turns your content item into a container that is capable of containing content items that have the ContainablePart attached.")); - ContentDefinitionManager.AlterPartDefinition("ContainablePart", builder => builder - .WithDescription("Allows your content item to be contained by a content item that has the ContainerPart attached")); + ContentDefinitionManager.AlterPartDefinition("ContainablePart", part => part + .WithDescription("Allows your content item to be contained by a content item that has the ContainerPart attached.")); - ContentDefinitionManager.AlterPartDefinition("CustomPropertiesPart", builder => builder + ContentDefinitionManager.AlterPartDefinition("CustomPropertiesPart", part => part .WithDescription("Adds 3 custom properties to your content item.")); return 4; } + + public int UpdateFrom4() { + ContentDefinitionManager.DeleteTypeDefinition("CustomPropertiesPart"); + SchemaBuilder.DropTable("CustomPropertiesPartRecord"); + SchemaBuilder.AlterTable("ContainerPartRecord", table => { + table.DropColumn("OrderByProperty"); + table.DropColumn("OrderByDirection"); + table.DropColumn("ItemContentType"); + table.AddColumn("ItemContentTypes"); + table.AddColumn("ShowOnAdminMenu", c => c.NotNull()); + table.AddColumn("AdminMenuText", c => c.WithLength(50)); + table.AddColumn("AdminMenuPosition", c => c.WithLength(50)); + table.AddColumn("AdminMenuImageSet", c => c.WithLength(50)); + table.AddColumn("EnablePositioning"); + table.AddColumn("AdminListViewName", c => c.WithLength(50)); + table.AddColumn("ItemCount", c => c.NotNull()); + }); + + SchemaBuilder.AlterTable("ContainablePartRecord", table => { + table.DropColumn("Weight"); + table.AddColumn("Position"); + }); + + SchemaBuilder.AlterTable("ContainerWidgetPartRecord", table => { + table.DropColumn("OrderByProperty"); + table.DropColumn("OrderByDirection"); + table.DropColumn("ApplyFilter"); + table.DropColumn("FilterByProperty"); + table.DropColumn("FilterByOperator"); + table.DropColumn("FilterByValue"); + }); + return 5; + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Models/ContainablePart.cs b/src/Orchard.Web/Core/Containers/Models/ContainablePart.cs index 7b83322ec..35af2dfde 100644 --- a/src/Orchard.Web/Core/Containers/Models/ContainablePart.cs +++ b/src/Orchard.Web/Core/Containers/Models/ContainablePart.cs @@ -3,13 +3,13 @@ using Orchard.ContentManagement.Records; namespace Orchard.Core.Containers.Models { public class ContainablePart : ContentPart { - public int Weight { - get { return Record.Weight; } - set { Record.Weight = value; } + public int Position { + get { return Record.Position; } + set { Record.Position = value; } } } public class ContainablePartRecord : ContentPartRecord { - public virtual int Weight { get; set; } + public virtual int Position { get; set; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Models/ContainerPart.cs b/src/Orchard.Web/Core/Containers/Models/ContainerPart.cs index 6e7550a73..b0230123a 100644 --- a/src/Orchard.Web/Core/Containers/Models/ContainerPart.cs +++ b/src/Orchard.Web/Core/Containers/Models/ContainerPart.cs @@ -1,12 +1,35 @@ -using Orchard.ContentManagement; +using System.Collections.Generic; +using Orchard.ContentManagement; +using Orchard.ContentManagement.MetaData.Models; using Orchard.ContentManagement.Records; +using Orchard.Core.Common.Utilities; +using Orchard.Core.Containers.Services; +using Orchard.Core.Containers.Settings; using Orchard.UI.Navigation; namespace Orchard.Core.Containers.Models { public class ContainerPart : ContentPart { - public string ItemContentType { - get { return Record.ItemContentType; } - set { Record.ItemContentType = value; } + // ReSharper disable InconsistentNaming + internal LazyField> ItemContentTypesField = new LazyField>(); + internal LazyField ContainerSettingsField = new LazyField(); + internal LazyField AdminListViewField = new LazyField(); + // ReSharper restore InconsistentNaming + + public IEnumerable ItemContentTypes { + get { return ItemContentTypesField.Value; } + set { ItemContentTypesField.Value = value; } + } + + public ContainerTypePartSettings ContainerSettings { + get { return ContainerSettingsField.Value; } + } + + public bool EnablePositioning { + get { return ContainerSettings.EnablePositioning != null ? ContainerSettings.EnablePositioning.Value : Record.EnablePositioning; } + } + + public IListViewProvider AdminListView { + get { return AdminListViewField.Value; } } public bool ItemsShown { @@ -24,14 +47,29 @@ namespace Orchard.Core.Containers.Models { set { Record.PageSize = value; } } - public string OrderByProperty { - get { return Record.OrderByProperty; } - set { Record.OrderByProperty = value; } + public bool ShowOnAdminMenu { + get { return Record.ShowOnAdminMenu; } + set { Record.ShowOnAdminMenu = value; } } - public int OrderByDirection { - get { return Record.OrderByDirection; } - set { Record.OrderByDirection = value; } + public string AdminMenuText { + get { return Record.AdminMenuText; } + set { Record.AdminMenuText = value; } + } + + public string AdminMenuPosition { + get { return Record.AdminMenuPosition; } + set { Record.AdminMenuPosition = value; } + } + + public string AdminMenuImageSet { + get { return Record.AdminMenuImageSet; } + set { Record.AdminMenuImageSet = value; } + } + + public int ItemCount { + get { return Record.ItemCount; } + set { Record.ItemCount = value; } } public PagerParameters PagerParameters { get; set; } @@ -42,11 +80,16 @@ namespace Orchard.Core.Containers.Models { } public class ContainerPartRecord : ContentPartRecord { - public virtual string ItemContentType { get; set; } + public virtual string ItemContentTypes { get; set; } public virtual bool ItemsShown { get; set; } public virtual bool Paginated { get; set; } public virtual int PageSize { get; set; } - public virtual string OrderByProperty { get; set; } - public virtual int OrderByDirection { get; set; } + public virtual bool ShowOnAdminMenu { get; set; } + public virtual string AdminMenuText { get; set; } + public virtual string AdminMenuPosition { get; set; } + public virtual string AdminMenuImageSet { get; set; } + public virtual bool EnablePositioning { get; set; } + public virtual string AdminListViewName { get; set; } + public virtual int ItemCount { get; set; } } } diff --git a/src/Orchard.Web/Core/Containers/Models/CustomPropertiesPart.cs b/src/Orchard.Web/Core/Containers/Models/CustomPropertiesPart.cs index 51afdcac3..797268493 100644 --- a/src/Orchard.Web/Core/Containers/Models/CustomPropertiesPart.cs +++ b/src/Orchard.Web/Core/Containers/Models/CustomPropertiesPart.cs @@ -1,10 +1,13 @@ -using Orchard.ContentManagement; +using System; +using Orchard.ContentManagement; using Orchard.ContentManagement.Records; namespace Orchard.Core.Containers.Models { + [Obsolete("Use content fields instead.")] public class CustomPropertiesPart : ContentPart { } + [Obsolete("Use content fields instead.")] public class CustomPropertiesPartRecord : ContentPartRecord { public virtual string CustomOne { get; set; } public virtual string CustomTwo { get; set; } diff --git a/src/Orchard.Web/Core/Containers/Models/OrderByDirection.cs b/src/Orchard.Web/Core/Containers/Models/OrderByDirection.cs deleted file mode 100644 index 44b7bdb42..000000000 --- a/src/Orchard.Web/Core/Containers/Models/OrderByDirection.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Orchard.Core.Containers.Models { - public enum OrderByDirection { - Ascending, - Descending, - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Placement.info b/src/Orchard.Web/Core/Containers/Placement.info index f25b8d22d..70df62957 100644 --- a/src/Orchard.Web/Core/Containers/Placement.info +++ b/src/Orchard.Web/Core/Containers/Placement.info @@ -7,7 +7,7 @@ - + diff --git a/src/Orchard.Web/Core/Containers/Services/ContainerService.cs b/src/Orchard.Web/Core/Containers/Services/ContainerService.cs new file mode 100644 index 000000000..d63738d4b --- /dev/null +++ b/src/Orchard.Web/Core/Containers/Services/ContainerService.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Orchard.ContentManagement; +using Orchard.ContentManagement.MetaData; +using Orchard.ContentManagement.MetaData.Models; +using Orchard.Core.Common.Models; +using Orchard.Core.Containers.Models; +using Orchard.Core.Containers.ViewModels; + +namespace Orchard.Core.Containers.Services { + public interface IContainerService : IDependency { + IEnumerable GetContainableTypes(); + IEnumerable GetContainerTypes(); + IEnumerable GetContainers(VersionOptions options = null); + IContentQuery GetContainersQuery(VersionOptions options = null); + ContainerPart Get(int id, VersionOptions options = null); + IEnumerable GetContentItems(int containerId, VersionOptions options = null); + int CountContentItems(int containerId, VersionOptions options = null); + ContentItem Next(int containerId, ContainablePart current); + ContentItem Previous(int containerId, ContainablePart current); + ContainerPart GetContainer(IContent content, VersionOptions options = null); + + /// + /// Returns the position of the last item. + /// + int GetLastPosition(int containerId); + + /// + /// Returns the position of the first item. + /// + int GetFirstPosition(int containerId); + + /// + /// Updates the path to an item. Use this method when its container changes. + /// + void UpdateItemPath(ContentItem item); + + void Reverse(IEnumerable items); + void Shuffle(IEnumerable items); + void Sort(IEnumerable items, SortBy sortBy, SortDirection sortDirection); + + void MoveItem(ContainablePart item, ContainerPart targetContainer, int? position = null); + void UpdateItemCount(ContainerPart container); + IContentQuery GetContentItemsQuery(int containerId, IEnumerable ids = null, VersionOptions options = null); + IContentQuery GetOrderedContentItemsQuery(int containerId, IEnumerable ids = null, VersionOptions options = null); + } + + public class ContainerService : IContainerService { + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentManager _contentManager; + private readonly IRandomizer _randomizer; + + public ContainerService(IContentDefinitionManager contentDefinitionManager, IContentManager contentManager, IRandomizer randomizer) { + _contentDefinitionManager = contentDefinitionManager; + _contentManager = contentManager; + _randomizer = randomizer; + } + + public IEnumerable GetContainableTypes() { + return _contentDefinitionManager.ListTypeDefinitions().Where(td => td.Parts.Any(p => p.PartDefinition.Name == typeof(ContainablePart).Name)).OrderBy(x => x.DisplayName); + } + + public IEnumerable GetContainerTypes() { + return _contentDefinitionManager.ListTypeDefinitions().Where(td => td.Parts.Any(p => p.PartDefinition.Name == typeof(ContainerPart).Name)).OrderBy(x => x.DisplayName); + } + + public IEnumerable GetContainers(VersionOptions options = null) { + return GetContainersQuery(options).List(); + } + + public IContentQuery GetContainersQuery(VersionOptions options = null) { + options = options ?? VersionOptions.Published; + return _contentManager.Query(options); + } + + public ContainerPart Get(int id, VersionOptions options = null) { + options = options ?? VersionOptions.Published; + return _contentManager.Get(id, options); + } + + public IEnumerable GetContentItems(int containerId, VersionOptions options = null) { + options = options ?? VersionOptions.Published; + return GetOrderedContentItemsQuery(containerId, null, options).List().Select(x => x.ContentItem); + } + + public int CountContentItems(int containerId, VersionOptions options = null) { + options = options ?? VersionOptions.Published; + return GetOrderedContentItemsQuery(containerId, options: options).Count(); + } + + public ContentItem Next(int containerId, ContainablePart current) { + if (current == null) + return null; + return GetOrderedContentItemsQuery(containerId, null, VersionOptions.Latest).Where(x => x.Position < current.Position).Slice(0, 1).Select(x => x.ContentItem).FirstOrDefault(); + } + + public ContentItem Previous(int containerId, ContainablePart current) { + if (current == null) + return null; + return GetContentItemsQuery(containerId, null, VersionOptions.Latest).OrderBy(x => x.Position).Where(x => x.Position > current.Position).Slice(0, 1).Select(x => x.ContentItem).FirstOrDefault(); + } + + public ContainerPart GetContainer(IContent content, VersionOptions options = null) { + options = options ?? VersionOptions.Published; + if (content == null) + return null; + + var commonPart = content.As(); + if (commonPart == null) + return null; + + var container = commonPart.Record.Container; + return container == null ? null : _contentManager.Get(container.Id, options); + } + + public int GetLastPosition(int containerId) { + var last = GetContentItemsQuery(containerId, null, VersionOptions.Latest).OrderBy(x => x.Position).Slice(0, 1).FirstOrDefault(); + return last != null ? last.As().Position : 0; + } + + public int GetFirstPosition(int containerId) { + var first = GetContentItemsQuery(containerId, null, VersionOptions.Latest).OrderByDescending(x => x.Position).Slice(0, 1).FirstOrDefault(); + return first != null ? first.As().Position : 0; + } + + public IContentQuery GetContentItemsQuery(int containerId, IEnumerable ids = null, VersionOptions options = null) { + options = options ?? VersionOptions.Published; + var query = _contentManager.Query(options); + query = ids == null ? query.Where(x => x.Container.Id == containerId) : query.Where(x => (x.Container.Id == containerId || x.Container == null) && ids.Contains(x.Id)); + + return query.Join(); + } + + public IContentQuery GetOrderedContentItemsQuery(int containerId, IEnumerable ids = null, VersionOptions options = null) { + return GetContentItemsQuery(containerId, ids, options).OrderByDescending(x => x.Position); + } + + public void Reverse(IEnumerable items) { + var list = UpdatePositions(items).ToArray(); + Array.Reverse(list); + UpdatePositions(list); + } + + public void Shuffle(IEnumerable items) { + var list = UpdatePositions(items).ToArray(); + Shuffle(list); + UpdatePositions(list); + } + + public void Sort(IEnumerable items, SortBy sortBy, SortDirection sortDirection) { + var list = items.ToArray(); + switch (sortBy) { + case SortBy.Created: + Sort(list, sortDirection, (a, b) => a.As().CreatedUtc.GetValueOrDefault().CompareTo(b.As().CreatedUtc.GetValueOrDefault())); + break; + case SortBy.Modified: + Sort(list, sortDirection, (a, b) => a.As().ModifiedUtc.GetValueOrDefault().CompareTo(b.As().ModifiedUtc.GetValueOrDefault())); + break; + case SortBy.Published: + Sort(list, sortDirection, (a, b) => a.As().PublishedUtc.GetValueOrDefault().CompareTo(b.As().PublishedUtc.GetValueOrDefault())); + break; + case SortBy.DisplayText: + Sort(list, sortDirection, (a, b) => String.CompareOrdinal(_contentManager.GetItemMetadata(a).DisplayText, _contentManager.GetItemMetadata(b).DisplayText)); + break; + } + UpdatePositions(list); + } + + public void MoveItem(ContainablePart item, ContainerPart targetContainer, int? position = null) { + var commonPart = item.As(); + + if(commonPart == null) + throw new ArgumentException("Cannot move content that have no CommonPart", "item"); + + var previousContainer = commonPart.Container != null ? commonPart.Container.As() : default(ContainerPart); + commonPart.Container = targetContainer; + + if (previousContainer != null && previousContainer.Id != targetContainer.Id) + UpdateItemCount(previousContainer); + + if (position != null) + item.Position = position.Value; + + UpdateItemCount(targetContainer); + UpdateItemPath(item.ContentItem); + } + + public void UpdateItemCount(ContainerPart container) { + if(container == null) throw new ArgumentNullException("container"); + container.ItemCount = CountContentItems(container.Id, VersionOptions.Published); + } + + /// + /// Updates the path to an item. Use this method when its container changes. + /// + public void UpdateItemPath(ContentItem item) { + // Fixes an Item's path when its container changes. + // Force a publish/unpublish event so AutoroutePart fixes the content items path and the paths of any child objects if it is also a container. + if (item.VersionRecord.Published) { + item.VersionRecord.Published = false; + _contentManager.Publish(item); + } + else { + item.VersionRecord.Published = true; + _contentManager.Unpublish(item); + } + } + + private void Sort(TValue[] array, SortDirection direction, Func sort) { + Array.Sort(array, Sort(direction, sort)); + } + + private Comparison Sort(SortDirection direction, Func sort) { + return (a, b) => direction == SortDirection.Ascending ? sort(a, b) : sort(b, a); + } + + private IList UpdatePositions(IEnumerable items) { + var list = items.ToList(); + var index = list.Count; + foreach (var item in list) { + item.Position = --index; + } + return list; + } + + private void Shuffle(IList source) { + var n = source.Count; + for (var i = 0; i < n; i++) { + var r = i + _randomizer.Next(0, n - i); + var temp = source[i]; + source[i] = source[r]; + source[r] = temp; + } + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Services/ListViewProvider.cs b/src/Orchard.Web/Core/Containers/Services/ListViewProvider.cs new file mode 100644 index 000000000..a7d5cb3bc --- /dev/null +++ b/src/Orchard.Web/Core/Containers/Services/ListViewProvider.cs @@ -0,0 +1,17 @@ +using Orchard.Utility.Extensions; + +namespace Orchard.Core.Containers.Services { + public interface IListViewProvider : IDependency { + string Name { get; } + string DisplayName { get; } + int Priority { get; } + dynamic BuildDisplay(BuildListViewDisplayContext context); + } + + public abstract class ListViewProviderBase : IListViewProvider { + public virtual string Name { get { return GetType().Name.Replace("ListView", ""); } } + public virtual string DisplayName { get { return Name.CamelFriendly(); } } + public virtual int Priority { get { return 0; } } + public abstract dynamic BuildDisplay(BuildListViewDisplayContext context); + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Services/ListViewService.cs b/src/Orchard.Web/Core/Containers/Services/ListViewService.cs new file mode 100644 index 000000000..b7e9469b3 --- /dev/null +++ b/src/Orchard.Web/Core/Containers/Services/ListViewService.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Linq; +using Orchard.ContentManagement; +using Orchard.Core.Containers.Models; +using Orchard.UI.Navigation; + +namespace Orchard.Core.Containers.Services { + public interface IListViewService : IDependency { + IEnumerable Providers { get; } + IListViewProvider GetProvider(string name); + IListViewProvider GetDefaultProvider(); + } + + public class ListViewService : IListViewService { + private readonly IEnumerable _providers; + + public ListViewService(IEnumerable providers) { + _providers = providers.OrderBy(x => x.Priority); + } + + public IEnumerable Providers { + get { return _providers; } + } + + public IListViewProvider GetProvider(string name) { + return Providers.FirstOrDefault(x => x.Name == name); + } + + public IListViewProvider GetDefaultProvider() { + return Providers.First(); + } + } + + public class BuildListViewDisplayContext { + public dynamic New { get; set; } + public IContentQuery ContentQuery { get; set; } + public Pager Pager { get; set; } + public ContainerPart Container { get; set; } + public string ContainerDisplayName { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Services/Randomizer.cs b/src/Orchard.Web/Core/Containers/Services/Randomizer.cs new file mode 100644 index 000000000..e7ff3ef1a --- /dev/null +++ b/src/Orchard.Web/Core/Containers/Services/Randomizer.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading; + +namespace Orchard.Core.Containers.Services { + public interface IRandomizer : ISingletonDependency { + int Next(int minValue, int maxValue); + int Next(int maxValue); + } + + public class Randomizer : IRandomizer { + private readonly Lazy _randomField = new Lazy(() => new Random(Guid.NewGuid().GetHashCode()), LazyThreadSafetyMode.ExecutionAndPublication); + private Random Random { get { return _randomField.Value; } } + + public int Next(int minValue, int maxValue) { + return Random.Next(minValue, maxValue); + } + + public int Next(int maxValue) { + return Random.Next(maxValue); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Settings/ContainableSettings.cs b/src/Orchard.Web/Core/Containers/Settings/ContainableSettings.cs new file mode 100644 index 000000000..fabf93b2c --- /dev/null +++ b/src/Orchard.Web/Core/Containers/Settings/ContainableSettings.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using Orchard.ContentManagement; +using Orchard.ContentManagement.MetaData; +using Orchard.ContentManagement.MetaData.Builders; +using Orchard.ContentManagement.MetaData.Models; +using Orchard.ContentManagement.ViewModels; + +namespace Orchard.Core.Containers.Settings { + public class ContainableTypePartSettings { + public bool ShowContainerPicker { get; set; } + public bool ShowPositionEditor { get; set; } + } + + public class ContainableTypePartSettingsHooks : ContentDefinitionEditorEventsBase { + public override IEnumerable TypePartEditor(ContentTypePartDefinition definition) { + if (definition.PartDefinition.Name != "ContainablePart") + yield break; + + var model = definition.Settings.GetModel(); + yield return DefinitionTemplate(model); + } + + public override IEnumerable TypePartEditorUpdate(ContentTypePartDefinitionBuilder builder, IUpdateModel updateModel) { + if (builder.Name != "ContainerPart") + yield break; + + var model = new ContainableTypePartSettings(); + updateModel.TryUpdateModel(model, "ContainableTypePartSettings", null, null); + builder.WithSetting("ContainableTypePartSettings.ShowContainerPicker", model.ShowContainerPicker.ToString()); + builder.WithSetting("ContainableTypePartSettings.ShowPositionEditor", model.ShowPositionEditor.ToString()); + yield return DefinitionTemplate(model); + } + } +} diff --git a/src/Orchard.Web/Core/Containers/Settings/ContainerSettings.cs b/src/Orchard.Web/Core/Containers/Settings/ContainerSettings.cs index c58f329e0..5ed8f1e47 100644 --- a/src/Orchard.Web/Core/Containers/Settings/ContainerSettings.cs +++ b/src/Orchard.Web/Core/Containers/Settings/ContainerSettings.cs @@ -1,9 +1,13 @@ using System.Collections.Generic; +using System.Linq; using Orchard.ContentManagement; using Orchard.ContentManagement.MetaData; using Orchard.ContentManagement.MetaData.Builders; using Orchard.ContentManagement.MetaData.Models; using Orchard.ContentManagement.ViewModels; +using Orchard.Core.Containers.Extensions; +using Orchard.Core.Containers.Services; +using Orchard.Core.Containers.ViewModels; namespace Orchard.Core.Containers.Settings { public class ContainerPartSettings { @@ -35,9 +39,23 @@ namespace Orchard.Core.Containers.Settings { public class ContainerTypePartSettings { public int? PageSizeDefault { get; set; } public bool? PaginatedDefault { get; set; } + public string RestrictedItemContentTypes { get; set; } + public bool RestrictItemContentTypes { get; set; } + public bool? EnablePositioning { get; set; } + public string AdminListViewName { get; set; } } public class ContainerSettingsHooks : ContentDefinitionEditorEventsBase { + private readonly IContainerService _containerService; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IListViewService _listViewService; + + public ContainerSettingsHooks(IContainerService containerService, IContentDefinitionManager contentDefinitionManager, IListViewService listViewService) { + _containerService = containerService; + _contentDefinitionManager = contentDefinitionManager; + _listViewService = listViewService; + } + public override IEnumerable TypePartEditor(ContentTypePartDefinition definition) { if (definition.PartDefinition.Name != "ContainerPart") yield break; @@ -51,7 +69,18 @@ namespace Orchard.Core.Containers.Settings { if (model.PaginatedDefault == null) model.PaginatedDefault = partModel.PaginatedDefault; - yield return DefinitionTemplate(model); + var viewModel = new ContainerTypePartSettingsViewModel { + PageSizeDefault = model.PageSizeDefault, + PaginatedDefault = model.PaginatedDefault, + RestrictedItemContentTypes = _contentDefinitionManager.ParseContentTypeDefinitions(model.RestrictedItemContentTypes).Select(x => x.Name).ToList(), + RestrictItemContentTypes = model.RestrictItemContentTypes, + EnablePositioning = model.EnablePositioning, + AdminListViewName = model.AdminListViewName, + AvailableItemContentTypes = _containerService.GetContainableTypes().ToList(), + ListViewProviders = _listViewService.Providers.ToList() + }; + + yield return DefinitionTemplate(viewModel); } public override IEnumerable PartEditor(ContentPartDefinition definition) { @@ -66,11 +95,17 @@ namespace Orchard.Core.Containers.Settings { if (builder.Name != "ContainerPart") yield break; - var model = new ContainerTypePartSettings(); - updateModel.TryUpdateModel(model, "ContainerTypePartSettings", null, null); - builder.WithSetting("ContainerTypePartSettings.PageSizeDefault", model.PageSizeDefault.ToString()); - builder.WithSetting("ContainerTypePartSettings.PaginatedDefault", model.PaginatedDefault.ToString()); - yield return DefinitionTemplate(model); + var viewModel = new ContainerTypePartSettingsViewModel { + AvailableItemContentTypes = _containerService.GetContainableTypes().ToList() + }; + updateModel.TryUpdateModel(viewModel, "ContainerTypePartSettingsViewModel", null, new[] { "AvailableItemContentTypes" }); + builder.WithSetting("ContainerTypePartSettings.PageSizeDefault", viewModel.PageSizeDefault.ToString()); + builder.WithSetting("ContainerTypePartSettings.PaginatedDefault", viewModel.PaginatedDefault.ToString()); + builder.WithSetting("ContainerTypePartSettings.RestrictedItemContentTypes", viewModel.RestrictedItemContentTypes != null ? string.Join(",", viewModel.RestrictedItemContentTypes) : ""); + builder.WithSetting("ContainerTypePartSettings.RestrictItemContentTypes", viewModel.RestrictItemContentTypes.ToString()); + builder.WithSetting("ContainerTypePartSettings.EnablePositioning", viewModel.EnablePositioning.ToString()); + builder.WithSetting("ContainerTypePartSettings.AdminListViewName", viewModel.AdminListViewName); + yield return DefinitionTemplate(viewModel); } public override IEnumerable PartEditorUpdate(ContentPartDefinitionBuilder builder, IUpdateModel updateModel) { diff --git a/src/Orchard.Web/Core/Containers/Settings/LocationSettings.cs b/src/Orchard.Web/Core/Containers/Settings/LocationSettings.cs deleted file mode 100644 index 7e595c13a..000000000 --- a/src/Orchard.Web/Core/Containers/Settings/LocationSettings.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Orchard.Core.Common.Settings { - public class LocationSettings { - public string Zone { get; set; } - public string Position { get; set; } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/ViewModels/ContainableViewModel.cs b/src/Orchard.Web/Core/Containers/ViewModels/ContainableViewModel.cs index d34c3d4ff..3d63b896f 100644 --- a/src/Orchard.Web/Core/Containers/ViewModels/ContainableViewModel.cs +++ b/src/Orchard.Web/Core/Containers/ViewModels/ContainableViewModel.cs @@ -4,6 +4,8 @@ namespace Orchard.Core.Containers.ViewModels { public class ContainableViewModel { public int ContainerId { get; set; } public SelectList AvailableContainers { get; set; } - public int Weight { get; set; } + public int Position { get; set; } + public bool ShowContainerPicker { get; set; } + public bool ShowPositionEditor { get; set; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/ViewModels/ContainerTypePartSettingsViewModel.cs b/src/Orchard.Web/Core/Containers/ViewModels/ContainerTypePartSettingsViewModel.cs new file mode 100644 index 000000000..04f930c7b --- /dev/null +++ b/src/Orchard.Web/Core/Containers/ViewModels/ContainerTypePartSettingsViewModel.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Orchard.ContentManagement.MetaData.Models; +using Orchard.Core.Containers.Services; + +namespace Orchard.Core.Containers.ViewModels { + public class ContainerTypePartSettingsViewModel { + public int? PageSizeDefault { get; set; } + public bool? PaginatedDefault { get; set; } + public bool RestrictItemContentTypes { get; set; } + public IList RestrictedItemContentTypes { get; set; } + public IList AvailableItemContentTypes { get; set; } + public IList ListViewProviders { get; set; } + public bool? EnablePositioning { get; set; } + + [UIHint("ListViewPicker")] + public string AdminListViewName { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/ViewModels/ContainerViewModel.cs b/src/Orchard.Web/Core/Containers/ViewModels/ContainerViewModel.cs index a5dcb7a6f..65006a4eb 100644 --- a/src/Orchard.Web/Core/Containers/ViewModels/ContainerViewModel.cs +++ b/src/Orchard.Web/Core/Containers/ViewModels/ContainerViewModel.cs @@ -1,9 +1,33 @@ -using System.Web.Mvc; -using Orchard.Core.Containers.Models; +using System.Collections.Generic; +using Orchard.ContentManagement.MetaData.Models; namespace Orchard.Core.Containers.ViewModels { public class ContainerViewModel { - public ContainerPart Part { get; set; } - public SelectList AvailableContainables { get; set; } + public IList SelectedItemContentTypes { get; set; } + public bool ItemsShown { get; set; } + public bool Paginated { get; set; } + public int PageSize { get; set; } + public bool ShowOnAdminMenu { get; set; } + public string AdminMenuText { get; set; } + public string AdminMenuPosition { get; set; } + public string AdminMenuImageSet { get; set; } + public IList AvailableItemContentTypes { get; set; } + public bool RestrictItemContentTypes { get; set; } + public bool EnablePositioning { get; set; } + public bool OverrideEnablePositioning { get; set; } + } + + public enum SortBy { + Modified, + Published, + Created, + DisplayText + } + + public enum SortDirection { + Ascending, + Descending, + Created, + DisplayText } } \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Views/DefinitionTemplates/ContainableTypePartSettings.cshtml b/src/Orchard.Web/Core/Containers/Views/DefinitionTemplates/ContainableTypePartSettings.cshtml new file mode 100644 index 000000000..4465b90df --- /dev/null +++ b/src/Orchard.Web/Core/Containers/Views/DefinitionTemplates/ContainableTypePartSettings.cshtml @@ -0,0 +1,11 @@ +@model Orchard.Core.Containers.Settings.ContainableTypePartSettings +
+ @Html.CheckBoxFor(m => m.ShowContainerPicker) + @Html.LabelFor(m => m.ShowContainerPicker, @T("Show Container Picker").ToString(), new { @class = "forcheckbox" }) + @T("Check this option to render a container picker on the content editor for this type.") +
+
+ @Html.CheckBoxFor(m => m.ShowPositionEditor) + @Html.LabelFor(m => m.ShowPositionEditor, @T("Show Position Editor").ToString(), new { @class = "forcheckbox" }) + @T("Check this option to render a position editor on the content editor for this type.") +
\ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Views/DefinitionTemplates/ContainerTypePartSettings.cshtml b/src/Orchard.Web/Core/Containers/Views/DefinitionTemplates/ContainerTypePartSettings.cshtml deleted file mode 100644 index 8bf4dfbbd..000000000 --- a/src/Orchard.Web/Core/Containers/Views/DefinitionTemplates/ContainerTypePartSettings.cshtml +++ /dev/null @@ -1,9 +0,0 @@ -@model Orchard.Core.Containers.Settings.ContainerTypePartSettings -
- - @Html.EditorFor(m => m.PageSizeDefault) -
-
- - @Html.EditorFor(m => m.PaginatedDefault) -
\ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Views/DefinitionTemplates/ContainerTypePartSettingsViewModel.cshtml b/src/Orchard.Web/Core/Containers/Views/DefinitionTemplates/ContainerTypePartSettingsViewModel.cshtml new file mode 100644 index 000000000..d384e16f7 --- /dev/null +++ b/src/Orchard.Web/Core/Containers/Views/DefinitionTemplates/ContainerTypePartSettingsViewModel.cshtml @@ -0,0 +1,39 @@ +@model Orchard.Core.Containers.ViewModels.ContainerTypePartSettingsViewModel +@{ + Script.Require("ShapesBase"); +} +
+ + @Html.EditorFor(m => m.PageSizeDefault) +
+
+ + @Html.EditorFor(m => m.PaginatedDefault) +
+
+ @Html.LabelFor(m => m.EnablePositioning, @T("Enable drag and drop")) + @Html.EditorFor(m => m.EnablePositioning) + @T("Check this option to enable manual repositioning of items using. If not set, this option will be configurable on the list content item itself.") +
+
+ @Html.LabelFor(m => m.AdminListViewName, @T("List View")) + @Html.EditorFor(m => m.AdminListViewName, new { Providers = Model.ListViewProviders }) + @T("The default list view to use when rendering this list in the admin. This is configurable per individual list.") +
+@if (Model.AvailableItemContentTypes.Any()) { +
+ @Html.CheckBoxFor(m => m.RestrictItemContentTypes) + @Html.LabelFor(m => m.RestrictItemContentTypes, @T("Restrict the item content types this type supports.").ToString(), new { @class = "forcheckbox" }) + +
+ @Html.LabelFor(m => m.RestrictedItemContentTypes, T("Supported Types")) + + @T("Select zero or more content types that this content type supports. Selecting zero content types means the list can contain any content type. Only types with the Containable part can be contained in a list.") +
+
+} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Views/EditorTemplates/Containable.cshtml b/src/Orchard.Web/Core/Containers/Views/EditorTemplates/Containable.cshtml index f5c1b05ee..b17bbf312 100644 --- a/src/Orchard.Web/Core/Containers/Views/EditorTemplates/Containable.cshtml +++ b/src/Orchard.Web/Core/Containers/Views/EditorTemplates/Containable.cshtml @@ -1,11 +1,15 @@ @model Orchard.Core.Containers.ViewModels.ContainableViewModel
- - @Html.LabelFor(m => m.ContainerId, T("Add to")) - @Html.DropDownListFor(m => m.ContainerId, Model.AvailableContainers) - - - @Html.LabelFor(m => m.Weight, T("Weight")) - @Html.TextBoxFor(m => m.Weight, new { @class = "text small" }) - + @if (Model.ShowContainerPicker) { + + @Html.LabelFor(m => m.ContainerId, T("Member of")) + @Html.DropDownListFor(m => m.ContainerId, Model.AvailableContainers) + + } + @if (Model.ShowPositionEditor) { + + @Html.LabelFor(m => m.Position, T("Position")) + @Html.TextBoxFor(m => m.Position, new {@class = "text text-small"}) + + }
\ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Views/EditorTemplates/Container.cshtml b/src/Orchard.Web/Core/Containers/Views/EditorTemplates/Container.cshtml index a3373538a..02cdf1673 100644 --- a/src/Orchard.Web/Core/Containers/Views/EditorTemplates/Container.cshtml +++ b/src/Orchard.Web/Core/Containers/Views/EditorTemplates/Container.cshtml @@ -1,39 +1,56 @@ @model Orchard.Core.Containers.ViewModels.ContainerViewModel -@using Orchard.Core.Containers.Models; -
+@{ + Script.Require("ShapesBase"); +} +
- @Html.CheckBoxFor(m => m.Part.ItemsShown) - + @Html.EditorFor(m => m.ItemsShown) + + @T("Renders the contained items when this list is displayed.")
-
- @Html.LabelFor(m => m.Part.ItemContentType, T("Contains")) - @Html.DropDownListFor(m => m.Part.ItemContentType, Model.AvailableContainables) - @T("Only types with the Containable part can be contained in a list.") -
-
- @Html.LabelFor(m => m.Part.OrderByProperty, T("Order by")) - - -
+@if (Model.OverrideEnablePositioning) { +
+ + @Html.EditorFor(m => m.EnablePositioning) + @Html.LabelFor(m => m.EnablePositioning, @T("Enable positoning").ToString(), new { @class = "forcheckbox" }) + @T("Check this option to enable manual repositioning of items.") + +
+} +@if (Model.AvailableItemContentTypes.Any()) { +
+ @Html.LabelFor(m => m.SelectedItemContentTypes, T("Contains")) + + @T("Select zero or more content types. Selecting zero content types means the list can contain any content type. Only types with the Containable part can be contained in a list.") +
+}
- @Html.LabelFor(m => m.Part.PageSize, T("Page size")) - @Html.TextBoxFor(m => m.Part.PageSize, new { @class = "text small" }) + @Html.LabelFor(m => m.PageSize, T("Page size")) + @Html.TextBoxFor(m => m.PageSize, new { @class = "text text-small" }) - @Html.CheckBoxFor(m => m.Part.Paginated) - + @Html.CheckBoxFor(m => m.Paginated) + +
+
+ @Html.EditorFor(m => m.ShowOnAdminMenu) + @Html.LabelFor(m => m.ShowOnAdminMenu, T("Show on admin menu").ToString(), new { @class = "forcheckbox" }) +
+ @Html.LabelFor(m => m.AdminMenuText, T("Menu text")) + @Html.TextBoxFor(m => m.AdminMenuText, new { @class = "text-box single-line" }) + + @Html.LabelFor(m => m.AdminMenuPosition, T("Position")) + @Html.TextBoxFor(m => m.AdminMenuPosition, new { @class = "text-box single-line" }) + + @Html.LabelFor(m => m.AdminMenuImageSet, T("Image Set")) + @Html.TextBoxFor(m => m.AdminMenuImageSet, new { @class = "text-box single-line" }) +
\ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Views/EditorTemplates/ListViewPicker.cshtml b/src/Orchard.Web/Core/Containers/Views/EditorTemplates/ListViewPicker.cshtml new file mode 100644 index 000000000..719690f94 --- /dev/null +++ b/src/Orchard.Web/Core/Containers/Views/EditorTemplates/ListViewPicker.cshtml @@ -0,0 +1,7 @@ +@using Orchard.Core.Containers.Services +@{ + var providers = (IEnumerable)ViewBag.Providers; + var value = ViewData.TemplateInfo.FormattedModelValue as string; + var options = providers.Select(x => new SelectListItem {Text = x.DisplayName, Value = x.Name, Selected = x.Name == value}); +} +@Html.DropDownList("", options) \ No newline at end of file diff --git a/src/Orchard.Web/Core/Orchard.Core.csproj b/src/Orchard.Web/Core/Orchard.Core.csproj index 143a45047..665b0d9cd 100644 --- a/src/Orchard.Web/Core/Orchard.Core.csproj +++ b/src/Orchard.Web/Core/Orchard.Core.csproj @@ -102,6 +102,8 @@ + + @@ -113,11 +115,16 @@ - + + + + + + @@ -359,7 +366,7 @@ - + @@ -393,7 +400,7 @@ - + @@ -541,6 +548,12 @@ + + + + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/src/Orchard.Web/Modules/Orchard.ContentPicker/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.ContentPicker/Controllers/AdminController.cs index 44f21aa73..da513a65f 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentPicker/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.ContentPicker/Controllers/AdminController.cs @@ -40,10 +40,8 @@ namespace Orchard.ContentPicker.Controllers { public Localizer T { get; set; } [Themed(false)] - public ActionResult Index(ListContentsViewModel model, PagerParameters pagerParameters, string part, string field) { - - IEnumerable menuItems = _navigationManager.BuildMenu("content-picker").ToList(); - + public ActionResult Index(ListContentsViewModel model, PagerParameters pagerParameters, string part, string field, string types) { + var menuItems = _navigationManager.BuildMenu("content-picker").ToList(); var contentPickerMenuItem = menuItems.FirstOrDefault(); if (contentPickerMenuItem == null) { return HttpNotFound(); @@ -82,9 +80,13 @@ namespace Orchard.ContentPicker.Controllers { } } - IEnumerable contentTypes; if (settings != null && !String.IsNullOrEmpty(settings.DisplayedContentTypes)) { - var rawTypes = settings.DisplayedContentTypes.Split(new[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries).ToList(); + types = settings.DisplayedContentTypes; + } + + IEnumerable contentTypes; + if (!String.IsNullOrEmpty(types)) { + var rawTypes = types.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList(); contentTypes = _contentDefinitionManager .ListTypeDefinitions() .Where(x => x.Parts.Any(p => rawTypes.Contains(p.PartDefinition.Name)) || rawTypes.Contains(x.Name)) @@ -95,7 +97,6 @@ namespace Orchard.ContentPicker.Controllers { } var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters); - var query = Services.ContentManager.Query(VersionOptions.Latest, contentTypes.Select(ctd => ctd.Name).ToArray()); if (!string.IsNullOrEmpty(model.Options.SelectedFilter)) { @@ -128,15 +129,15 @@ namespace Orchard.ContentPicker.Controllers { var pagerShape = Services.New.Pager(pager).TotalItemCount(query.Count()); var pageOfContentItems = query.Slice(pager.GetStartIndex(), pager.PageSize).ToList(); - var list = Services.New.List(); + list.AddRange(pageOfContentItems.Select(ci => Services.ContentManager.BuildDisplay(ci, "SummaryAdmin"))); foreach(IShape item in list.Items) { item.Metadata.Type = "ContentPicker"; } - dynamic tab = Services.New.RecentContentTab() + var tab = Services.New.RecentContentTab() .ContentItems(list) .Pager(pagerShape) .Options(model.Options) diff --git a/src/Orchard.Web/Modules/Orchard.ContentPicker/Scripts/ContentPicker.js b/src/Orchard.Web/Modules/Orchard.ContentPicker/Scripts/ContentPicker.js index 11b3a9f26..983909eb1 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentPicker/Scripts/ContentPicker.js +++ b/src/Orchard.Web/Modules/Orchard.ContentPicker/Scripts/ContentPicker.js @@ -1,6 +1,6 @@ jQuery(function ($) { - $("form").bind("orchard-admin-contentpicker-open", function (ev, data) { + $("#layout-content").on("orchard-admin-contentpicker-open", "form", function (ev, data) { data = data || {}; // the popup will be doing full page reloads, so will not be able to retain // a pointer to the callback. We will generate a temporary callback @@ -21,10 +21,13 @@ if (baseUrl.slice(-1) == '/') baseUrl = baseUrl.substr(0, baseUrl.length - 1); - var url = baseUrl - + "/Admin/Orchard.ContentPicker?" - + "callback=" + callbackName - + "&" + (new Date() - 0); + var url = baseUrl + "/Admin/Orchard.ContentPicker?"; + + if (data.types) { + url += "types=" + encodeURIComponent(data.types) + "&"; + } + + url += "callback=" + callbackName + "&" + (new Date() - 0); if (data.part) { url += "&part=" + encodeURIComponent(data.part); diff --git a/src/Orchard.Web/Modules/Orchard.ContentPicker/Views/ContentPicker.SummaryAdmin.cshtml b/src/Orchard.Web/Modules/Orchard.ContentPicker/Views/ContentPicker.SummaryAdmin.cshtml index 92f3c6089..cca2889ac 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentPicker/Views/ContentPicker.SummaryAdmin.cshtml +++ b/src/Orchard.Web/Modules/Orchard.ContentPicker/Views/ContentPicker.SummaryAdmin.cshtml @@ -13,7 +13,14 @@ } -