#8565: Delete and Unpublish buttons in backoffice content edit (#8566)
Some checks failed
Compile / Compile .NET solution (push) Has been cancelled
Compile / Compile Client-side Assets (push) Has been cancelled

* Added publish and delete buttons to backoffice content edit.

* Corrected condition for content unpublishing.
Managed unpublish and delete button for blogs and widgets.

* Some little code refactoring

* Added css classes for unpublish-button.
Condition for delete button visibility now checks for contentItem.Id > 0 instead of IsNew() function

* Removed UnpublishButton from Widget Editor.

* Removed Widget_DeleteButton to use the generic Content_DeleteButton shape and avoid its duplication or confusing management via Placement.info.

* Removed Widget_UnpublishButton shape

* Removed unpublish button show/hide logic from driver.
Added comments to clarify how new Unpublish and Delete actions work.

* EOL fix to Orchard.Widgets.csproj.

* Added unpublish button to edit widget (shown if widget is draftable only)

* Hide Content edit delete button at frontend

* ContentsDriver: removed AdminFilter check to display delete button and checked the content is published before showing the unpublish button.

* Applied some of the proposed formatting changes

* Several refactoring operations

* Autoformatting.

* Stylesheet refactoring and reorganization to properly position backoffice buttons

* Autoformatting

---------

Co-authored-by: Alessandro Agostini <alessandro.agostini@laser-group.com>
Co-authored-by: Matteo Piovanelli <matteo.piovanelli@laser-group.com>
This commit is contained in:
Andrea Piovanelli
2025-06-13 16:17:11 +02:00
committed by GitHub
parent d310fb56e1
commit b14c29503b
14 changed files with 1183 additions and 853 deletions

View File

@@ -352,6 +352,32 @@ namespace Orchard.Core.Contents.Controllers {
return EditPOST(id, returnUrl, contentItem => _contentManager.Publish(contentItem));
}
/// <summary>
/// This action is specific to the submit button of the edit form.
/// Unpublish logic is the same of the Unpublish action, which is called by the content list.
/// </summary>
/// <param name="id"></param>
/// <param name="returnUrl"></param>
/// <returns></returns>
[HttpPost, ActionName("Edit")]
[Mvc.FormValueRequired("submit.Unpublish")]
public ActionResult EditUnpublishPOST(int id, string returnUrl) {
return Unpublish(id, returnUrl);
}
/// <summary>
/// This action is specific to the submit button of the edit form.
/// Delete logic is the same of the Remove action, which is called by the content list.
/// </summary>
/// <param name="id"></param>
/// <param name="returnUrl"></param>
/// <returns></returns>
[HttpPost, ActionName("Edit")]
[Mvc.FormValueRequired("submit.Delete")]
public ActionResult EditDeletePOST(int id, string returnUrl) {
return Remove(id, returnUrl);
}
private ActionResult EditPOST(int id, string returnUrl, Action<ContentItem> conditionallyPublish) {
var contentItem = _contentManager.Get(id, VersionOptions.DraftRequired);

View File

@@ -21,8 +21,14 @@ namespace Orchard.Core.Contents.Drivers {
protected override DriverResult Editor(ContentPart part, dynamic shapeHelper) {
var results = new List<DriverResult> { ContentShape("Content_SaveButton", saveButton => saveButton) };
if (part.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable)
if (part.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable) {
results.Add(ContentShape("Content_PublishButton", publishButton => publishButton));
results.Add(ContentShape("Content_UnpublishButton", unpublishButton => unpublishButton));
}
if (part.Id > 0) {
results.Add(ContentShape("Content_DeleteButton", deleteButton => deleteButton));
}
return Combined(results.ToArray());
}

View File

@@ -6,8 +6,10 @@
Parts_Contents_Publish_SummaryAdmin
-->
<!-- edit "shape" -->
<Place Content_PublishButton="Sidebar:24"/>
<Place Content_SaveButton="Sidebar:23"/>
<Place Content_PublishButton="Sidebar:24"/>
<Place Content_UnpublishButton="Sidebar:25"/>
<Place Content_DeleteButton="Sidebar:26"/>
<Match DisplayType="Detail">
<Place Parts_Contents_Publish="Content:5"/>
</Match>

View File

@@ -0,0 +1,8 @@
@using Orchard.ContentManagement;
@using Orchard.Core.Contents;
@if (Authorizer.Authorize(Permissions.DeleteContent, (IContent)Model.ContentItem)) {
<fieldset class="delete-button">
<button type="submit" name="submit.Delete" value="submit.Delete" itemprop="RemoveUrl">@T("Delete")</button>
</fieldset>
}

View File

@@ -0,0 +1,12 @@
@using Orchard.ContentManagement;
@using Orchard.Core.Contents;
@{
var contentItem = Model.ContentItem as IContent;
}
@if (Authorizer.Authorize(Permissions.PublishContent, contentItem) && contentItem.IsPublished()) {
<fieldset class="unpublish-button">
<button type="submit" name="submit.Unpublish" value="submit.Unpublish">@T("Unpublish")</button>
</fieldset>
}

View File

@@ -612,12 +612,16 @@
<ItemGroup>
<Content Include="Navigation\Views\Admin\Edit.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Contents\Views\Content.UnpublishButton.cshtml" />
<Content Include="Contents\Views\Content.DeleteButton.cshtml" />
<None Include="packages.config" />
<Content Include="Title\Views\DefinitionTemplates\TitlePartSettings.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Containers\Styles\Web.config">
<SubType>Designer</SubType>
</Content>
<None Include="packages.config" />
<Content Include="Title\Views\DefinitionTemplates\TitlePartSettings.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>

View File

@@ -1,6 +1,5 @@
using System;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using Orchard.Blogs.Extensions;
using Orchard.Blogs.Models;
@@ -138,6 +137,12 @@ namespace Orchard.Blogs.Controllers {
});
}
[HttpPost, ActionName("Edit")]
[Mvc.FormValueRequired("submit.Delete")]
public ActionResult EditDeletePOST(int blogId, int postId, string returnUrl) {
return Delete(blogId, postId);
}
[HttpPost, ActionName("Edit")]
[FormValueRequired("submit.Publish")]
public ActionResult EditAndPublishPOST(int blogId, int postId, string returnUrl) {
@@ -156,6 +161,12 @@ namespace Orchard.Blogs.Controllers {
return EditPOST(blogId, postId, returnUrl, contentItem => Services.ContentManager.Publish(contentItem));
}
[HttpPost, ActionName("Edit")]
[Mvc.FormValueRequired("submit.Unpublish")]
public ActionResult EditUnpublishPOST(int blogId, int postId, string returnUrl) {
return Unpublish(blogId, postId);
}
public ActionResult EditPOST(int blogId, int postId, string returnUrl, Action<ContentItem> conditionallyPublish) {
var blog = _blogService.Get(blogId, VersionOptions.Latest);
if (blog == null)

View File

@@ -1101,17 +1101,17 @@ html.dyn #submit-pager, html.dyn .apply-bulk-actions-auto { display:none; }
padding:0;
}
fieldset.publish-button, fieldset.delete-button, fieldset.save-button {
fieldset.publish-button, fieldset.delete-button, fieldset.save-button, fieldset.unpublish-button {
clear:none;
float:left;
}
fieldset.save-button {
clear:left;
}
fieldset.publish-button {
fieldset.publish-button, fieldset.unpublish-button {
margin: 0 12px 0 0;
padding: 0 12px;
border-right:1px solid #ccc;
border-right: 1px solid #ccc;
}
fieldset.delete-button {
margin: 0 0 0 12px;

View File

@@ -33,15 +33,11 @@ namespace Orchard.Widgets.Drivers {
() => shapeHelper.EditorTemplate(TemplateName: "Parts.Widgets.LayerPart", Model: layerPart, Prefix: Prefix))
};
if (layerPart.Id > 0)
results.Add(ContentShape("Widget_DeleteButton",
deleteButton => deleteButton));
return Combined(results.ToArray());
}
protected override DriverResult Editor(LayerPart layerPart, IUpdateModel updater, dynamic shapeHelper) {
if(updater.TryUpdateModel(layerPart, Prefix, null, null)) {
if (updater.TryUpdateModel(layerPart, Prefix, null, null)) {
if (String.IsNullOrWhiteSpace(layerPart.LayerRule)) {
layerPart.LayerRule = "true";
}
@@ -55,8 +51,7 @@ namespace Orchard.Widgets.Drivers {
try {
_conditionManager.Matches(layerPart.LayerRule);
}
catch (Exception e) {
} catch (Exception e) {
updater.AddModelError("Description", T("The rule is not valid: {0}", e.Message));
}
}

View File

@@ -35,26 +35,22 @@ namespace Orchard.Widgets.Drivers {
() => shapeHelper.EditorTemplate(TemplateName: "Parts.Widgets.WidgetPart", Model: widgetPart, Prefix: Prefix))
};
if (widgetPart.Id > 0)
results.Add(ContentShape("Widget_DeleteButton",
deleteButton => deleteButton));
return Combined(results.ToArray());
}
protected override DriverResult Editor(WidgetPart widgetPart, IUpdateModel updater, dynamic shapeHelper) {
updater.TryUpdateModel(widgetPart, Prefix, null, null);
if(string.IsNullOrWhiteSpace(widgetPart.Title)) {
if (string.IsNullOrWhiteSpace(widgetPart.Title)) {
updater.AddModelError("Title", T("Title can't be empty."));
}
// if there is a name, ensure it's unique
if(!string.IsNullOrWhiteSpace(widgetPart.Name)) {
if (!string.IsNullOrWhiteSpace(widgetPart.Name)) {
widgetPart.Name = widgetPart.Name.ToHtmlName();
var widgets = _contentManager.Query<WidgetPart, WidgetPartRecord>().Where(x => x.Name == widgetPart.Name && x.Id != widgetPart.Id).Count();
if(widgets > 0) {
if (widgets > 0) {
updater.AddModelError("Name", T("A Widget with the same Name already exists."));
}
}

View File

@@ -207,9 +207,6 @@
<Content Include="Views\EditorTemplates\Parts.Widgets.LayerPart.cshtml" />
<Content Include="Views\EditorTemplates\Parts.Widgets.WidgetPart.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Widget.DeleteButton.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Web.config" />
</ItemGroup>

View File

@@ -1,5 +1,4 @@
<Placement>
<Place Widget_DeleteButton="Sidebar:25"/>
<Place Parts_Widgets_LayerPart="Content:1"/>
<Place Parts_Widgets_WidgetPart="Content:1"/>
<Place Parts_Widgets_WidegetBagPart="Content:5"/>

View File

@@ -1,3 +0,0 @@
<fieldset class="delete-button">
<button type="submit" name="submit.Delete" value="@T("Delete")" itemprop="RemoveUrl">@T("Delete")</button>
</fieldset>

File diff suppressed because it is too large Load Diff