Improved core content editing and publishing UI. Fixes #6388.

This commit is contained in:
Daniel Stolt 2017-03-04 09:36:06 +01:00
parent 162cfd2b67
commit 332a2676dd
11 changed files with 122 additions and 98 deletions

View File

@ -265,10 +265,7 @@ namespace Orchard.Core.Contents.Controllers {
[HttpPost, ActionName("Create")] [HttpPost, ActionName("Create")]
[Mvc.FormValueRequired("submit.Save")] [Mvc.FormValueRequired("submit.Save")]
public ActionResult CreatePOST(string id, string returnUrl) { public ActionResult CreatePOST(string id, string returnUrl) {
return CreatePOST(id, returnUrl, contentItem => { return CreatePOST(id, returnUrl, contentItem => { return false; });
if (!contentItem.Has<IPublishingControlAspect>() && !contentItem.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable)
_contentManager.Publish(contentItem);
});
} }
[HttpPost, ActionName("Create")] [HttpPost, ActionName("Create")]
@ -278,16 +275,19 @@ namespace Orchard.Core.Contents.Controllers {
// pass a dummy content to the authorization check to check for "own" variations // pass a dummy content to the authorization check to check for "own" variations
var dummyContent = _contentManager.New(id); var dummyContent = _contentManager.New(id);
if (!Services.Authorizer.Authorize(Permissions.PublishContent, dummyContent, T("Couldn't create content"))) if (!Services.Authorizer.Authorize(Permissions.PublishContent, dummyContent, T("You do not have permission to publish content.")))
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
return CreatePOST(id, returnUrl, contentItem => _contentManager.Publish(contentItem)); return CreatePOST(id, returnUrl, contentItem => {
_contentManager.Publish(contentItem);
return true;
});
} }
private ActionResult CreatePOST(string id, string returnUrl, Action<ContentItem> conditionallyPublish) { private ActionResult CreatePOST(string id, string returnUrl, Func<ContentItem, bool> conditionallyPublish) {
var contentItem = _contentManager.New(id); var contentItem = _contentManager.New(id);
if (!Services.Authorizer.Authorize(Permissions.EditContent, contentItem, T("Couldn't create content"))) if (!Services.Authorizer.Authorize(Permissions.EditContent, contentItem, T("You do not have permission to edit content.")))
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
_contentManager.Create(contentItem, VersionOptions.Draft); _contentManager.Create(contentItem, VersionOptions.Draft);
@ -299,14 +299,23 @@ namespace Orchard.Core.Contents.Controllers {
return View(model); return View(model);
} }
conditionallyPublish(contentItem); var contentWasPublished = conditionallyPublish(contentItem);
if (contentWasPublished) {
Services.Notifier.Success(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName)
? T("The content has been created and published.")
: T("The {0} has been created and published.", contentItem.TypeDefinition.DisplayName));
}
else {
Services.Notifier.Success(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName)
? T("The content has been created as a draft.")
: T("The {0} has been created as a draft.", contentItem.TypeDefinition.DisplayName));
}
Services.Notifier.Success(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName)
? T("Your content has been created.")
: T("Your {0} has been created.", contentItem.TypeDefinition.DisplayName));
if (!string.IsNullOrEmpty(returnUrl)) { if (!string.IsNullOrEmpty(returnUrl)) {
return this.RedirectLocal(returnUrl); return this.RedirectLocal(returnUrl);
} }
var adminRouteValues = _contentManager.GetItemMetadata(contentItem).AdminRouteValues; var adminRouteValues = _contentManager.GetItemMetadata(contentItem).AdminRouteValues;
return RedirectToRoute(adminRouteValues); return RedirectToRoute(adminRouteValues);
} }
@ -317,7 +326,7 @@ namespace Orchard.Core.Contents.Controllers {
if (contentItem == null) if (contentItem == null)
return HttpNotFound(); return HttpNotFound();
if (!Services.Authorizer.Authorize(Permissions.EditContent, contentItem, T("Cannot edit content"))) if (!Services.Authorizer.Authorize(Permissions.EditContent, contentItem, T("You do not have permission to edit content.")))
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
var model = _contentManager.BuildEditor(contentItem); var model = _contentManager.BuildEditor(contentItem);
@ -327,10 +336,7 @@ namespace Orchard.Core.Contents.Controllers {
[HttpPost, ActionName("Edit")] [HttpPost, ActionName("Edit")]
[Mvc.FormValueRequired("submit.Save")] [Mvc.FormValueRequired("submit.Save")]
public ActionResult EditPOST(int id, string returnUrl) { public ActionResult EditPOST(int id, string returnUrl) {
return EditPOST(id, returnUrl, contentItem => { return EditPOST(id, returnUrl, contentItem => { return false; });
if (!contentItem.Has<IPublishingControlAspect>() && !contentItem.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable)
_contentManager.Publish(contentItem);
});
} }
[HttpPost, ActionName("Edit")] [HttpPost, ActionName("Edit")]
@ -341,19 +347,22 @@ namespace Orchard.Core.Contents.Controllers {
if (content == null) if (content == null)
return HttpNotFound(); return HttpNotFound();
if (!Services.Authorizer.Authorize(Permissions.PublishContent, content, T("Couldn't publish content"))) if (!Services.Authorizer.Authorize(Permissions.PublishContent, content, T("You do not have permission to publish content.")))
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
return EditPOST(id, returnUrl, contentItem => _contentManager.Publish(contentItem)); return EditPOST(id, returnUrl, contentItem => {
_contentManager.Publish(contentItem);
return true;
});
} }
private ActionResult EditPOST(int id, string returnUrl, Action<ContentItem> conditionallyPublish) { private ActionResult EditPOST(int id, string returnUrl, Func<ContentItem, bool> conditionallyPublish) {
var contentItem = _contentManager.Get(id, VersionOptions.DraftRequired); var contentItem = _contentManager.Get(id, VersionOptions.DraftRequired);
if (contentItem == null) if (contentItem == null)
return HttpNotFound(); return HttpNotFound();
if (!Services.Authorizer.Authorize(Permissions.EditContent, contentItem, T("Couldn't edit content"))) if (!Services.Authorizer.Authorize(Permissions.EditContent, contentItem, T("You do not have permission to edit content.")))
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
string previousRoute = null; string previousRoute = null;
@ -372,7 +381,7 @@ namespace Orchard.Core.Contents.Controllers {
return View("Edit", model); return View("Edit", model);
} }
conditionallyPublish(contentItem); var contentWasPublished = conditionallyPublish(contentItem);
if (!string.IsNullOrWhiteSpace(returnUrl) if (!string.IsNullOrWhiteSpace(returnUrl)
&& previousRoute != null && previousRoute != null
@ -380,9 +389,16 @@ namespace Orchard.Core.Contents.Controllers {
returnUrl = Url.ItemDisplayUrl(contentItem); returnUrl = Url.ItemDisplayUrl(contentItem);
} }
Services.Notifier.Success(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName) if (contentWasPublished) {
? T("Your content has been saved.") Services.Notifier.Success(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName)
: T("Your {0} has been saved.", contentItem.TypeDefinition.DisplayName)); ? T("The content has been published.")
: T("The {0} has been published.", contentItem.TypeDefinition.DisplayName));
}
else {
Services.Notifier.Success(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName)
? T("The content has been saved as a draft.")
: T("The {0} has been saved as a draft.", contentItem.TypeDefinition.DisplayName));
}
return this.RedirectLocal(returnUrl, () => RedirectToAction("Edit", new RouteValueDictionary { { "Id", contentItem.Id } })); return this.RedirectLocal(returnUrl, () => RedirectToAction("Edit", new RouteValueDictionary { { "Id", contentItem.Id } }));
} }
@ -391,18 +407,20 @@ namespace Orchard.Core.Contents.Controllers {
public ActionResult Clone(int id) { public ActionResult Clone(int id) {
var originalContentItem = _contentManager.GetLatest(id); var originalContentItem = _contentManager.GetLatest(id);
if (!Services.Authorizer.Authorize(Permissions.ViewContent, originalContentItem, T("Couldn't open original content"))) if (!Services.Authorizer.Authorize(Permissions.ViewContent, originalContentItem, T("You do not have permission to view existing content.")))
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
// pass a dummy content to the authorization check to check for "own" variations // pass a dummy content to the authorization check to check for "own" variations
var dummyContent = _contentManager.New(originalContentItem.ContentType); var dummyContent = _contentManager.New(originalContentItem.ContentType);
if (!Services.Authorizer.Authorize(Permissions.EditContent, dummyContent, T("Couldn't create clone content"))) if (!Services.Authorizer.Authorize(Permissions.EditContent, dummyContent, T("You do not have permission to edit (or create) content.")))
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
var cloneContentItem = _contentManager.Clone(originalContentItem); var cloneContentItem = _contentManager.Clone(originalContentItem);
Services.Notifier.Success(T("Successfully cloned. The clone was saved as a draft.")); Services.Notifier.Success(string.IsNullOrWhiteSpace(originalContentItem.TypeDefinition.DisplayName)
? T("The content has been cloned as a draft.")
: T("The {0} has been cloned as a draft.", originalContentItem.TypeDefinition.DisplayName));
var adminRouteValues = _contentManager.GetItemMetadata(cloneContentItem).AdminRouteValues; var adminRouteValues = _contentManager.GetItemMetadata(cloneContentItem).AdminRouteValues;
return RedirectToRoute(adminRouteValues); return RedirectToRoute(adminRouteValues);
@ -412,7 +430,7 @@ namespace Orchard.Core.Contents.Controllers {
public ActionResult Remove(int id, string returnUrl) { public ActionResult Remove(int id, string returnUrl) {
var contentItem = _contentManager.Get(id, VersionOptions.Latest); var contentItem = _contentManager.Get(id, VersionOptions.Latest);
if (!Services.Authorizer.Authorize(Permissions.DeleteContent, contentItem, T("Couldn't remove content"))) if (!Services.Authorizer.Authorize(Permissions.DeleteContent, contentItem, T("You do not have permission to delete content.")))
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
if (contentItem != null) { if (contentItem != null) {
@ -439,15 +457,15 @@ namespace Orchard.Core.Contents.Controllers {
return new HttpStatusCodeResult(HttpStatusCode.BadRequest); return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
} }
if (!Services.Authorizer.Authorize(Permissions.DeleteContent, contentItem, T("Couldn't remove draft"))) { if (!Services.Authorizer.Authorize(Permissions.DeleteContent, contentItem, T("You do not have permission to delete content (or discard draft content)."))) {
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
} }
_contentManager.DiscardDraft(contentItem); _contentManager.DiscardDraft(contentItem);
Services.Notifier.Information(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName) Services.Notifier.Information(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName)
? T("That draft has been removed.") ? T("The draft content has been removed.")
: T("That {0} draft has been removed.", contentItem.TypeDefinition.DisplayName)); : T("The draft {0} has been removed.", contentItem.TypeDefinition.DisplayName));
return this.RedirectLocal(returnUrl, () => RedirectToAction("List")); return this.RedirectLocal(returnUrl, () => RedirectToAction("List"));
} }
@ -458,12 +476,14 @@ namespace Orchard.Core.Contents.Controllers {
if (contentItem == null) if (contentItem == null)
return HttpNotFound(); return HttpNotFound();
if (!Services.Authorizer.Authorize(Permissions.PublishContent, contentItem, T("Couldn't publish content"))) if (!Services.Authorizer.Authorize(Permissions.PublishContent, contentItem, T("You do not have permission to publish content.")))
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
_contentManager.Publish(contentItem); _contentManager.Publish(contentItem);
Services.Notifier.Success(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName) ? T("The content has been published.") : T("The {0} has been published.", contentItem.TypeDefinition.DisplayName)); Services.Notifier.Information(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName)
? T("The content has been published.")
: T("The {0} has been published.", contentItem.TypeDefinition.DisplayName));
return this.RedirectLocal(returnUrl, () => RedirectToAction("List")); return this.RedirectLocal(returnUrl, () => RedirectToAction("List"));
} }
@ -474,12 +494,14 @@ namespace Orchard.Core.Contents.Controllers {
if (contentItem == null) if (contentItem == null)
return HttpNotFound(); return HttpNotFound();
if (!Services.Authorizer.Authorize(Permissions.PublishContent, contentItem, T("Couldn't unpublish content"))) if (!Services.Authorizer.Authorize(Permissions.PublishContent, contentItem, T("You do not have permission to publish (or unpublish) content.")))
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
_contentManager.Unpublish(contentItem); _contentManager.Unpublish(contentItem);
Services.Notifier.Success(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName) ? T("The content has been unpublished.") : T("The {0} has been unpublished.", contentItem.TypeDefinition.DisplayName)); Services.Notifier.Information(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName)
? T("The content has been unpublished.")
: T("The {0} has been unpublished.", contentItem.TypeDefinition.DisplayName));
return this.RedirectLocal(returnUrl, () => RedirectToAction("List")); return this.RedirectLocal(returnUrl, () => RedirectToAction("List"));
} }

View File

@ -17,10 +17,13 @@ namespace Orchard.Core.Contents.Drivers {
} }
protected override DriverResult Editor(ContentPart part, dynamic shapeHelper) { protected override DriverResult Editor(ContentPart part, dynamic shapeHelper) {
var results = new List<DriverResult> { ContentShape("Content_SaveButton", saveButton => saveButton) }; var results = new List<DriverResult>();
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_SaveButton", saveButton => saveButton));
results.Add(ContentShape("Content_PublishButton", publishButton => publishButton));
results.Add(ContentShape("Content_CancelButton", cancelButton => cancelButton));
return Combined(results.ToArray()); return Combined(results.ToArray());
} }

View File

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

View File

@ -0,0 +1,9 @@
@using Orchard.Utility.Extensions
<fieldset class="cancel-button">
@{
var returnUrl = Request.QueryString["returnUrl"];
}
@if (!String.IsNullOrWhiteSpace(returnUrl) && Request.IsLocalUrl(returnUrl)) {
<a id="button-cancel" href="@returnUrl" class="button">@T("Cancel")</a>
}
</fieldset>

View File

@ -1,9 +1,8 @@
@using Orchard.ContentManagement; @using Orchard.ContentManagement;
@using Orchard.Core.Contents; @using Orchard.Core.Contents;
@using Orchard.Utility.Extensions;
@if (Authorizer.Authorize(Permissions.PublishContent, (IContent)Model.ContentItem)) { @if (Authorizer.Authorize(Permissions.PublishContent, (IContent)Model.ContentItem)) {
<fieldset class="publish-button"> <fieldset class="publish-button">
<button type="submit" name="submit.Publish" value="submit.Publish">@T("Publish Now")</button> <button class="primaryAction" type="submit" name="submit.Publish" value="submit.Publish">@T("Publish")</button>
</fieldset> </fieldset>
} }

View File

@ -1,12 +1,3 @@
@using Orchard.Utility.Extensions <fieldset class="save-button">
<fieldset class="save-button"> <button class="primaryAction" type="submit" name="submit.Save" value="submit.Save">@T("Save Draft")</button>
<button class="primaryAction" type="submit" name="submit.Save" value="submit.Save">@T("Save")</button>
@{
var returnUrl = Request.QueryString["returnUrl"];
}
@if (!String.IsNullOrWhiteSpace(returnUrl) && Request.IsLocalUrl(returnUrl)) {
<a id="button-cancel" href="@returnUrl" class="button">@T("Cancel")</a>
}
</fieldset> </fieldset>

View File

@ -394,7 +394,9 @@
<ItemGroup> <ItemGroup>
<Content Include="Common\Views\Parts.Common.Body.Summary.cshtml" /> <Content Include="Common\Views\Parts.Common.Body.Summary.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup>
<Content Include="Contents\Views\Content.CancelButton.cshtml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Contents\Views\Content.cshtml" /> <Content Include="Contents\Views\Content.cshtml" />
<Content Include="Contents\Views\Content.SummaryAdmin.cshtml" /> <Content Include="Contents\Views\Content.SummaryAdmin.cshtml" />

View File

@ -105,7 +105,7 @@ namespace Orchard.PublishLater.Drivers {
} }
} }
else { else {
updater.AddModelError(Prefix, T("Both the date and time need to be specified for when this is to be published. If you don't want to schedule publishing then click Save or Publish Now.")); updater.AddModelError(Prefix, T("Both the date and time need to be specified for when this is to be published. If you don't want to schedule publishing then click Save Draft or Publish Now."));
} }
} }

View File

@ -7,7 +7,7 @@
--> -->
<!-- edit shape just get default placement --> <!-- edit shape just get default placement -->
<!-- edit "shape" --> <!-- edit "shape" -->
<Place Parts_PublishLater_Edit="Sidebar:25"/><!-- immediately following the contents module's Publish Now button --> <Place Parts_PublishLater_Edit="Sidebar:25"/>
<!-- default positioning --> <!-- default positioning -->
<Match DisplayType="SummaryAdmin"> <Match DisplayType="SummaryAdmin">
<Place Parts_PublishLater_Metadata_SummaryAdmin="Meta:1"/> <Place Parts_PublishLater_Metadata_SummaryAdmin="Meta:1"/>

View File

@ -1,40 +1,38 @@
@model Orchard.PublishLater.ViewModels.PublishLaterViewModel @model Orchard.PublishLater.ViewModels.PublishLaterViewModel
@using Orchard.ContentManagement;
@using Orchard.Core.Contents; @using Orchard.Core.Contents;
@using Orchard.Utility.Extensions;
@if (Authorizer.Authorize(Permissions.PublishContent, Model.ContentItem)) { @if (Authorizer.Authorize(Permissions.PublishContent, Model.ContentItem)) {
<style type="text/css"> using (Script.Head()) {
/* TODO: Extract to resource (Decorum). */ <style type="text/css">
/* TODO: Extract to resource (Decorum). */
.publish-later-datetime { .publish-later-datetime {
float: left; white-space: nowrap;
clear: none; vertical-align: middle;
white-space: nowrap; }
vertical-align: middle;
}
.publish-later-datetime legend { .publish-later-datetime legend {
display: none; display: none;
} }
.publish-later-datetime button { .publish-later-datetime button {
margin-left: 4px; margin-left: 4px;
} }
/* RTL */ /* RTL */
.dir-rtl .publish-later-datetime { .dir-rtl .publish-later-datetime {
float: right; float: right;
} }
.dir-rtl .publish-later-datetime button {
margin-left: inherit;
margin-right: 4px;
}
</style>
}
.dir-rtl .publish-later-datetime button {
margin-left: inherit;
margin-right: 4px;
}
</style>
<fieldset class="publish-later-datetime"> <fieldset class="publish-later-datetime">
<legend>@T("Publish")</legend> <legend>@T("Publish")</legend>
@Html.HiddenFor(m => m.Editor.ShowDate) @Html.HiddenFor(m => m.Editor.ShowDate)

View File

@ -1157,25 +1157,24 @@ html.dyn #submit-pager, html.dyn .apply-bulk-actions-auto { display:none; }
} }
/* Core Contents and Orchard.PublishLater */ /* Core Contents and Orchard.PublishLater */
.edit-item-sidebar fieldset { .edit-item-secondary {
margin:0; margin-top: 2em;
padding:0;
} }
fieldset.publish-button, fieldset.delete-button, fieldset.save-button { .edit-item-sidebar fieldset {
clear:none; float: left;
float:left; clear: none;
margin: 0;
padding: 0;
} }
fieldset.save-button {
clear:left; .edit-item-sidebar fieldset + fieldset {
margin-left: 12px;
} }
fieldset.publish-button {
margin: 0 12px 0 0; fieldset.cancel-button {
padding: 0 12px; padding-left: 12px;
border-right:1px solid #ccc; border-left: 1px solid #ccc;
}
fieldset.delete-button {
margin: 0 0 0 12px;
} }
/* Dashboard */ /* Dashboard */