Skip to content

Commit

Permalink
13715: Added edit button to the ContentPickerField Edit view (#13764)
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel Draghici <daniel.draghici@stellenanzeigen.de>
Co-authored-by: Mike Alhayek <mike@crestapps.com>
Co-authored-by: Zoltán Lehóczky <zoltan.lehoczky@lombiq.com>
  • Loading branch information
4 people committed Apr 19, 2024
1 parent 3777f07 commit 7a6f8da
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ function initVueMultiselect(element) {
if (element) {
var elementId = element.id;
var selectedItems = JSON.parse(element.dataset.selectedItems || "[]");
var editUrl = element.dataset.editUrl;
var viewUrl = element.dataset.viewUrl;
var searchUrl = element.dataset.searchUrl;
var multiple = JSON.parse(element.dataset.multiple);

Expand Down Expand Up @@ -99,6 +101,10 @@ function initVueMultiselect(element) {
// a single content item and we've just selected that one item.
this.searchBoxContainer.css("display", multiple ? "block" : "none");
},
url: function(item) {
var url = item.isEditable ? editUrl : viewUrl;
return url.replace('contentItemId', item.id);
},
remove: function (item) {
this.arrayOfItems.splice(this.arrayOfItems.indexOf(item), 1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,40 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using OrchardCore.Admin;
using OrchardCore.ContentFields.Settings;
using OrchardCore.ContentFields.ViewModels;
using OrchardCore.ContentManagement;
using OrchardCore.ContentManagement.Metadata;
using OrchardCore.ContentManagement.Metadata.Models;
using OrchardCore.Contents;

namespace OrchardCore.ContentFields.Controllers
{
[Admin]
public class ContentPickerAdminController : Controller
{
private readonly IContentDefinitionManager _contentDefinitionManager;
private readonly IContentManager _contentManager;
private readonly IEnumerable<IContentPickerResultProvider> _resultProviders;
private readonly IAuthorizationService _authorizationService;
private readonly IHttpContextAccessor _httpContextAccessor;

public ContentPickerAdminController(
IContentDefinitionManager contentDefinitionManager,
IEnumerable<IContentPickerResultProvider> resultProviders
)
IContentManager contentManager,
IEnumerable<IContentPickerResultProvider> resultProviders,
IAuthorizationService authorizationService,
IHttpContextAccessor httpContextAccessor)
{
_contentDefinitionManager = contentDefinitionManager;
_contentManager = contentManager;
_resultProviders = resultProviders;
_authorizationService = authorizationService;
_httpContextAccessor = httpContextAccessor;
}

[Admin("ContentFields/SearchContentItems", "ContentPicker")]
Expand Down Expand Up @@ -76,7 +87,23 @@ public async Task<IActionResult> SearchContentItems(string part, string field, s
PartFieldDefinition = partFieldDefinition,
});

return new ObjectResult(results.Select(r => new VueMultiselectItemViewModel() { Id = r.ContentItemId, DisplayText = r.DisplayText, HasPublished = r.HasPublished }));
var contentItems = await _contentManager
.GetAsync(results.Select(r => r.ContentItemId));

var selectedItems = new List<VueMultiselectItemViewModel>();
foreach (var contentItem in contentItems)
{
selectedItems.Add(new VueMultiselectItemViewModel()
{
Id = contentItem.ContentItemId,
DisplayText = contentItem.ToString(),
HasPublished = contentItem.IsPublished(),
IsViewable = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext?.User,
CommonPermissions.EditContent, contentItem)
});
}

return new ObjectResult(selectedItems);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.Threading.Tasks;
using Fluid;
using Fluid.Values;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using OrchardCore.ContentFields.Fields;
using OrchardCore.ContentFields.Settings;
Expand All @@ -12,6 +14,7 @@
using OrchardCore.ContentManagement.Display.ContentDisplay;
using OrchardCore.ContentManagement.Display.Models;
using OrchardCore.ContentManagement.Metadata.Models;
using OrchardCore.Contents;
using OrchardCore.DisplayManagement.ModelBinding;
using OrchardCore.DisplayManagement.Views;
using OrchardCore.Liquid;
Expand All @@ -25,15 +28,21 @@ public class ContentPickerFieldDisplayDriver : ContentFieldDisplayDriver<Content
private readonly IContentManager _contentManager;
private readonly ILiquidTemplateManager _templateManager;
protected readonly IStringLocalizer S;
private readonly IAuthorizationService _authorizationService;
private readonly IHttpContextAccessor _httpContextAccessor;

public ContentPickerFieldDisplayDriver(
IContentManager contentManager,
IStringLocalizer<ContentPickerFieldDisplayDriver> localizer,
ILiquidTemplateManager templateManager)
ILiquidTemplateManager templateManager,
IAuthorizationService authorizationService,
IHttpContextAccessor httpContextAccessor)
{
_contentManager = contentManager;
S = localizer;
_templateManager = templateManager;
_authorizationService = authorizationService;
_httpContextAccessor = httpContextAccessor;
}

public override IDisplayResult Display(ContentPickerField field, BuildFieldDisplayContext fieldDisplayContext)
Expand Down Expand Up @@ -79,9 +88,12 @@ public override IDisplayResult Edit(ContentPickerField field, BuildFieldEditorCo
Id = contentItemId,
DisplayText = await _templateManager.RenderStringAsync(settings.TitlePattern, NullEncoder.Default, contentItem,
new Dictionary<string, FluidValue>() { [nameof(ContentItem)] = new ObjectValue(contentItem) }),
HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem)
HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem),
IsEditable = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext!.User, CommonPermissions.EditContent, contentItem),
IsViewable = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext!.User, CommonPermissions.ViewContent, contentItem)
});
}
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ public class VueMultiselectItemViewModel
public string Id { get; set; }
public string DisplayText { get; set; }
public bool HasPublished { get; set; }
public bool IsViewable { get; set; }
public bool IsEditable { get; set; }
public bool IsClickable => IsEditable || IsViewable;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
var selectedItems = JConvert.SerializeObject(Model.SelectedItems, JOptions.CamelCase);
var partName = Model.PartFieldDefinition.PartDefinition.Name;
var fieldName = Model.PartFieldDefinition.Name;
var editUrl = Url.RouteUrl(new { area = "OrchardCore.Contents", controller = "Admin", action = "Edit", contentItemId = "contentItemId", returnUrl = FullRequestPath });
var viewUrl = Url.RouteUrl(new { area = "OrchardCore.Contents", controller = "Admin", action = "Display", contentItemId = "contentItemId" });
var searchUrl = Url.RouteUrl(new { area = "OrchardCore.ContentFields", controller = "ContentPickerAdmin", action = "SearchContentItems", part = partName, field = fieldName });
var vueElementId = $"ContentPicker_{partName}_{fieldName}_{Guid.NewGuid().ToString("n")}";
var multiple = settings.Multiple.ToString().ToLowerInvariant();
Expand All @@ -18,15 +20,21 @@
<div class="@Orchard.GetFieldWrapperClasses(Model.PartFieldDefinition)" id="@Html.IdFor(x => x.ContentItemIds)_FieldWrapper">
<label asp-for="ContentItemIds" class="@Orchard.GetLabelClasses(inputRequired: settings.Required)">@Model.PartFieldDefinition.DisplayName()</label>
<div class="@Orchard.GetEndClasses()">
<div id="@vueElementId" class="vue-multiselect" data-part="@partName" data-field="@fieldName" data-editor-type="ContentPicker" data-selected-items="@selectedItems" data-search-url="@searchUrl" data-multiple="@multiple">
<div id="@vueElementId" class="vue-multiselect" data-part="@partName" data-field="@fieldName" data-editor-type="ContentPicker" data-selected-items="@selectedItems" data-edit-url="@editUrl" data-view-url="@viewUrl" data-search-url="@searchUrl" data-multiple="@multiple">

<ul class="mb-1 list-group w-lg-75 w-xxl-50 content-picker-default__list" v-show="arrayOfItems.length" v-cloak>
<draggable v-model="arrayOfItems">
<li v-for="(item, i) in arrayOfItems"
class="cursor-move list-group-item content-picker-default__list-item d-flex align-items-start justify-content-between"
:key="item.id">
<div class="align-items-center align-self-center"><span>{{ item.displayText }}</span> <span v-show="!item.hasPublished" class="text-muted small">(@T["Not published"])</span></div>

<div class="align-items-center align-self-center">
<a v-if="item.isClickable" :href="url(item)" target="_blank">
<span>{{ item.displayText }}</span> <span v-show="!item.hasPublished" class="text-muted small">(@T["Not published"])</span>
</a>
<span v-else>
<span>{{ item.displayText }}</span> <span v-show="!item.hasPublished" class="text-muted small">(@T["Not published"])</span>
</span>
</div>
<div class="btn-group btn-group-sm align-items-center" role="group">
<button v-on:click="remove(item)" type="button" class="btn btn-secondary content-picker-default__list-item__delete"><i class="fa-solid fa-trash fa-sm" aria-hidden="true"></i></button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ function initVueMultiselect(element) {
if (element) {
var elementId = element.id;
var selectedItems = JSON.parse(element.dataset.selectedItems || "[]");
var editUrl = element.dataset.editUrl;
var viewUrl = element.dataset.viewUrl;
var searchUrl = element.dataset.searchUrl;
var multiple = JSON.parse(element.dataset.multiple);
var debouncedSearch = debounce(function (vm, query) {
Expand Down Expand Up @@ -107,6 +109,10 @@ function initVueMultiselect(element) {
// a single content item and we've just selected that one item.
this.searchBoxContainer.css("display", multiple ? "block" : "none");
},
url: function url(item) {
var url = item.isEditable ? editUrl : viewUrl;
return url.replace('contentItemId', item.id);
},
remove: function remove(item) {
this.arrayOfItems.splice(this.arrayOfItems.indexOf(item), 1);

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 7a6f8da

Please sign in to comment.