From df689ab80fe09a01b5510cc0e8c96000694409cc Mon Sep 17 00:00:00 2001 From: Magnus Pettersson Date: Sun, 6 Dec 2020 15:41:01 +0100 Subject: [PATCH 1/7] Update placement GUI. --- .../Controllers/AdminController.cs | 94 +++++++-- .../ListShapePlacementsViewModel.cs | 25 ++- .../Views/Admin/Edit.cshtml | 2 +- .../Views/Admin/Index.cshtml | 190 ++++++++++++------ .../Views/_ViewImports.cshtml | 2 + 5 files changed, 233 insertions(+), 80 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs index d34fd3ef9a9..123da38a8d5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs @@ -5,14 +5,19 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Localization; +using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using OrchardCore.DisplayManagement; using OrchardCore.DisplayManagement.Descriptors.ShapePlacementStrategy; using OrchardCore.DisplayManagement.Notify; +using OrchardCore.Navigation; using OrchardCore.Placements.Services; using OrchardCore.Placements.ViewModels; +using OrchardCore.Routing; +using OrchardCore.Settings; namespace OrchardCore.Placements.Controllers { @@ -24,6 +29,8 @@ public class AdminController : Controller private readonly IHtmlLocalizer H; private readonly IStringLocalizer S; private readonly INotifier _notifier; + private readonly ISiteService _siteService; + private readonly dynamic New; public AdminController( ILogger logger, @@ -31,33 +38,63 @@ public AdminController( PlacementsManager placementsManager, IHtmlLocalizer htmlLocalizer, IStringLocalizer stringLocalizer, - INotifier notifier) + INotifier notifier, + ISiteService siteService, + IShapeFactory shapeFactory) { _logger = logger; _authorizationService = authorizationService; _placementsManager = placementsManager; _notifier = notifier; + _siteService = siteService; + New = shapeFactory; H = htmlLocalizer; S = stringLocalizer; } - public async Task Index() + public async Task Index(ContentOptions options, PagerParameters pagerParameters) { if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) { return Forbid(); } + var siteSettings = await _siteService.GetSiteSettingsAsync(); + var pager = new Pager(pagerParameters, siteSettings.PageSize); + var shapeTypes = await _placementsManager.ListShapePlacementsAsync(); - return View(new ListShapePlacementsViewModel + var shapeList = shapeTypes.Select(entry => new ShapePlacementViewModel { - ShapePlacements = shapeTypes.Select(entry => new ShapePlacementViewModel - { - ShapeType = entry.Key - }) - }); + ShapeType = entry.Key + }).ToList(); + + if (!string.IsNullOrWhiteSpace(options.Search)) + { + shapeList = shapeList.Where(x => x.ShapeType.Contains(options.Search)).ToList(); + } + + var count = shapeList.Count(); + + shapeList = shapeList.OrderBy(x => x.ShapeType) + .Skip(pager.GetStartIndex()) + .Take(pager.PageSize).ToList(); + + var pagerShape = (await New.Pager(pager)).TotalItemCount(count); + + var model = new ListShapePlacementsViewModel + { + ShapePlacements = shapeList, + Pager = pagerShape, + Options = options, + }; + + model.Options.ContentsBulkAction = new List() { + new SelectListItem() { Text = S["Delete"], Value = nameof(ContentsBulkAction.Remove) } + }; + + return View("Index", model); } public async Task Create(string suggestion, string returnUrl = null) @@ -139,9 +176,6 @@ public async Task Edit(EditShapePlacementViewModel viewModel, str { IEnumerable placementNodes = JsonConvert.DeserializeObject(viewModel.Nodes) ?? new PlacementNode[0]; - // Remove empty nodes - placementNodes = placementNodes.Where(node => !IsEmpty(node)); - if (placementNodes.Any()) { // Save @@ -196,6 +230,36 @@ public async Task Delete(string shapeType, string returnUrl = nul return RedirectToReturnUrlOrIndex(returnUrl); } + [HttpPost, ActionName("Index")] + [FormValueRequired("submit.BulkAction")] + public async Task IndexPost(ViewModels.ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) + { + return Forbid(); + } + + if (itemIds?.Count() > 0) + { + switch (options.BulkAction) + { + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Remove: + foreach (var item in itemIds) + { + await _placementsManager.RemoveShapePlacementsAsync(item); + } + _notifier.Success(H["Placements successfully removed."]); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + return RedirectToAction("Index"); + } + private IActionResult RedirectToReturnUrlOrIndex(string returnUrl) { if ((String.IsNullOrEmpty(returnUrl) == false) && (Url.IsLocalUrl(returnUrl))) @@ -224,14 +288,6 @@ private static bool ShouldCreateNode(IEnumerable nodes, string di } } - private static bool IsEmpty(PlacementNode node) - { - return string.IsNullOrEmpty(node.Location) - && string.IsNullOrEmpty(node.ShapeType) - && (node.Alternates == null || node.Alternates.Length == 0) - && (node.Wrappers == null || node.Wrappers.Length == 0); - } - private static bool FilterEquals(JToken token, string value) { if (token is JArray) diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ListShapePlacementsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ListShapePlacementsViewModel.cs index fab8c08d747..aa3709a78a3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ListShapePlacementsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ListShapePlacementsViewModel.cs @@ -1,9 +1,32 @@ using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.Rendering; namespace OrchardCore.Placements.ViewModels { public class ListShapePlacementsViewModel { - public IEnumerable ShapePlacements { get; set; } + public IList ShapePlacements { get; set; } + public dynamic Pager { get; set; } + public ContentOptions Options { get; set; } = new ContentOptions(); + } + + public class ContentOptions + { + public string Search { get; set; } + public ContentsBulkAction BulkAction { get; set; } + + #region Lists to populate + + [BindNever] + public List ContentsBulkAction { get; set; } + + #endregion Lists to populate + } + + public enum ContentsBulkAction + { + None, + Remove } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Edit.cshtml index 8bdf90f8822..1781d2b8ba4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Edit.cshtml @@ -48,7 +48,7 @@ @if (Url.IsLocalUrl(returnUrl)) { - @T["Cancel"] + @T["Cancel"] } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml index 33bd03704f8..eaf0b934fd0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml @@ -1,74 +1,146 @@ @model ListShapePlacementsViewModel + +@{ + int startIndex = (Model.Pager.Page - 1) * (Model.Pager.Page) + 1; + int endIndex = startIndex + Model.ShapePlacements.Count - 1; +}

@RenderTitleSegments(T["Placements"])

-
-
-
-
- @T["Filter"] +@* the form is necessary to generate and antiforgery token for the delete action *@ +
+ + + + +
+
+
+
+
+ @T["Filter"] +
+ +
+ +
+
+
-
- @T["Create new placement"]
-
- -
    - @foreach (var shapeType in Model.ShapePlacements) - { -
  • - @Html.DisplayFor(m => shapeType) -
  • - } -
- +
    + @if (Model.ShapePlacements.Any()) + { +
  • +
    +
    +
    + + + + +
    +
    + +
    +
  • + @foreach (var entry in Model.ShapePlacements) + { +
  • +
    + + +
    + +
  • + } + } + else + { +
  • + +
  • + } +
+ - diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Views/_ViewImports.cshtml b/src/OrchardCore.Modules/OrchardCore.Placements/Views/_ViewImports.cshtml index 1553256c8ef..16f5bb9730c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Views/_ViewImports.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Views/_ViewImports.cshtml @@ -7,4 +7,6 @@ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, OrchardCore.DisplayManagement @addTagHelper *, OrchardCore.ResourceManagement +@using Microsoft.Extensions.Localization +@using Microsoft.AspNetCore.Mvc.Localization @using OrchardCore.Placements.ViewModels From fca56576fea55255ddd6a5d4196dbbf2275b57c0 Mon Sep 17 00:00:00 2001 From: Magnus Pettersson Date: Mon, 7 Dec 2020 07:30:52 +0100 Subject: [PATCH 2/7] Added admin options to dropdowns. Added options to edit admin placements to dropdowns on elements. --- .../Settings/PlacementContentPartDefinitionDriver.cs | 8 ++++++++ .../PlacementContentPartFieldDefinitionDisplayDriver.cs | 9 +++++++++ .../Settings/PlacementContentTypePartDefinitionDriver.cs | 9 +++++++++ 3 files changed, 26 insertions(+) diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartDefinitionDriver.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartDefinitionDriver.cs index ca8f044e0a4..f75624e1bf9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartDefinitionDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartDefinitionDriver.cs @@ -44,6 +44,14 @@ public override IDisplayResult Edit(ContentPartDefinition contentPartDefinition) DisplayType = "Summary", Description = S["Placement for a {0} part in summary views", displayName] }); + + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = $"{contentPartDefinition.Name}_Edit", + Description = S["Placement in admin editor for a {0} part", displayName] + }); + }).Location("Shortcuts"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartFieldDefinitionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartFieldDefinitionDisplayDriver.cs index 89e2932d10a..169dff85c0c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartFieldDefinitionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartFieldDefinitionDisplayDriver.cs @@ -49,6 +49,15 @@ public override IDisplayResult Edit(ContentPartFieldDefinition contentPartFieldD DisplayType = "Summary", Description = S["Placement for the {0} field in a {1} in summary views", displayName, partName] }); + + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = $"{shapeType}_Edit", + Differentiator = differentiator, + Description = S["Placement in admin editor for the {0} field in a {1}", displayName, partName] + }); + }).Location("Shortcuts"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentTypePartDefinitionDriver.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentTypePartDefinitionDriver.cs index 214350d9a47..276452b3fdb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentTypePartDefinitionDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentTypePartDefinitionDriver.cs @@ -48,6 +48,15 @@ public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDef DisplayType = "Summary", Description = S["Placement for the {0} part in a {1} type in summary views", partName, displayName] }); + + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = $"{partName}_Edit", + ContentType = contentType, + Description = S["Placement in admin editor for the {0} part in a {1} type", partName, displayName] + }); + }).Location("Shortcuts"); } } From cdb52ff1d8853b38b0fe699a48cd22fe7abada94 Mon Sep 17 00:00:00 2001 From: Magnus Pettersson Date: Mon, 7 Dec 2020 07:51:44 +0100 Subject: [PATCH 3/7] Enable cancel button when navigating from index view. --- .../OrchardCore.Placements/Views/Admin/Edit.cshtml | 5 +---- .../OrchardCore.Placements/Views/Admin/Index.cshtml | 6 +++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Edit.cshtml index 1781d2b8ba4..f467e0105ea 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Edit.cshtml @@ -46,10 +46,7 @@ @T["Delete"] } - @if (Url.IsLocalUrl(returnUrl)) - { - @T["Cancel"] - } + @T["Cancel"]
diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml index eaf0b934fd0..08dfd16f3a0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml @@ -67,10 +67,10 @@
} From 4bbd3bd8ab5c109ed248dc42c4e67c777c4e4a2a Mon Sep 17 00:00:00 2001 From: Magnus Pettersson Date: Mon, 7 Dec 2020 13:27:38 +0100 Subject: [PATCH 4/7] Put back IsEmpty check. --- .../Controllers/AdminController.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs index 123da38a8d5..fabdf56acfa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs @@ -176,6 +176,9 @@ public async Task Edit(EditShapePlacementViewModel viewModel, str { IEnumerable placementNodes = JsonConvert.DeserializeObject(viewModel.Nodes) ?? new PlacementNode[0]; + // Remove empty nodes + placementNodes = placementNodes.Where(node => !IsEmpty(node)); + if (placementNodes.Any()) { // Save @@ -288,6 +291,15 @@ private static bool ShouldCreateNode(IEnumerable nodes, string di } } + private static bool IsEmpty(PlacementNode node) + { + return string.IsNullOrEmpty(node.Location) + && string.IsNullOrEmpty(node.ShapeType) + && (node.Alternates == null || node.Alternates.Length == 0) + && (node.Wrappers == null || node.Wrappers.Length == 0); + } + + private static bool FilterEquals(JToken token, string value) { if (token is JArray) From a2a7bd1f8970e7d16be04fb4d3b7b695d142c3f3 Mon Sep 17 00:00:00 2001 From: Magnus Pettersson Date: Thu, 17 Dec 2020 13:06:21 +0100 Subject: [PATCH 5/7] Update search box. --- .../OrchardCore.Placements/Views/Admin/Index.cshtml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml index 08dfd16f3a0..503eb92574c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml @@ -15,21 +15,17 @@
-
-
- @T["Filter"] -
+
+
    @if (Model.ShapePlacements.Any()) { @@ -58,6 +54,7 @@
+ @foreach (var entry in Model.ShapePlacements) {
  • From 1d495dd0a3846cd7dd193906fb7fb7cb27734433 Mon Sep 17 00:00:00 2001 From: Jasmin Savard Date: Thu, 17 Dec 2020 09:58:29 -0500 Subject: [PATCH 6/7] Add submit.filter post action. Fix some typo and layout. --- .../Controllers/AdminController.cs | 10 ++++ .../Views/Admin/Index.cshtml | 51 +++++++++---------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs index 4e8281ab66a..d273e0d242a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Localization; using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using Newtonsoft.Json; @@ -97,6 +98,15 @@ public async Task Index(ContentOptions options, PagerParameters p return View("Index", model); } + [HttpPost, ActionName("Index")] + [FormValueRequired("submit.Filter")] + public ActionResult IndexFilterPOST(ListShapePlacementsViewModel model) + { + return RedirectToAction("Index", new RouteValueDictionary { + { "Options.Search", model.Options.Search } + }); + } + public async Task Create(string suggestion, string returnUrl = null) { if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml index 503eb92574c..3a50bcaaf14 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Views/Admin/Index.cshtml @@ -27,34 +27,33 @@
      - @if (Model.ShapePlacements.Any()) - { -
    • -
      -
      -
      - - - - -
      +
    • +
      +
      +
      + + + +
      - + +
      -
    • - + + + @if (Model.ShapePlacements.Any()) + { @foreach (var entry in Model.ShapePlacements) {
    • @@ -76,7 +75,7 @@ {
    • } From ebac24aca84f83ab1819f283cb3417a6260ac8f6 Mon Sep 17 00:00:00 2001 From: Jasmin Savard Date: Thu, 17 Dec 2020 10:00:19 -0500 Subject: [PATCH 7/7] Update src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs Co-authored-by: Dean Marcussen --- .../OrchardCore.Placements/Controllers/AdminController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs index d273e0d242a..e49253ae455 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs @@ -73,7 +73,7 @@ public async Task Index(ContentOptions options, PagerParameters p if (!string.IsNullOrWhiteSpace(options.Search)) { - shapeList = shapeList.Where(x => x.ShapeType.Contains(options.Search)).ToList(); + shapeList = shapeList.Where(x => x.ShapeType.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); } var count = shapeList.Count();