diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs
index 7dfeb716e3..93960d70c4 100644
--- a/src/Umbraco.Core/Models/UserExtensions.cs
+++ b/src/Umbraco.Core/Models/UserExtensions.cs
@@ -21,6 +21,8 @@ namespace Umbraco.Core.Models
internal static bool HasPathAccess(string path, int startNodeId, int recycleBinId)
{
+ Mandate.ParameterNotNullOrEmpty(path, "path");
+
var formattedPath = "," + path + ",";
var formattedStartNodeId = "," + startNodeId.ToInvariantString() + ",";
var formattedRecycleBinId = "," + recycleBinId.ToInvariantString() + ",";
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js
index e1fe4afe79..a7bf00954b 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js
@@ -564,6 +564,10 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
publish: function (content, isNew, files) {
return saveContentItem(content, "publish" + (isNew ? "New" : ""), files);
},
+
+ sendToPublish: function (content, isNew, files) {
+ return saveContentItem(content, "sendPublish" + (isNew ? "New" : ""), files);
+ },
publishById: function(id){
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js
index 98cec6bb6b..057a88721a 100644
--- a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js
@@ -73,7 +73,7 @@ function ContentEditController($scope, $routeParams, $q, $timeout, $window, cont
return {
letter: ch,
labelKey: "buttons_saveToPublish",
- handler: $scope.saveAndPublish,
+ handler: $scope.sendToPublish,
hotKey: "ctrl+t"
};
case "A":
@@ -96,6 +96,45 @@ function ContentEditController($scope, $routeParams, $q, $timeout, $window, cont
}
}
+ /** This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish */
+ function performSave(args) {
+ var deferred = $q.defer();
+
+ if (formHelper.submitForm({ scope: $scope, statusMessage: args.statusMessage })) {
+
+ args.saveMethod($scope.content, $routeParams.create, fileManager.getFiles())
+ .then(function (data) {
+
+ formHelper.resetForm({ scope: $scope, notifications: data.notifications });
+
+ contentEditingHelper.handleSuccessfulSave({
+ scope: $scope,
+ newContent: data,
+ rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data)
+ });
+
+ configureButtons(data);
+
+ navigationService.syncPath(data.path.split(","), true);
+
+ deferred.resolve(data);
+
+ }, function (err) {
+ contentEditingHelper.handleSaveError({
+ err: err,
+ allNewProps: contentEditingHelper.getAllProps(err.data),
+ allOrigProps: contentEditingHelper.getAllProps($scope.content)
+ });
+
+ deferred.reject(err);
+ });
+ }
+ else {
+ deferred.reject();
+ }
+
+ return deferred.promise;
+ }
if ($routeParams.create) {
//we are creating so get an empty content item
@@ -151,36 +190,16 @@ function ContentEditController($scope, $routeParams, $q, $timeout, $window, cont
};
+ $scope.sendToPublish = function() {
+ return performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending..." });
+ };
+
$scope.saveAndPublish = function() {
+ return performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing..." });
+ };
- if (formHelper.submitForm({ scope: $scope, statusMessage: "Publishing..." })) {
-
- contentResource.publish($scope.content, $routeParams.create, fileManager.getFiles())
- .then(function(data) {
-
- formHelper.resetForm({ scope: $scope, notifications: data.notifications });
-
- contentEditingHelper.handleSuccessfulSave({
- scope: $scope,
- newContent: data,
- rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data)
- });
-
- configureButtons(data);
-
- navigationService.syncPath(data.path.split(","), true);
-
- }, function(err) {
-
- contentEditingHelper.handleSaveError({
- err: err,
- redirectOnFailure: true,
- allNewProps: contentEditingHelper.getAllProps(err.data),
- rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, err.data)
- });
- });
- }
-
+ $scope.save = function () {
+ return performSave({ saveMethod: contentResource.save, statusMessage: "Saving..." });
};
$scope.preview = function(content){
@@ -192,46 +211,7 @@ function ContentEditController($scope, $routeParams, $q, $timeout, $window, cont
$window.open('dialogs/preview.aspx?id='+content.id,'umbpreview');
}
};
-
- $scope.save = function() {
- var deferred = $q.defer();
-
- if (formHelper.submitForm({ scope: $scope, statusMessage: "Saving..." })) {
-
- contentResource.save($scope.content, $routeParams.create, fileManager.getFiles())
- .then(function(data) {
-
- formHelper.resetForm({ scope: $scope, notifications: data.notifications });
-
- contentEditingHelper.handleSuccessfulSave({
- scope: $scope,
- newContent: data,
- rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data)
- });
-
- configureButtons(data);
-
- //fetch tree
- navigationService.syncPath(data.path.split(","), true);
-
- deferred.resolve(data);
- }, function(err) {
- contentEditingHelper.handleSaveError({
- err: err,
- allNewProps: contentEditingHelper.getAllProps(err.data),
- allOrigProps: contentEditingHelper.getAllProps($scope.content)
- });
-
- deferred.reject(err);
- });
- }
- else {
- deferred.reject();
- }
-
- return deferred.promise;
- };
-
+
/** this method is called for all action buttons and then we proxy based on the btn definition */
$scope.performAction = function(btn) {
if (!btn || !angular.isFunction(btn.handler)) {
diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs
index 961a452180..06cf0ec417 100644
--- a/src/Umbraco.Web/Editors/ContentController.cs
+++ b/src/Umbraco.Web/Editors/ContentController.cs
@@ -209,10 +209,9 @@ namespace Umbraco.Web.Editors
// then we cannot continue saving, we can only display errors
// * If there are validation errors and they were attempting to publish, we can only save, NOT publish and display
// a message indicating this
- if (!ModelState.IsValid)
+ if (ModelState.IsValid == false)
{
- if (ValidationHelper.ModelHasRequiredForPersistenceErrors(contentItem)
- && (contentItem.Action == ContentSaveAction.SaveNew || contentItem.Action == ContentSaveAction.PublishNew))
+ if (ValidationHelper.ModelHasRequiredForPersistenceErrors(contentItem) && IsCreatingAction(contentItem.Action))
{
//ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
// add the modelstate to the outgoing object and throw a validation message
@@ -242,6 +241,10 @@ namespace Umbraco.Web.Editors
//save the item
Services.ContentService.Save(contentItem.PersistedContent, (int)Security.CurrentUser.Id);
}
+ else if (contentItem.Action == ContentSaveAction.SendPublish || contentItem.Action == ContentSaveAction.SendPublishNew)
+ {
+ throw new NotSupportedException("Send to publish is currently not supported");
+ }
else
{
//publish the item and check if it worked, if not we will show a diff msg below
@@ -262,9 +265,13 @@ namespace Umbraco.Web.Editors
case ContentSaveAction.SaveNew:
display.AddSuccessNotification(ui.Text("speechBubbles", "editContentSavedHeader"), ui.Text("speechBubbles", "editContentSavedText"));
break;
+ case ContentSaveAction.SendPublish:
+ case ContentSaveAction.SendPublishNew:
+ display.AddSuccessNotification(ui.Text("speechBubbles", "editContentSendToPublish"), ui.Text("speechBubbles", "editContentSendToPublishText"));
+ break;
case ContentSaveAction.Publish:
case ContentSaveAction.PublishNew:
- ShowMessageForStatus(publishStatus.Result, display);
+ ShowMessageForPublishStatus(publishStatus.Result, display);
break;
}
@@ -553,7 +560,7 @@ namespace Umbraco.Web.Editors
return toMove;
}
- private void ShowMessageForStatus(PublishStatus status, ContentItemDisplay display)
+ private void ShowMessageForPublishStatus(PublishStatus status, ContentItemDisplay display)
{
switch (status.StatusType)
{
diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs
index 4d8add3866..77125cc4b9 100644
--- a/src/Umbraco.Web/Editors/ContentControllerBase.cs
+++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs
@@ -159,5 +159,15 @@ namespace Umbraco.Web.Editors
: (TPersisted) Request.Properties[typeof (TPersisted).ToString()];
}
+ ///
+ /// Returns true if the action passed in means we need to create something new
+ ///
+ ///
+ ///
+ internal static bool IsCreatingAction(ContentSaveAction action)
+ {
+ return (action.ToString().EndsWith("New"));
+ }
+
}
}
diff --git a/src/Umbraco.Web/Editors/ContentPostValidateAttribute.cs b/src/Umbraco.Web/Editors/ContentPostValidateAttribute.cs
index 24b684d9f7..62898e7889 100644
--- a/src/Umbraco.Web/Editors/ContentPostValidateAttribute.cs
+++ b/src/Umbraco.Web/Editors/ContentPostValidateAttribute.cs
@@ -52,6 +52,11 @@ namespace Umbraco.Web.Editors
get { return _userService ?? ApplicationContext.Current.Services.UserService; }
}
+ public override bool AllowMultiple
+ {
+ get { return true; }
+ }
+
public override void OnActionExecuting(HttpActionContext actionContext)
{
var contentItem = (ContentItemSave)actionContext.ActionArguments["contentItem"];
@@ -75,8 +80,31 @@ namespace Umbraco.Web.Editors
contentToCheck = contentItem.PersistedContent;
contentIdToCheck = contentToCheck.Id;
break;
+ case ContentSaveAction.SendPublish:
+ permissionToCheck.Add(ActionToPublish.Instance.Letter);
+ contentToCheck = contentItem.PersistedContent;
+ contentIdToCheck = contentToCheck.Id;
+ break;
case ContentSaveAction.SaveNew:
+ //Save new requires both ActionNew AND ActionUpdate
+
permissionToCheck.Add(ActionNew.Instance.Letter);
+ permissionToCheck.Add(ActionUpdate.Instance.Letter);
+ if (contentItem.ParentId != Constants.System.Root)
+ {
+ contentToCheck = ContentService.GetById(contentItem.ParentId);
+ contentIdToCheck = contentToCheck.Id;
+ }
+ else
+ {
+ contentIdToCheck = contentItem.ParentId;
+ }
+ break;
+ case ContentSaveAction.SendPublishNew:
+ //Send new requires both ActionToPublish AND ActionUpdate
+
+ permissionToCheck.Add(ActionNew.Instance.Letter);
+ permissionToCheck.Add(ActionToPublish.Instance.Letter);
if (contentItem.ParentId != Constants.System.Root)
{
contentToCheck = ContentService.GetById(contentItem.ParentId);
diff --git a/src/Umbraco.Web/Editors/MediaPostValidateAttribute.cs b/src/Umbraco.Web/Editors/MediaPostValidateAttribute.cs
index 31a581eac2..ed26fcbc57 100644
--- a/src/Umbraco.Web/Editors/MediaPostValidateAttribute.cs
+++ b/src/Umbraco.Web/Editors/MediaPostValidateAttribute.cs
@@ -71,8 +71,6 @@ namespace Umbraco.Web.Editors
}
break;
- case ContentSaveAction.Publish:
- case ContentSaveAction.PublishNew:
default:
//we don't support this for media
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.NotFound);
diff --git a/src/Umbraco.Web/Editors/MembershipProviderValidationFilterAttribute.cs b/src/Umbraco.Web/Editors/MembershipProviderValidationFilterAttribute.cs
index f25a833b7d..714074861f 100644
--- a/src/Umbraco.Web/Editors/MembershipProviderValidationFilterAttribute.cs
+++ b/src/Umbraco.Web/Editors/MembershipProviderValidationFilterAttribute.cs
@@ -74,8 +74,6 @@ namespace Umbraco.Web.Editors
return false;
}
break;
- case ContentSaveAction.Publish:
- case ContentSaveAction.PublishNew:
default:
//we don't support this for members
throw new HttpResponseException(HttpStatusCode.NotFound);
@@ -120,8 +118,6 @@ namespace Umbraco.Web.Editors
return false;
}
break;
- case ContentSaveAction.Publish:
- case ContentSaveAction.PublishNew:
default:
//we don't support this for members
throw new HttpResponseException(HttpStatusCode.NotFound);
diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentSaveAction.cs b/src/Umbraco.Web/Models/ContentEditing/ContentSaveAction.cs
index 337c97e12b..ed54588ccc 100644
--- a/src/Umbraco.Web/Models/ContentEditing/ContentSaveAction.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/ContentSaveAction.cs
@@ -11,7 +11,7 @@
Save = 0,
///
- /// Saves a new content item
+ /// Creates a new content item
///
SaveNew = 1,
@@ -21,8 +21,18 @@
Publish = 2,
///
- /// Saves an publishes a new content item
+ /// Creates and publishes a new content item
///
- PublishNew = 3
+ PublishNew = 3,
+
+ ///
+ /// Saves and sends publish notification
+ ///
+ SendPublish = 4,
+
+ ///
+ /// Creates and sends publish notification
+ ///
+ SendPublishNew = 5
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/WebApi/Binders/ContentItemBaseBinder.cs b/src/Umbraco.Web/WebApi/Binders/ContentItemBaseBinder.cs
index 6e415b0d43..df9a76cda8 100644
--- a/src/Umbraco.Web/WebApi/Binders/ContentItemBaseBinder.cs
+++ b/src/Umbraco.Web/WebApi/Binders/ContentItemBaseBinder.cs
@@ -18,6 +18,7 @@ using Newtonsoft.Json.Serialization;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
+using Umbraco.Web.Editors;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Security;
using Umbraco.Web.WebApi.Filters;
@@ -163,16 +164,16 @@ namespace Umbraco.Web.WebApi.Binders
});
}
- if (model.Action == ContentSaveAction.Publish || model.Action == ContentSaveAction.Save)
- {
- //finally, let's lookup the real content item and create the DTO item
- model.PersistedContent = GetExisting(model);
- }
- else
+ if (ContentControllerBase.IsCreatingAction(model.Action))
{
//we are creating new content
model.PersistedContent = CreateNew(model);
}
+ else
+ {
+ //finally, let's lookup the real content item and create the DTO item
+ model.PersistedContent = GetExisting(model);
+ }
//create the dto from the persisted model
if (model.PersistedContent != null)