diff --git a/src/Umbraco.Core/Models/ContentEditing/PublishContent.cs b/src/Umbraco.Core/Models/ContentEditing/PublishContent.cs
new file mode 100644
index 0000000000..ba6d8d2f6f
--- /dev/null
+++ b/src/Umbraco.Core/Models/ContentEditing/PublishContent.cs
@@ -0,0 +1,16 @@
+using System.Runtime.Serialization;
+
+namespace Umbraco.Cms.Core.Models.ContentEditing;
+
+///
+/// Used to publish content and variants
+///
+[DataContract(Name = "publish", Namespace = "")]
+public class PublishContent
+{
+ [DataMember(Name = "id")]
+ public int Id { get; set; }
+
+ [DataMember(Name = "cultures")]
+ public string[]? Cultures { get; set; }
+}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
index c9bc07457a..944068114a 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
@@ -168,8 +168,8 @@ public class ContentController : ContentControllerBase
authorizationService,
contentVersionService,
StaticServiceProvider.Instance.GetRequiredService())
- {
- }
+ {
+ }
public object? Domains { get; private set; }
@@ -1950,6 +1950,7 @@ public class ContentController : ContentControllerBase
}
PublishResult publishResult = _contentService.SaveAndPublish(foundContent, userId: _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().Result ?? 0);
+
if (publishResult.Success == false)
{
var notificationModel = new SimpleNotificationModel();
@@ -1960,6 +1961,56 @@ public class ContentController : ContentControllerBase
return Ok();
}
+ ///
+ /// Publishes a document with a given ID and cultures.
+ ///
+ ///
+ ///
+ ///
+ /// The EnsureUserPermissionForContent attribute will deny access to this method if the current user
+ /// does not have Publish access to this node.
+ ///
+ [Authorize(Policy = AuthorizationPolicies.ContentPermissionPublishById)]
+ public IActionResult PostPublishByIdAndCulture(PublishContent model)
+ {
+ var languageCount = _allLangs.Value.Count();
+
+ // If there is no culture specified or the cultures specified are equal to the total amount of languages, publish the content in all cultures.
+ if (model.Cultures == null || !model.Cultures.Any() || model.Cultures.Length == languageCount)
+ {
+ return PostPublishById(model.Id);
+ }
+
+ IContent? foundContent = GetObjectFromRequest(() => _contentService.GetById(model.Id));
+
+ if (foundContent == null)
+ {
+ return HandleContentNotFound(model.Id);
+ }
+
+ var results = new Dictionary();
+
+ foreach (var culture in model.Cultures)
+ {
+ PublishResult publishResult = _contentService.SaveAndPublish(foundContent, culture, _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().Result ?? 0);
+ results[culture] = publishResult;
+ }
+
+ if (results.Any(x => x.Value.Success == false))
+ {
+ var notificationModel = new SimpleNotificationModel();
+
+ foreach (var culture in results.Where(x => x.Value.Success == false))
+ {
+ AddMessageForPublishStatus(new[] { culture.Value }, notificationModel);
+ }
+
+ return ValidationProblem(notificationModel);
+ }
+
+ return Ok();
+ }
+
[HttpDelete]
[HttpPost]
public IActionResult DeleteBlueprint(int id)
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 7e6c6658e5..b3218b2c7f 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
@@ -278,7 +278,8 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
* alert("node wasnt unpublished:" + err.data.Message);
* });
*
- * @param {Int} id the ID of the node to unpublish
+ * @param {Int} id the ID of the node to unpublish.
+ * @param {array} cultures the cultures to unpublish.
* @returns {Promise} resourcePromise object.
*
*/
@@ -1086,23 +1087,31 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
*
*
* @param {Int} id The ID of the conten to publish
+ * @param {array} cultures the cultures to publish.
* @returns {Promise} resourcePromise object containing the published content item.
*
*/
- publishById: function (id) {
-
+ publishById: function (id, cultures) {
if (!id) {
throw "id cannot be null";
}
- return umbRequestHelper.resourcePromise(
- $http.post(
+ if (!cultures) {
+ return umbRequestHelper.resourcePromise(
+ $http.post(
umbRequestHelper.getApiUrl(
- "contentApiBaseUrl",
- "PostPublishById",
- [{ id: id }])),
- 'Failed to publish content with id ' + id);
-
+ "contentApiBaseUrl",
+ "PostPublishById"), { id: id }),
+ 'Failed to publish content with id ' + id);
+ }
+ else {
+ return umbRequestHelper.resourcePromise(
+ $http.post(
+ umbRequestHelper.getApiUrl(
+ "contentApiBaseUrl",
+ "PostPublishByIdAndCulture"), { id: id, cultures: cultures }),
+ 'Failed to publish content with id ' + id);
+ }
},
/**