diff --git a/src/Orchard.Web/Core/Containers/Drivers/ContainerPartDriver.cs b/src/Orchard.Web/Core/Containers/Drivers/ContainerPartDriver.cs index 11bf406ee..081c5ffd7 100644 --- a/src/Orchard.Web/Core/Containers/Drivers/ContainerPartDriver.cs +++ b/src/Orchard.Web/Core/Containers/Drivers/ContainerPartDriver.cs @@ -1,12 +1,16 @@ -using System.Linq; +using System; +using System.Linq; +using System.Web.Mvc; using System.Web.Routing; using Orchard.ContentManagement; +using Orchard.ContentManagement.Aspects; using Orchard.ContentManagement.Drivers; using Orchard.ContentManagement.Handlers; using Orchard.ContentManagement.MetaData; using Orchard.Core.Common.Models; using Orchard.Core.Containers.Models; using Orchard.Core.Containers.Settings; +using Orchard.Core.Containers.ViewModels; using Orchard.Data; using Orchard.Localization; using Orchard.UI.Notify; @@ -42,17 +46,31 @@ namespace Orchard.Core.Containers.Drivers { Services.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, null, shapeHelper); + return Editor(part, (IUpdateModel)null, shapeHelper); } protected override DriverResult Editor(ContainerPart part, IUpdateModel updater, dynamic shapeHelper) { return ContentShape( "Parts_Container_Edit", () => { - if (updater != null) - updater.TryUpdateModel(part, "Container", null, null); + var model = new ContainerViewModel { Part = part }; + // todo: is there a non-string comparison way to find ConaintableParts? + 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 shapeHelper.EditorTemplate(TemplateName: "Container", Model: part, Prefix: "Container"); + model.AvailableContainables = new SelectList(listItems, "Value", "Text", model.Part.Record.ItemContentType); + + if (updater != null) { + updater.TryUpdateModel(model, "Container", null, null); + } + + return shapeHelper.EditorTemplate(TemplateName: "Container", Model: model, Prefix: "Container"); }); } } @@ -71,20 +89,5 @@ namespace Orchard.Core.Containers.Drivers { part.Record.OrderByDirection = (int)OrderByDirection.Descending; }); } - - protected override void GetItemMetadata(GetContentItemMetadataContext context) { - var container = context.ContentItem.As(); - - if (container == null) - return; - - // containers link to their contents in admin screens - context.Metadata.AdminRouteValues = new RouteValueDictionary { - {"Area", "Contents"}, - {"Controller", "Admin"}, - {"Action", "List"}, - {"containerId", container.Id} - }; - } } } diff --git a/src/Orchard.Web/Core/Containers/Migrations.cs b/src/Orchard.Web/Core/Containers/Migrations.cs index 4a91eaec6..bab60d441 100644 --- a/src/Orchard.Web/Core/Containers/Migrations.cs +++ b/src/Orchard.Web/Core/Containers/Migrations.cs @@ -45,5 +45,10 @@ namespace Orchard.Core.Containers { return 1; } + + public int UpdateFrom1() { + SchemaBuilder.AlterTable("ContainerPartRecord", table => table.AddColumn("ItemContentType")); + return 2; + } } } \ 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 31615d26a..6ee085097 100644 --- a/src/Orchard.Web/Core/Containers/Models/ContainerPart.cs +++ b/src/Orchard.Web/Core/Containers/Models/ContainerPart.cs @@ -6,6 +6,7 @@ namespace Orchard.Core.Containers.Models { } public class ContainerPartRecord : ContentPartRecord { + public virtual string ItemContentType { get; set; } public virtual bool Paginated { get; set; } public virtual int PageSize { get; set; } public virtual string OrderByProperty { get; set; } diff --git a/src/Orchard.Web/Core/Containers/Placement.info b/src/Orchard.Web/Core/Containers/Placement.info index 17d6f76a9..9feba91ba 100644 --- a/src/Orchard.Web/Core/Containers/Placement.info +++ b/src/Orchard.Web/Core/Containers/Placement.info @@ -3,7 +3,6 @@ @@ -11,7 +10,4 @@ - - - diff --git a/src/Orchard.Web/Core/Containers/ViewModels/ContainerViewModel.cs b/src/Orchard.Web/Core/Containers/ViewModels/ContainerViewModel.cs new file mode 100644 index 000000000..a5dcb7a6f --- /dev/null +++ b/src/Orchard.Web/Core/Containers/ViewModels/ContainerViewModel.cs @@ -0,0 +1,9 @@ +using System.Web.Mvc; +using Orchard.Core.Containers.Models; + +namespace Orchard.Core.Containers.ViewModels { + public class ContainerViewModel { + public ContainerPart Part { get; set; } + public SelectList AvailableContainables { get; set; } + } +} \ 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 d9cc0c7d2..b799df38d 100644 --- a/src/Orchard.Web/Core/Containers/Views/EditorTemplates/Container.cshtml +++ b/src/Orchard.Web/Core/Containers/Views/EditorTemplates/Container.cshtml @@ -1,27 +1,31 @@ -@model Orchard.Core.Containers.Models.ContainerPart +@model Orchard.Core.Containers.ViewModels.ContainerViewModel @using Orchard.Core.Containers.Models;
- @Html.LabelFor(m => m.Record.OrderByProperty, T("Order by")) - + @Html.SelectOption(Model.Part.Record.OrderByProperty, "CommonPart.PublishedUtc", T("Date Published").Text) + @Html.SelectOption(Model.Part.Record.OrderByProperty, "RoutePart.Title", T("Title").Text) + @Html.SelectOption(Model.Part.Record.OrderByProperty, "RoutePart.Slug", T("Slug").Text) + @Html.SelectOption(Model.Part.Record.OrderByProperty, "CustomPropertiesPart.CustomOne", T("Custom 1").Text) + @Html.SelectOption(Model.Part.Record.OrderByProperty, "CustomPropertiesPart.CustomTwo", T("Custom 2").Text) + @Html.SelectOption(Model.Part.Record.OrderByProperty, "CustomPropertiesPart.CustomThree", T("Custom 3").Text) - + @Html.SelectOption(Model.Part.Record.OrderByDirection, (int)OrderByDirection.Ascending, T("Ascending").Text) + @Html.SelectOption(Model.Part.Record.OrderByDirection, (int)OrderByDirection.Descending, T("Descending").Text)
- @Html.LabelFor(m => m.Record.PageSize, T("Page size")) - @Html.TextBoxFor(m => m.Record.PageSize, new { @class = "text text-small" }) + @Html.LabelFor(m => m.Part.Record.PageSize, T("Page size")) + @Html.TextBoxFor(m => m.Part.Record.PageSize, new { @class = "text text-small" }) - @Html.CheckBoxFor(m => m.Record.Paginated) - + @Html.CheckBoxFor(m => m.Part.Record.Paginated) +
\ No newline at end of file diff --git a/src/Orchard.Web/Core/Contents/Controllers/AdminController.cs b/src/Orchard.Web/Core/Contents/Controllers/AdminController.cs index 79e7fb403..94384eb18 100644 --- a/src/Orchard.Web/Core/Contents/Controllers/AdminController.cs +++ b/src/Orchard.Web/Core/Contents/Controllers/AdminController.cs @@ -9,6 +9,7 @@ using Orchard.ContentManagement.Aspects; using Orchard.ContentManagement.MetaData; using Orchard.ContentManagement.MetaData.Models; using Orchard.Core.Common.Models; +using Orchard.Core.Containers.Models; using Orchard.Core.Contents.Settings; using Orchard.Core.Contents.ViewModels; using Orchard.Data; @@ -52,10 +53,8 @@ namespace Orchard.Core.Contents.Controllers { public ActionResult List(ListContentsViewModel model, PagerParameters pagerParameters) { Pager pager = new Pager(_siteService.GetSiteSettings(), pagerParameters); - if (model.ContainerId != null && _contentManager.GetLatest((int)model.ContainerId) == null) - return HttpNotFound(); - var query = _contentManager.Query(VersionOptions.Latest, GetCreatableTypes().Select(ctd => ctd.Name).ToArray()); + var query = _contentManager.Query(VersionOptions.Latest, GetCreatableTypes(false).Select(ctd => ctd.Name).ToArray()); if (!string.IsNullOrEmpty(model.TypeName)) { var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(model.TypeName); @@ -68,10 +67,6 @@ namespace Orchard.Core.Contents.Controllers { query = query.ForType(model.TypeName); } - if (model.ContainerId != null) { - query = query.Join().Where(cr => cr.Container.Id == model.ContainerId); - } - switch (model.Options.OrderBy) { case ContentsOrder.Modified: //query = query.OrderByDescending(ci => ci.ContentItemRecord.Versions.Single(civr => civr.Latest).Id); @@ -87,7 +82,7 @@ namespace Orchard.Core.Contents.Controllers { } model.Options.SelectedFilter = model.TypeName; - model.Options.FilterOptions = GetCreatableTypes() + model.Options.FilterOptions = GetCreatableTypes(false) .Select(ctd => new KeyValuePair(ctd.Name, ctd.DisplayName)) .ToList().OrderBy(kvp => kvp.Key); @@ -107,8 +102,8 @@ namespace Orchard.Core.Contents.Controllers { return View((object)viewModel); } - private IEnumerable GetCreatableTypes() { - return _contentDefinitionManager.ListTypeDefinitions().Where(ctd => ctd.Settings.GetModel().Creatable); + private IEnumerable GetCreatableTypes(bool andContainable) { + return _contentDefinitionManager.ListTypeDefinitions().Where(ctd => ctd.Settings.GetModel().Creatable && (!andContainable || ctd.Parts.Any(p => p.PartDefinition.Name == "ContainablePart")) ); } [HttpPost, ActionName("List")] @@ -117,7 +112,7 @@ namespace Orchard.Core.Contents.Controllers { var routeValues = ControllerContext.RouteData.Values; if (options != null) { routeValues["Options.OrderBy"] = options.OrderBy; //todo: don't hard-code the key - if (GetCreatableTypes().Any(ctd => string.Equals(ctd.Name, options.SelectedFilter, StringComparison.OrdinalIgnoreCase))) { + if (GetCreatableTypes(false).Any(ctd => string.Equals(ctd.Name, options.SelectedFilter, StringComparison.OrdinalIgnoreCase))) { routeValues["id"] = options.SelectedFilter; } else { @@ -177,22 +172,29 @@ namespace Orchard.Core.Contents.Controllers { return this.RedirectLocal(returnUrl, () => RedirectToAction("List")); } - ActionResult CreatableTypeList() { - dynamic viewModel = Shape.ViewModel(ContentTypes: GetCreatableTypes()); + ActionResult CreatableTypeList(int? containerId) { + dynamic viewModel = Shape.ViewModel(ContentTypes: GetCreatableTypes(containerId.HasValue), ContainerId: containerId); // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation. return View("CreatableTypeList", (object)viewModel); } - public ActionResult Create(string id) { + public ActionResult Create(string id, int? containerId) { if (string.IsNullOrEmpty(id)) - return CreatableTypeList(); + return CreatableTypeList(containerId); var contentItem = _contentManager.New(id); if (!Services.Authorizer.Authorize(Permissions.PublishContent, contentItem, T("Cannot create content"))) return new HttpUnauthorizedResult(); + if (containerId.HasValue && contentItem.Is()) { + var common = contentItem.As(); + if (common != null) { + common.Container = _contentManager.Get(containerId.Value); + } + } + dynamic model = _contentManager.BuildEditor(contentItem); // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation. return View((object)model); diff --git a/src/Orchard.Web/Core/Contents/ViewModels/ListContentsViewModel.cs b/src/Orchard.Web/Core/Contents/ViewModels/ListContentsViewModel.cs index bf6cd6be5..c3171687b 100644 --- a/src/Orchard.Web/Core/Contents/ViewModels/ListContentsViewModel.cs +++ b/src/Orchard.Web/Core/Contents/ViewModels/ListContentsViewModel.cs @@ -8,7 +8,6 @@ namespace Orchard.Core.Contents.ViewModels { } public string Id { get; set; } - public int? ContainerId { get; set; } public string TypeName { get { return Id; } diff --git a/src/Orchard.Web/Core/Contents/Views/Admin/CreatableTypeList.cshtml b/src/Orchard.Web/Core/Contents/Views/Admin/CreatableTypeList.cshtml index c5c5d445c..277567394 100644 --- a/src/Orchard.Web/Core/Contents/Views/Admin/CreatableTypeList.cshtml +++ b/src/Orchard.Web/Core/Contents/Views/Admin/CreatableTypeList.cshtml @@ -1,5 +1,5 @@ @{ Layout.Title = T("Create New Content").ToString(); } @foreach (var type in Model.ContentTypes) { -

@Html.ActionLink((string)type.DisplayName, "Create", new { Area = "Contents", Id = (string)type.Name })

+

@Html.ActionLink((string)type.DisplayName, "Create", new { Area = "Contents", Id = (string)type.Name, ContainerId = Model.ContainerId })

} \ 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 2109b0297..54d6e3a87 100644 --- a/src/Orchard.Web/Core/Orchard.Core.csproj +++ b/src/Orchard.Web/Core/Orchard.Core.csproj @@ -87,6 +87,7 @@ + @@ -99,7 +100,6 @@ - @@ -383,9 +383,6 @@ - - - diff --git a/src/Orchard.Web/Modules/Orchard.Lists/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Lists/Controllers/AdminController.cs new file mode 100644 index 000000000..5fbc671d4 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Lists/Controllers/AdminController.cs @@ -0,0 +1,269 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Collections.Generic; +using System.Web.Mvc; +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.Contents; +using Orchard.Core.Contents.Controllers; +using Orchard.Core.Contents.Settings; +using Orchard.Data; +using Orchard.DisplayManagement; +using Orchard.DisplayManagement.Shapes; +using Orchard.Lists.ViewModels; +using Orchard.Localization; +using Orchard; +using Orchard.Logging; +using Orchard.Mvc.Extensions; +using Orchard.Settings; +using Orchard.UI.Navigation; +using Orchard.UI.Notify; + +namespace Lists.Controllers { + public class AdminController : Controller { + private readonly IContentManager _contentManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ITransactionManager _transactionManager; + private readonly ISiteService _siteService; + + public IOrchardServices Services { get; set; } + + public AdminController( + IOrchardServices orchardServices, + IContentManager contentManager, + IContentDefinitionManager contentDefinitionManager, + ITransactionManager transactionManager, + ISiteService siteService, + IShapeFactory shapeFactory) { + Services = orchardServices; + _contentManager = contentManager; + _contentDefinitionManager = contentDefinitionManager; + _transactionManager = transactionManager; + _siteService = siteService; + T = NullLocalizer.Instance; + Logger = NullLogger.Instance; + Shape = shapeFactory; + } + + public Localizer T { get; set; } + public ILogger Logger { get; set; } + dynamic Shape { get; set; } + + private IEnumerable GetContainableTypes() { + return _contentDefinitionManager.ListTypeDefinitions().Where(ctd => ctd.Parts.Any(c => c.PartDefinition.Name == "ContainablePart") && ctd.Settings.GetModel().Creatable); + } + + public ActionResult List(ListContentsViewModel model, PagerParameters pagerParameters) { + var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters); + var container = _contentManager.GetLatest((int)model.ContainerId); + if (container == null) { + return HttpNotFound(); + } + var restrictedContentType = container.As().Record.ItemContentType; + var hasRestriction = !string.IsNullOrEmpty(restrictedContentType); + + var metadata = container.ContentManager.GetItemMetadata(container); + model.ContainerDisplayName = metadata.DisplayText; + if (string.IsNullOrEmpty(model.ContainerDisplayName)) { + model.ContainerDisplayName = container.ContentType; + } + + var query = _contentManager.Query(VersionOptions.Latest); + + if (hasRestriction) { + model.FilterByContentType = restrictedContentType; + } + + if (!string.IsNullOrEmpty(model.FilterByContentType)) { + var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(model.FilterByContentType); + if (contentTypeDefinition == null) + return HttpNotFound(); + query = query.ForType(model.FilterByContentType); + } + query = query.Join().Where(cr => cr.Container.Id == model.ContainerId); + + switch (model.Options.OrderBy) { + case ContentsOrder.Modified: + query = query.OrderByDescending(cr => cr.ModifiedUtc); + break; + case ContentsOrder.Published: + query = query.OrderByDescending(cr => cr.PublishedUtc); + break; + case ContentsOrder.Created: + query = query.OrderByDescending(cr => cr.CreatedUtc); + break; + } + + model.Options.SelectedFilter = model.FilterByContentType; + + if (!hasRestriction) { + model.Options.FilterOptions = GetContainableTypes() + .Select(ctd => new KeyValuePair(ctd.Name, ctd.DisplayName)) + .ToList().OrderBy(kvp => kvp.Key); + } + + var pagerShape = Shape.Pager(pager).TotalItemCount(query.Count()); + var pageOfContentItems = query.Slice(pager.GetStartIndex(), pager.PageSize).ToList(); + + var list = Shape.List(); + list.AddRange(pageOfContentItems.Select(ci => _contentManager.BuildDisplay(ci, "SummaryAdmin"))); + + dynamic viewModel = Shape.ViewModel() + .ContentItems(list) + .Pager(pagerShape) + .ContainerId(model.ContainerId) + .Options(model.Options) + .HasRestriction(hasRestriction) + .ContainerDisplayName(model.ContainerDisplayName) + .ContainerContentType(container.ContentType) + .ContainerItemContentType(hasRestriction ? restrictedContentType : (model.FilterByContentType ?? "")) + .OtherLists(_contentManager.Query(VersionOptions.Latest).List() + .Select(part => part.ContentItem) + .Where(item => item != container) + .OrderBy(item => item.As().VersionPublishedUtc)); + + // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation. + return View((object)viewModel); + } + + [HttpPost, ActionName("List")] + [FormValueRequired("submit.BulkEdit")] + public ActionResult ListPOST(ContentOptions options, IEnumerable itemIds, int? targetContainerId, string returnUrl) { + if (itemIds != null) { + switch (options.BulkAction) { + case ContentsBulkAction.None: + break; + case ContentsBulkAction.PublishNow: + if (!BulkPublishNow(itemIds)) { + return new HttpUnauthorizedResult(); + } + break; + case ContentsBulkAction.Unpublish: + if (!BulkUnpublish(itemIds)) { + return new HttpUnauthorizedResult(); + } + break; + case ContentsBulkAction.Remove: + if (!BulkRemove(itemIds)) { + return new HttpUnauthorizedResult(); + } + break; + case ContentsBulkAction.RemoveFromList: + if (!BulkRemoveFromList(itemIds)) { + return new HttpUnauthorizedResult(); + } + break; + case ContentsBulkAction.MoveToList: + if (!BulkMoveToList(itemIds, targetContainerId)) { + return new HttpUnauthorizedResult(); + } + break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + + return this.RedirectLocal(returnUrl, () => RedirectToAction("List")); + } + + private bool BulkMoveToList(IEnumerable itemIds, int? targetContainerId) { + if (!targetContainerId.HasValue) { + Services.Notifier.Information(T("Please select the list to move the items to.")); + return true; + } + var id = targetContainerId.Value; + var targetContainer = _contentManager.Get(id); + if (targetContainer == null) { + Services.Notifier.Information(T("Please select the list to move the items to.")); + return true; + } + var itemContentType = targetContainer.Record.ItemContentType; + + foreach (var item in itemIds.Select(itemId => _contentManager.GetLatest(itemId))) { + if (!Services.Authorizer.Authorize(Permissions.EditContent, item, T("Couldn't move selected content."))) { + return false; + } + // ensure the item can be in that container. + if (!string.IsNullOrEmpty(itemContentType) && item.ContentType != itemContentType) { + Services.Notifier.Information(T("One or more items could not be moved to the '{0}' list because it is restricted to containing items of type '{1}'.", _contentManager.GetItemMetadata(item).DisplayText, itemContentType)); + return true; // todo: transactions + } + + item.As().Record.Container = targetContainer.ContentItem.Record; + } + Services.Notifier.Information(T("Content successfully moved to {1}.", + Url.Action("List", new { containerId = targetContainerId }), _contentManager.GetItemMetadata(targetContainer).DisplayText)); + return true; + } + + private bool BulkRemoveFromList(IEnumerable itemIds) { + foreach (var item in itemIds.Select(itemId => _contentManager.GetLatest(itemId))) { + if (!Services.Authorizer.Authorize(Permissions.EditContent, item, T("Couldn't remove selected content from the list."))) { + return false; + } + item.As().Record.Container = null; + } + Services.Notifier.Information(T("Content successfully removed from the list.")); + return true; + } + + private bool BulkRemove(IEnumerable itemIds) { + foreach (var item in itemIds.Select(itemId => _contentManager.GetLatest(itemId))) { + if (!Services.Authorizer.Authorize(Permissions.DeleteContent, item, T("Couldn't remove selected content."))) { + return false; + } + + _contentManager.Remove(item); + } + Services.Notifier.Information(T("Content successfully removed.")); + return true; + } + + private bool BulkUnpublish(IEnumerable itemIds) { + foreach (var item in itemIds.Select(itemId => _contentManager.GetLatest(itemId))) { + if (!Services.Authorizer.Authorize(Permissions.PublishContent, item, T("Couldn't unpublish selected content."))) { + return false; + } + + _contentManager.Unpublish(item); + } + Services.Notifier.Information(T("Content successfully unpublished.")); + return true; + } + + private bool BulkPublishNow(IEnumerable itemIds) { + foreach (var item in itemIds.Select(itemId => _contentManager.GetLatest(itemId))) { + if (!Services.Authorizer.Authorize(Permissions.PublishContent, item, T("Couldn't publish selected content."))) + return false; + + _contentManager.Publish(item); + } + Services.Notifier.Information(T("Content successfully published.")); + return true; + } + + [HttpPost, ActionName("List")] + [FormValueRequired("submit.Filter")] + public ActionResult ListFilterPOST(ContentOptions options) { + var routeValues = ControllerContext.RouteData.Values; + if (options != null) { + routeValues["Options.OrderBy"] = options.OrderBy; + if (GetContainableTypes().Any(ctd => string.Equals(ctd.Name, options.SelectedFilter, StringComparison.OrdinalIgnoreCase))) { + routeValues["filterByContentType"] = options.SelectedFilter; + } + else { + routeValues.Remove("filterByContentType"); + } + } + + return RedirectToAction("List", routeValues); + } + + + } +} diff --git a/src/Orchard.Web/Modules/Orchard.Lists/Handlers/ContainerPartHandler.cs b/src/Orchard.Web/Modules/Orchard.Lists/Handlers/ContainerPartHandler.cs new file mode 100644 index 000000000..6abc4c5e1 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Lists/Handlers/ContainerPartHandler.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Web.Routing; +using Orchard.ContentManagement; +using Orchard.ContentManagement.Handlers; +using Orchard.Core.Containers.Models; +using Orchard.Data; + +namespace Orchard.Lists.Handlers { + public class ContainerPartHandler : ContentHandler { + public ContainerPartHandler() { + } + + protected override void GetItemMetadata(GetContentItemMetadataContext context) { + var container = context.ContentItem.As(); + + if (container == null) + return; + + // containers link to their contents in admin screens + context.Metadata.AdminRouteValues = new RouteValueDictionary { + {"Area", "Orchard.Lists"}, + {"Controller", "Admin"}, + {"Action", "List"}, + {"containerId", container.Id} + }; + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Lists/Orchard.Lists.csproj b/src/Orchard.Web/Modules/Orchard.Lists/Orchard.Lists.csproj index ed0881b70..cae96d59b 100644 --- a/src/Orchard.Web/Modules/Orchard.Lists/Orchard.Lists.csproj +++ b/src/Orchard.Web/Modules/Orchard.Lists/Orchard.Lists.csproj @@ -39,6 +39,7 @@ AllRules.ruleset + False ..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll @@ -47,7 +48,11 @@ + + + + @@ -61,6 +66,7 @@ + @@ -76,7 +82,21 @@ - + + + + + + + + + + + + + + Designer + diff --git a/src/Orchard.Web/Modules/Orchard.Lists/Placement.info b/src/Orchard.Web/Modules/Orchard.Lists/Placement.info new file mode 100644 index 000000000..95aaa5357 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Lists/Placement.info @@ -0,0 +1,9 @@ + + + + + + + diff --git a/src/Orchard.Web/Core/Contents/Routes.cs b/src/Orchard.Web/Modules/Orchard.Lists/Routes.cs similarity index 76% rename from src/Orchard.Web/Core/Contents/Routes.cs rename to src/Orchard.Web/Modules/Orchard.Lists/Routes.cs index 8ceac5787..dcb67529e 100644 --- a/src/Orchard.Web/Core/Contents/Routes.cs +++ b/src/Orchard.Web/Modules/Orchard.Lists/Routes.cs @@ -3,7 +3,7 @@ using System.Web.Mvc; using System.Web.Routing; using Orchard.Mvc.Routes; -namespace Orchard.Core.Contents { +namespace Orchard.Lists { public class Routes : IRouteProvider { public void GetRoutes(ICollection routes) { foreach (RouteDescriptor routeDescriptor in GetRoutes()) { @@ -16,36 +16,36 @@ namespace Orchard.Core.Contents { new RouteDescriptor { Priority = 5, Route = new Route( - "Admin/Contents/List/{id}/InContainer/{containerId}", + "Admin/Orchard.Lists/{containerId}/{filterByContentType}", new RouteValueDictionary { {"area", "Contents"}, {"controller", "Admin"}, - {"action", "List"} + {"action", "List"}, + {"filterByContentType", ""} }, - new RouteValueDictionary { - {"id", @"\w+"}, + new RouteValueDictionary{ + {"filterByContentType", @"\w+"}, {"containerId", @"\d+"} }, new RouteValueDictionary { - {"area", "Contents"} + {"area", "Orchard.Lists"} }, new MvcRouteHandler()) }, new RouteDescriptor { Priority = 5, Route = new Route( - "Admin/Contents/List/InContainer/{containerId}", + "Admin/Orchard.Lists/{containerId}", new RouteValueDictionary { - {"area", "Contents"}, + {"area", "Orchard.Lists"}, {"controller", "Admin"}, - {"action", "List"}, - {"id", ""} + {"action", "List"} }, - new RouteValueDictionary{ + new RouteValueDictionary { {"containerId", @"\d+"} }, new RouteValueDictionary { - {"area", "Contents"} + {"area", "Orchard.Lists"} }, new MvcRouteHandler()) } diff --git a/src/Orchard.Web/Modules/Orchard.Lists/Scripts/Web.config b/src/Orchard.Web/Modules/Orchard.Lists/Scripts/Web.config new file mode 100644 index 000000000..0dc62ece6 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Lists/Scripts/Web.config @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.Lists/Scripts/orchard-lists-admin.js b/src/Orchard.Web/Modules/Orchard.Lists/Scripts/orchard-lists-admin.js new file mode 100644 index 000000000..96efbf3d3 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Lists/Scripts/orchard-lists-admin.js @@ -0,0 +1,10 @@ +jQuery("#publishActions").bind("change", function () { + var value = jQuery(this).val(), + target = jQuery("#TargetContainerId"); + if (value === "MoveToList") { + target.css("display", "inline"); + } + else { + target.css("display", "none"); + } +}); \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Lists/ViewModels/ListContentsViewModel.cs b/src/Orchard.Web/Modules/Orchard.Lists/ViewModels/ListContentsViewModel.cs new file mode 100644 index 000000000..822ffcf08 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Lists/ViewModels/ListContentsViewModel.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using Orchard.ContentManagement; + +namespace Orchard.Lists.ViewModels { + public class ListContentsViewModel { + public ListContentsViewModel() { + Options = new ContentOptions(); + } + + public string FilterByContentType { get; set; } + public int? ContainerId { get; set; } + public string ContainerDisplayName { get; set; } + + public int? Page { get; set; } + public IList Entries { get; set; } + public ContentOptions Options { get; set; } + + #region Nested type: Entry + + public class Entry { + public ContentItem ContentItem { get; set; } + public ContentItemMetadata ContentItemMetadata { get; set; } + } + + #endregion + } + + public class ContentOptions { + public ContentOptions() { + OrderBy = ContentsOrder.Modified; + BulkAction = ContentsBulkAction.None; + } + public string SelectedFilter { get; set; } + public IEnumerable> FilterOptions { get; set; } + public ContentsOrder OrderBy { get; set; } + public ContentsBulkAction BulkAction { get; set; } + } + + public enum ContentsOrder { + Modified, + Published, + Created + } + + public enum ContentsBulkAction { + None, + PublishNow, + Unpublish, + Remove, + RemoveFromList, + MoveToList + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/List.cshtml b/src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/List.cshtml new file mode 100644 index 000000000..ad8723dbb --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/List.cshtml @@ -0,0 +1,64 @@ +@using Orchard.Lists.ViewModels; +@{ + Script.Include("orchard-lists-admin.js"); + + string createLinkText = string.IsNullOrEmpty(Model.ContainerItemContentType) ? T("Create New Content").ToString() : T("Create New {0}", Model.ContainerItemContentType).ToString(); + + Layout.Title = T("Manage Content for {0}", Model.ContainerDisplayName); + + var lists = ((IEnumerable)Model.OtherLists).Select( + contentItem => new SelectListItem { + Text = contentItem.ContentType + ": " + contentItem.ContentManager.GetItemMetadata(contentItem).DisplayText, + Value = contentItem.Id.ToString(System.Globalization.CultureInfo.InvariantCulture) + }).ToList(); + lists.Insert(0, new SelectListItem { Text = T("Move to...").ToString(), Value = "" }); +} + + +
 
+@Display.Parts_Container_Manage(ContainerDisplayName: Model.ContainerDisplayName, ContainerContentType: Model.ContainerContentType, ContainerId: Model.ContainerId) + +
+ @Html.ActionLink(createLinkText, "Create", new { Area = "Contents", Id = (string)Model.Options.SelectedFilter, ContainerId = Model.ContainerId, ReturnUrl = Html.ViewContext.HttpContext.Request.RawUrl }, new { @class = "button primaryAction" }) +
+@using (Html.BeginFormAntiForgeryPost()) { +
+ + + @Html.DropDownList("TargetContainerId", lists, new { id = "TargetContainerId" }) + +
+
+ @if(!Model.HasRestriction) { + + + } + + + +
+
+@Display(Model.ContentItems) +
+@Display(Model.Pager) +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Containers/Views/Parts.Container.Contained.SummaryAdmin.cshtml b/src/Orchard.Web/Modules/Orchard.Lists/Views/Parts.Container.Contained.SummaryAdmin.cshtml similarity index 75% rename from src/Orchard.Web/Core/Containers/Views/Parts.Container.Contained.SummaryAdmin.cshtml rename to src/Orchard.Web/Modules/Orchard.Lists/Views/Parts.Container.Contained.SummaryAdmin.cshtml index 1a1a0534a..48d8e4653 100644 --- a/src/Orchard.Web/Core/Containers/Views/Parts.Container.Contained.SummaryAdmin.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Lists/Views/Parts.Container.Contained.SummaryAdmin.cshtml @@ -5,5 +5,5 @@ ContentPart contentPart = Model.ContentPart; } @if (contentPart.Is()) { - @Html.Link(T("Contained Items").Text, Url.Action("List", "Admin", new { area = "Contents", containerId = contentPart.Id }))@T(" | ") + @Html.Link(T("Contained Items").Text, Url.Action("List", "Admin", new { area = "Orchard.Lists", containerId = contentPart.Id }))@T(" | ") } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Lists/Views/Parts.Container.Manage.cshtml b/src/Orchard.Web/Modules/Orchard.Lists/Views/Parts.Container.Manage.cshtml new file mode 100644 index 000000000..b57c18609 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Lists/Views/Parts.Container.Manage.cshtml @@ -0,0 +1,8 @@ +
+

+ @Html.ActionLink(T("Show Other Lists").ToString(), "List", new { Area = "Contents", Id = Model.ContainerContentType }) | + @Html.ActionLink(T("Show Orphaned Content Items").ToString(), "Orphaned") | + @Html.ActionLink(T("{0} Properties", (string)Model.ContainerContentType).ToString(), "Edit", new { Area = "Contents", Id = Model.ContainerId }) + +

+
\ No newline at end of file