diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Extensions/ModelStateExtensionsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Extensions/ModelStateExtensionsTests.cs
index 3c4b78b4fa..dd2c4cd424 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Extensions/ModelStateExtensionsTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Extensions/ModelStateExtensionsTests.cs
@@ -9,6 +9,7 @@ using Moq;
using NUnit.Framework;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.BackOffice.Extensions;
+using Umbraco.Extensions;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Extensions
{
diff --git a/src/Umbraco.Web.BackOffice/ActionResults/UmbracoNotificationSuccessResponse.cs b/src/Umbraco.Web.BackOffice/ActionResults/UmbracoNotificationSuccessResponse.cs
deleted file mode 100644
index 37e4902626..0000000000
--- a/src/Umbraco.Web.BackOffice/ActionResults/UmbracoNotificationSuccessResponse.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Umbraco.Cms.Core.Models.ContentEditing;
-using Umbraco.Extensions;
-
-namespace Umbraco.Cms.Web.BackOffice.ActionResults
-{
- public class UmbracoNotificationSuccessResponse : OkObjectResult
- {
- public UmbracoNotificationSuccessResponse(string successMessage) : base(null)
- {
- var notificationModel = new SimpleNotificationModel
- {
- Message = successMessage
- };
- notificationModel.AddSuccessNotification(successMessage, string.Empty);
-
- Value = notificationModel;
- }
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
index c552c0d976..66c0c4b849 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
@@ -209,7 +209,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
else
{
AddModelErrors(result);
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return new ValidationErrorResult(ModelState);
}
}
@@ -474,7 +474,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
if (ModelState.IsValid == false)
{
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return new ValidationErrorResult(ModelState);
}
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeNotificationsController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeNotificationsController.cs
index 4cecb20aa5..27be8ec263 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeNotificationsController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeNotificationsController.cs
@@ -1,4 +1,10 @@
-using Umbraco.Cms.Web.BackOffice.Filters;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Infrastructure;
+using Umbraco.Cms.Core.Models.ContentEditing;
+using Umbraco.Cms.Web.BackOffice.ActionResults;
+using Umbraco.Cms.Web.BackOffice.Filters;
+using Umbraco.Cms.Web.Common.ActionsResults;
+using Umbraco.Extensions;
namespace Umbraco.Cms.Web.BackOffice.Controllers
{
@@ -11,5 +17,55 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
[AppendCurrentEventMessages]
public abstract class BackOfficeNotificationsController : UmbracoAuthorizedJsonController
{
+ ///
+ /// returns a 200 OK response with a notification message
+ ///
+ ///
+ ///
+ protected OkObjectResult Ok(string message)
+ {
+ var notificationModel = new SimpleNotificationModel
+ {
+ Message = message
+ };
+ notificationModel.AddSuccessNotification(message, string.Empty);
+
+ return new OkObjectResult(notificationModel);
+ }
+
+ ///
+ /// Overridden to ensure that the error message is an error notification message
+ ///
+ ///
+ ///
+ protected override ActionResult ValidationProblem(string errorMessage)
+ => ValidationProblem(errorMessage, string.Empty);
+
+ ///
+ /// Creates a notofication validation problem with a header and message
+ ///
+ ///
+ ///
+ ///
+ protected ActionResult ValidationProblem(string errorHeader, string errorMessage)
+ {
+ var notificationModel = new SimpleNotificationModel
+ {
+ Message = errorMessage
+ };
+ notificationModel.AddErrorNotification(errorHeader, errorMessage);
+ return new ValidationErrorResult(notificationModel);
+ }
+
+ ///
+ /// Overridden to ensure that all queued notifications are sent to the back office
+ ///
+ ///
+ [NonAction]
+ public override ActionResult ValidationProblem()
+ // returning an object of INotificationModel will ensure that any pending
+ // notification messages are added to the response.
+ => new ValidationErrorResult(new SimpleNotificationModel());
+
}
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs
index 822f5a4911..15a0469a4a 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs
@@ -86,9 +86,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
view.Content = display.Content;
var result = _fileService.CreatePartialView(view, display.Snippet, currentUser.Id);
if (result.Success)
+ {
return Ok();
+ }
else
- return ValidationErrorResult.CreateNotificationValidationErrorResult(result.Exception.Message);
+ {
+ return ValidationProblem(result.Exception.Message);
+ }
case Constants.Trees.PartialViewMacros:
var viewMacro = new PartialView(PartialViewType.PartialViewMacro, display.VirtualPath);
@@ -97,7 +101,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (resultMacro.Success)
return Ok();
else
- return ValidationErrorResult.CreateNotificationValidationErrorResult(resultMacro.Exception.Message);
+ return ValidationProblem(resultMacro.Exception.Message);
case Constants.Trees.Scripts:
var script = new Script(display.VirtualPath);
@@ -123,7 +127,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (string.IsNullOrWhiteSpace(parentId)) throw new ArgumentException("Value cannot be null or whitespace.", "parentId");
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name");
if (name.ContainsAny(Path.GetInvalidPathChars())) {
- return ValidationErrorResult.CreateNotificationValidationErrorResult(_localizedTextService.Localize("codefile/createFolderIllegalChars"));
+ return ValidationProblem(_localizedTextService.Localize("codefile/createFolderIllegalChars"));
}
// if the parentId is root (-1) then we just need an empty string as we are
diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
index dff4cf54bf..e82c22680d 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
@@ -607,7 +607,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (!EnsureUniqueName(name, content, nameof(name)))
{
- return new ValidationErrorResult(ModelState.ToErrorDictionary());
+ return ValidationProblem(ModelState);
}
var blueprint = _contentService.CreateContentFromBlueprint(content, name, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0));
@@ -714,8 +714,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
// ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
// add the model state to the outgoing object and throw a validation message
var forDisplay = mapToDisplay(contentItem.PersistedContent);
- forDisplay.Errors = ModelState.ToErrorDictionary();
- return new ValidationErrorResult(forDisplay);
+ return ValidationProblem(forDisplay, ModelState);
}
// if there's only one variant and the model state is not valid we cannot publish so change it to save
@@ -866,8 +865,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
//lastly, if it is not valid, add the model state to the outgoing object and throw a 400
if (!ModelState.IsValid)
{
- display.Errors = ModelState.ToErrorDictionary();
- return new ValidationErrorResult(display);
+ return ValidationProblem(display, ModelState);
}
if (wasCancelled)
@@ -878,7 +876,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
//If the item is new and the operation was cancelled, we need to return a different
// status code so the UI can handle it since it won't be able to redirect since there
// is no Id to redirect to!
- return new ValidationErrorResult(display);
+ return ValidationProblem(display);
}
}
@@ -1508,7 +1506,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
var notificationModel = new SimpleNotificationModel();
AddMessageForPublishStatus(new[] { publishResult }, notificationModel);
- return new ValidationErrorResult(notificationModel);
+ return ValidationProblem(notificationModel);
}
return Ok();
@@ -1558,9 +1556,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var moveResult = _contentService.MoveToRecycleBin(foundContent, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0));
if (moveResult.Success == false)
{
- //returning an object of INotificationModel will ensure that any pending
- // notification messages are added to the response.
- return new ValidationErrorResult(new SimpleNotificationModel());
+ return ValidationProblem();
}
}
else
@@ -1568,9 +1564,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var deleteResult = _contentService.Delete(foundContent, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0));
if (deleteResult.Success == false)
{
- //returning an object of INotificationModel will ensure that any pending
- // notification messages are added to the response.
- return new ValidationErrorResult(new SimpleNotificationModel());
+ return ValidationProblem();
}
}
@@ -1591,7 +1585,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
_contentService.EmptyRecycleBin(_backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId));
- return new UmbracoNotificationSuccessResponse(_localizedTextService.Localize("defaultdialogs/recycleBinIsEmpty"));
+ return Ok(_localizedTextService.Localize("defaultdialogs/recycleBinIsEmpty"));
}
///
@@ -1628,7 +1622,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
_logger.LogWarning("Content sorting failed, this was probably caused by an event being cancelled");
// TODO: Now you can cancel sorting, does the event messages bubble up automatically?
- return new ValidationErrorResult("Content sorting failed, this was probably caused by an event being cancelled");
+ return ValidationProblem("Content sorting failed, this was probably caused by an event being cancelled");
}
return Ok();
@@ -1727,7 +1721,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (!unpublishResult.Success)
{
AddCancelMessage(content);
- return new ValidationErrorResult(content);
+ return ValidationProblem(content);
}
else
{
@@ -1799,7 +1793,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
}
catch (UriFormatException)
{
- return new ValidationErrorResult(_localizedTextService.Localize("assignDomain/invalidDomain"));
+ return ValidationProblem(_localizedTextService.Localize("assignDomain/invalidDomain"));
}
}
@@ -2069,7 +2063,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
//cannot move if the content item is not allowed at the root
if (toMove.ContentType.AllowedAsRoot == false)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult(
+ return ValidationProblem(
_localizedTextService.Localize("moveOrCopy/notAllowedAtRoot"));
}
}
@@ -2086,14 +2080,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (parentContentType.AllowedContentTypes.Select(x => x.Id).ToArray()
.Any(x => x.Value == toMove.ContentType.Id) == false)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult(
+ return ValidationProblem(
_localizedTextService.Localize("moveOrCopy/notAllowedByContentType"));
}
// Check on paths
if ($",{parent.Path},".IndexOf($",{toMove.Id},", StringComparison.Ordinal) > -1)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult(
+ return ValidationProblem(
_localizedTextService.Localize("moveOrCopy/notAllowedByPath"));
}
}
@@ -2403,8 +2397,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (rollbackResult.Success)
return Ok();
- var notificationModel = new SimpleNotificationModel();
-
switch (rollbackResult.Result)
{
case OperationResultType.Failed:
@@ -2412,22 +2404,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
case OperationResultType.FailedExceptionThrown:
case OperationResultType.NoOperation:
default:
- notificationModel.AddErrorNotification(
- _localizedTextService.Localize("speechBubbles/operationFailedHeader"),
- null); // TODO: There is no specific failed to save error message AFAIK
- break;
+ return ValidationProblem(_localizedTextService.Localize("speechBubbles/operationFailedHeader"));
case OperationResultType.FailedCancelledByEvent:
- notificationModel.AddErrorNotification(
- _localizedTextService.Localize("speechBubbles/operationCancelledHeader"),
- _localizedTextService.Localize("speechBubbles/operationCancelledText"));
- break;
+ return ValidationProblem(
+ _localizedTextService.Localize("speechBubbles/operationCancelledHeader"),
+ _localizedTextService.Localize("speechBubbles/operationCancelledText"));
}
-
- return new ValidationErrorResult(notificationModel);
}
-
-
-
-
}
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs
index 79ea6f6329..95e9b5ecfe 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs
@@ -297,7 +297,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (result.Success)
return Ok(result.Result); //return the id
else
- return ValidationErrorResult.CreateNotificationValidationErrorResult(result.Exception.Message);
+ return ValidationProblem(result.Exception.Message);
}
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)]
@@ -308,7 +308,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (result.Success)
return Ok(result.Result); //return the id
else
- return ValidationErrorResult.CreateNotificationValidationErrorResult(result.Exception.Message);
+ return ValidationProblem(result.Exception.Message);
}
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)]
diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeControllerBase.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeControllerBase.cs
index 46c75e5186..ab7788e139 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeControllerBase.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeControllerBase.cs
@@ -13,9 +13,7 @@ using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Services;
-using Umbraco.Cms.Web.BackOffice.Extensions;
using Umbraco.Cms.Web.BackOffice.Filters;
-using Umbraco.Cms.Web.Common.ActionsResults;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Extensions;
using Constants = Umbraco.Cms.Core.Constants;
@@ -27,7 +25,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
///
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[PrefixlessBodyModelValidator]
- public abstract class ContentTypeControllerBase : UmbracoAuthorizedJsonController
+ public abstract class ContentTypeControllerBase : BackOfficeNotificationsController
where TContentType : class, IContentTypeComposition
{
private readonly EditorValidatorCollection _editorValidatorCollection;
@@ -283,7 +281,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (ModelState.IsValid == false)
{
var err = CreateModelStateValidationEror(ctId, contentTypeSave, ct);
- return new ValidationErrorResult(err);
+ return ValidationProblem(err);
}
//filter out empty properties
@@ -305,11 +303,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
catch (Exception ex)
{
var responseEx = CreateInvalidCompositionResponseException(ex, contentTypeSave, ct, ctId);
- if (responseEx != null) return new ValidationErrorResult(responseEx);
+ if (responseEx != null) return ValidationProblem(responseEx);
}
var exResult = CreateCompositionValidationExceptionIfInvalid(contentTypeSave, ct);
- if (exResult != null) return new ValidationErrorResult(exResult);
+ if (exResult != null) return ValidationProblem(exResult);
saveContentType(ct);
@@ -348,11 +346,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (responseEx is null)
throw ex;
- return new ValidationErrorResult(responseEx);
+ return ValidationProblem(responseEx);
}
var exResult = CreateCompositionValidationExceptionIfInvalid(contentTypeSave, newCt);
- if (exResult != null) return new ValidationErrorResult(exResult);
+ if (exResult != null) return ValidationProblem(exResult);
//set id to null to ensure its handled as a new type
contentTypeSave.Id = null;
@@ -417,13 +415,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
case MoveOperationStatusType.FailedParentNotFound:
return NotFound();
case MoveOperationStatusType.FailedCancelledByEvent:
- //returning an object of INotificationModel will ensure that any pending
- // notification messages are added to the response.
- return new ValidationErrorResult(new SimpleNotificationModel());
+ return ValidationProblem();
case MoveOperationStatusType.FailedNotAllowedByPath:
- var notificationModel = new SimpleNotificationModel();
- notificationModel.AddErrorNotification(LocalizedTextService.Localize("moveOrCopy/notAllowedByPath"), "");
- return new ValidationErrorResult(notificationModel);
+ return ValidationProblem(LocalizedTextService.Localize("moveOrCopy/notAllowedByPath"));
default:
throw new ArgumentOutOfRangeException();
}
@@ -458,13 +452,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
case MoveOperationStatusType.FailedParentNotFound:
return NotFound();
case MoveOperationStatusType.FailedCancelledByEvent:
- //returning an object of INotificationModel will ensure that any pending
- // notification messages are added to the response.
- return new ValidationErrorResult(new SimpleNotificationModel());
- case MoveOperationStatusType.FailedNotAllowedByPath:
- var notificationModel = new SimpleNotificationModel();
- notificationModel.AddErrorNotification(LocalizedTextService.Localize("moveOrCopy/notAllowedByPath"), "");
- return new ValidationErrorResult(notificationModel);
+ return ValidationProblem();
+ case MoveOperationStatusType.FailedNotAllowedByPath:
+ return ValidationProblem(LocalizedTextService.Localize("moveOrCopy/notAllowedByPath"));
default:
throw new ArgumentOutOfRangeException();
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs
index 49bff529bd..d6ebe946bf 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs
@@ -190,7 +190,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
// so that is why it is being used here.
ModelState.AddModelError("value", result.Errors.ToErrorMessage());
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
//They've successfully set their password, we can now update their user account to be approved
@@ -242,7 +242,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError.ErrorMessage);
}
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
// TODO: Why is this necessary? This inherits from UmbracoAuthorizedApiController
diff --git a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs
index 9f020125bb..c29b17f3a3 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs
@@ -16,9 +16,7 @@ using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
-using Umbraco.Cms.Web.BackOffice.Extensions;
using Umbraco.Cms.Web.BackOffice.Filters;
-using Umbraco.Cms.Web.Common.ActionsResults;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
@@ -267,7 +265,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (result.Success)
return Ok(result.Result); //return the id
else
- return ValidationErrorResult.CreateNotificationValidationErrorResult(result.Exception.Message);
+ return ValidationProblem(result.Exception.Message);
}
///
@@ -302,7 +300,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
catch (DuplicateNameException ex)
{
ModelState.AddModelError("Name", ex.Message);
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
// map back to display model, and return
@@ -335,13 +333,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
case MoveOperationStatusType.FailedParentNotFound:
return NotFound();
case MoveOperationStatusType.FailedCancelledByEvent:
- //returning an object of INotificationModel will ensure that any pending
- // notification messages are added to the response.
- return new ValidationErrorResult(new SimpleNotificationModel());
+ return ValidationProblem();
case MoveOperationStatusType.FailedNotAllowedByPath:
var notificationModel = new SimpleNotificationModel();
notificationModel.AddErrorNotification(_localizedTextService.Localize("moveOrCopy/notAllowedByPath"), "");
- return new ValidationErrorResult(notificationModel);
+ return ValidationProblem(notificationModel);
default:
throw new ArgumentOutOfRangeException();
}
@@ -355,7 +351,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (result.Success)
return Ok(result.Result);
else
- return ValidationErrorResult.CreateNotificationValidationErrorResult(result.Exception.Message);
+ return ValidationProblem(result.Exception.Message);
}
///
diff --git a/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs b/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs
index 980d08c2e2..b00fa20141 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs
@@ -13,8 +13,6 @@ using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
-using Umbraco.Cms.Web.BackOffice.Extensions;
-using Umbraco.Cms.Web.Common.ActionsResults;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
@@ -101,7 +99,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
public ActionResult Create(int parentId, string key)
{
if (string.IsNullOrEmpty(key))
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Key can not be empty."); // TODO: translate
+ return ValidationProblem("Key can not be empty."); // TODO: translate
if (_localizationService.DictionaryItemExists(key))
{
@@ -109,7 +107,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
"dictionaryItem/changeKeyError",
_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.GetUserCulture(_localizedTextService, _globalSettings),
new Dictionary { { "0", key } });
- return ValidationErrorResult.CreateNotificationValidationErrorResult(message);
+ return ValidationProblem(message);
}
try
@@ -130,7 +128,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
catch (Exception ex)
{
_logger.LogError(ex, "Error creating dictionary with {Name} under {ParentId}", key, parentId);
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Error creating dictionary item");
+ return ValidationProblem("Error creating dictionary item");
}
}
@@ -207,7 +205,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
_localizationService.GetDictionaryItemById(int.Parse(dictionary.Id.ToString()));
if (dictionaryItem == null)
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Dictionary item does not exist");
+ return ValidationProblem("Dictionary item does not exist");
var userCulture = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.GetUserCulture(_localizedTextService, _globalSettings);
@@ -224,7 +222,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
userCulture,
new Dictionary { { "0", dictionary.Name } });
ModelState.AddModelError("Name", message);
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
dictionaryItem.ItemKey = dictionary.Name;
@@ -251,7 +249,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
catch (Exception ex)
{
_logger.LogError(ex, "Error saving dictionary with {Name} under {ParentId}", dictionary.Name, dictionary.ParentId);
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Something went wrong saving dictionary");
+ return ValidationProblem("Something went wrong saving dictionary");
}
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs
index 8465e7a454..2cc35c8dfa 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs
@@ -97,7 +97,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (language.IsDefault)
{
var message = $"Language '{language.IsoCode}' is currently set to 'default' and can not be deleted.";
- return ValidationErrorResult.CreateNotificationValidationErrorResult(message);
+ return ValidationProblem(message);
}
// service is happy deleting a language that's fallback for another language,
@@ -116,7 +116,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
public ActionResult SaveLanguage(Language language)
{
if (!ModelState.IsValid)
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
// this is prone to race conditions but the service will not let us proceed anyways
var existingByCulture = _localizationService.GetLanguageByIsoCode(language.IsoCode);
@@ -132,7 +132,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
//someone is trying to create a language that already exist
ModelState.AddModelError("IsoCode", "The language " + language.IsoCode + " already exists");
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
var existingById = language.Id != default ? _localizationService.GetLanguageById(language.Id) : null;
@@ -149,7 +149,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
catch (CultureNotFoundException)
{
ModelState.AddModelError("IsoCode", "No Culture found with name " + language.IsoCode);
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
// create it (creating a new language cannot create a fallback cycle)
@@ -172,7 +172,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (existingById.IsDefault && !language.IsDefault)
{
ModelState.AddModelError("IsDefault", "Cannot un-default the default language.");
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
existingById.IsDefault = language.IsDefault;
@@ -187,12 +187,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (!languages.ContainsKey(existingById.FallbackLanguageId.Value))
{
ModelState.AddModelError("FallbackLanguage", "The selected fall back language does not exist.");
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
if (CreatesCycle(existingById, languages))
{
ModelState.AddModelError("FallbackLanguage", $"The selected fall back language {languages[existingById.FallbackLanguageId.Value].IsoCode} would create a circular path.");
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs b/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs
index 20d33bd83a..9fcf407581 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs
@@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Logging.Viewer;
using Umbraco.Cms.Core.Models;
-using Umbraco.Cms.Web.Common.ActionsResults;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
using Constants = Umbraco.Cms.Core.Constants;
@@ -18,7 +17,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)]
- public class LogViewerController : UmbracoAuthorizedJsonController
+ public class LogViewerController : BackOfficeNotificationsController
{
private readonly ILogViewer _logViewer;
@@ -51,7 +50,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
//We will need to stop the request if trying to do this on a 1GB file
if (CanViewLogs(logTimePeriod) == false)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Unable to view logs, due to size");
+ return ValidationProblem("Unable to view logs, due to size");
}
return _logViewer.GetNumberOfErrors(logTimePeriod);
@@ -64,7 +63,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
//We will need to stop the request if trying to do this on a 1GB file
if (CanViewLogs(logTimePeriod) == false)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Unable to view logs, due to size");
+ return ValidationProblem("Unable to view logs, due to size");
}
return _logViewer.GetLogLevelCounts(logTimePeriod);
@@ -77,7 +76,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
//We will need to stop the request if trying to do this on a 1GB file
if (CanViewLogs(logTimePeriod) == false)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Unable to view logs, due to size");
+ return ValidationProblem("Unable to view logs, due to size");
}
return new ActionResult>(_logViewer.GetMessageTemplates(logTimePeriod));
@@ -91,7 +90,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
//We will need to stop the request if trying to do this on a 1GB file
if (CanViewLogs(logTimePeriod) == false)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Unable to view logs, due to size");
+ return ValidationProblem("Unable to view logs, due to size");
}
var direction = orderDirection == "Descending" ? Direction.Descending : Direction.Ascending;
diff --git a/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs b/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs
index a7ec619ae1..ec91d76c8e 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs
@@ -15,7 +15,6 @@ using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
-using Umbraco.Cms.Web.Common.ActionsResults;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
@@ -74,19 +73,19 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
if (string.IsNullOrWhiteSpace(name))
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Name can not be empty");
+ return ValidationProblem("Name can not be empty");
}
var alias = name.ToSafeAlias(_shortStringHelper);
if (_macroService.GetByAlias(alias) != null)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Macro with this alias already exists");
+ return ValidationProblem("Macro with this alias already exists");
}
if (name == null || name.Length > 255)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Name cannnot be more than 255 characters in length.");
+ return ValidationProblem("Name cannnot be more than 255 characters in length.");
}
try
@@ -106,7 +105,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
const string errorMessage = "Error creating macro";
_logger.LogError(exception, errorMessage);
- return ValidationErrorResult.CreateNotificationValidationErrorResult(errorMessage);
+ return ValidationProblem(errorMessage);
}
}
@@ -117,7 +116,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (macro == null)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult($"Macro with id {id} does not exist");
+ return ValidationProblem($"Macro with id {id} does not exist");
}
var macroDisplay = MapToDisplay(macro);
@@ -132,7 +131,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (macro == null)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult($"Macro with id {id} does not exist");
+ return ValidationProblem($"Macro with id {id} does not exist");
}
var macroDisplay = MapToDisplay(macro);
@@ -145,12 +144,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
var guidUdi = id as GuidUdi;
if (guidUdi == null)
- return ValidationErrorResult.CreateNotificationValidationErrorResult($"Macro with id {id} does not exist");
+ return ValidationProblem($"Macro with id {id} does not exist");
var macro = _macroService.GetById(guidUdi.Guid);
if (macro == null)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult($"Macro with id {id} does not exist");
+ return ValidationProblem($"Macro with id {id} does not exist");
}
var macroDisplay = MapToDisplay(macro);
@@ -165,7 +164,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (macro == null)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult($"Macro with id {id} does not exist");
+ return ValidationProblem($"Macro with id {id} does not exist");
}
_macroService.Delete(macro);
@@ -178,19 +177,19 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
if (macroDisplay == null)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("No macro data found in request");
+ return ValidationProblem("No macro data found in request");
}
if (macroDisplay.Name == null || macroDisplay.Name.Length > 255)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Name cannnot be more than 255 characters in length.");
+ return ValidationProblem("Name cannnot be more than 255 characters in length.");
}
var macro = _macroService.GetById(int.Parse(macroDisplay.Id.ToString()));
if (macro == null)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult($"Macro with id {macroDisplay.Id} does not exist");
+ return ValidationProblem($"Macro with id {macroDisplay.Id} does not exist");
}
if (macroDisplay.Alias != macro.Alias)
@@ -199,7 +198,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (macroByAlias != null)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Macro with this alias already exists");
+ return ValidationProblem("Macro with this alias already exists");
}
}
@@ -227,7 +226,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
const string errorMessage = "Error creating macro";
_logger.LogError(exception, errorMessage);
- return ValidationErrorResult.CreateNotificationValidationErrorResult(errorMessage);
+ return ValidationProblem(errorMessage);
}
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs
index b98b2e9cd7..a2f1fe36c3 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs
@@ -452,9 +452,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var moveResult = _mediaService.MoveToRecycleBin(foundMedia, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId));
if (moveResult == false)
{
- //returning an object of INotificationModel will ensure that any pending
- // notification messages are added to the response.
- return new ValidationErrorResult(new SimpleNotificationModel());
+ return ValidationProblem();
}
}
else
@@ -462,9 +460,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var deleteResult = _mediaService.Delete(foundMedia, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId));
if (deleteResult == false)
{
- //returning an object of INotificationModel will ensure that any pending
- // notification messages are added to the response.
- return new ValidationErrorResult(new SimpleNotificationModel());
+ return ValidationProblem();
}
}
@@ -500,11 +496,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (sourceParentID == destinationParentID)
{
- return new ValidationErrorResult(new SimpleNotificationModel(new BackOfficeNotification("",_localizedTextService.Localize("media/moveToSameFolderFailed"),NotificationStyle.Error)));
+ return ValidationProblem(new SimpleNotificationModel(new BackOfficeNotification("",_localizedTextService.Localize("media/moveToSameFolderFailed"),NotificationStyle.Error)));
}
if (moveResult == false)
{
- return new ValidationErrorResult(new SimpleNotificationModel());
+ return ValidationProblem();
}
else
{
@@ -563,9 +559,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
//ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
// add the model state to the outgoing object and throw validation response
- var forDisplay = _umbracoMapper.Map(contentItem.PersistedContent);
- forDisplay.Errors = ModelState.ToErrorDictionary();
- return new ValidationErrorResult(forDisplay);
+ MediaItemDisplay forDisplay = _umbracoMapper.Map(contentItem.PersistedContent);
+ return ValidationProblem(forDisplay, ModelState);
}
}
@@ -578,8 +573,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
//lastly, if it is not valid, add the model state to the outgoing object and throw a 403
if (!ModelState.IsValid)
{
- display.Errors = ModelState.ToErrorDictionary();
- return new ValidationErrorResult(display, StatusCodes.Status403Forbidden);
+ return ValidationProblem(display, ModelState, StatusCodes.Status403Forbidden);
}
//put the correct msgs in
@@ -602,7 +596,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
// is no Id to redirect to!
if (saveStatus.Result.Result == OperationResultType.FailedCancelledByEvent && IsCreatingAction(contentItem.Action))
{
- return new ValidationErrorResult(display);
+ return ValidationProblem(display);
}
}
@@ -622,7 +616,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
_mediaService.EmptyRecycleBin(_backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId));
- return new UmbracoNotificationSuccessResponse(_localizedTextService.Localize("defaultdialogs/recycleBinIsEmpty"));
+ return Ok(_localizedTextService.Localize("defaultdialogs/recycleBinIsEmpty"));
}
///
@@ -661,7 +655,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (_mediaService.Sort(sortedMedia) == false)
{
_logger.LogWarning("Media sorting failed, this was probably caused by an event being cancelled");
- return new ValidationErrorResult("Media sorting failed, this was probably caused by an event being cancelled");
+ return ValidationProblem("Media sorting failed, this was probably caused by an event being cancelled");
}
return Ok();
}
@@ -919,7 +913,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
}
else
{
- return new ValidationErrorResult("The request was not formatted correctly, the parentId is not an integer, Guid or UDI");
+ return ValidationProblem("The request was not formatted correctly, the parentId is not an integer, Guid or UDI");
}
}
@@ -931,7 +925,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var authorizationResult = await _authorizationService.AuthorizeAsync(User, new MediaPermissionsResource(_mediaService.GetById(intParentId)), requirement);
if (!authorizationResult.Succeeded)
{
- return new ValidationErrorResult(
+ return ValidationProblem(
new SimpleNotificationModel(new BackOfficeNotification(
_localizedTextService.Localize("speechBubbles/operationFailedHeader"),
_localizedTextService.Localize("speechBubbles/invalidUserPermissionsText"),
@@ -970,7 +964,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
var notificationModel = new SimpleNotificationModel();
notificationModel.AddErrorNotification(_localizedTextService.Localize("moveOrCopy/notAllowedAtRoot"), "");
- return new ValidationErrorResult(notificationModel);
+ return ValidationProblem(notificationModel);
}
}
else
@@ -988,7 +982,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
var notificationModel = new SimpleNotificationModel();
notificationModel.AddErrorNotification(_localizedTextService.Localize("moveOrCopy/notAllowedByContentType"), "");
- return new ValidationErrorResult(notificationModel);
+ return ValidationProblem(notificationModel);
}
// Check on paths
@@ -996,7 +990,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
var notificationModel = new SimpleNotificationModel();
notificationModel.AddErrorNotification(_localizedTextService.Localize("moveOrCopy/notAllowedByPath"), "");
- return new ValidationErrorResult(notificationModel);
+ return ValidationProblem(notificationModel);
}
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs
index 5f61d1b1c1..3ca33c3643 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs
@@ -266,7 +266,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (result.Success)
return Ok(result.Result); //return the id
else
- return ValidationErrorResult.CreateNotificationValidationErrorResult(result.Exception.Message);
+ return ValidationProblem(result.Exception.Message);
}
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)]
@@ -277,7 +277,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (result.Success)
return Ok(result.Result); //return the id
else
- return ValidationErrorResult.CreateNotificationValidationErrorResult(result.Exception.Message);
+ return ValidationProblem(result.Exception.Message);
}
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)]
diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
index 6045cec8f9..7f4270c3d6 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
@@ -26,11 +26,8 @@ using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Implement;
using Umbraco.Cms.Core.Strings;
-using Umbraco.Cms.Web.BackOffice.Extensions;
using Umbraco.Cms.Web.BackOffice.Filters;
using Umbraco.Cms.Web.BackOffice.ModelBinders;
-using Umbraco.Cms.Web.BackOffice.Security;
-using Umbraco.Cms.Web.Common.ActionsResults;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Cms.Web.Common.Filters;
@@ -260,8 +257,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (ModelState.IsValid == false)
{
MemberDisplay forDisplay = _umbracoMapper.Map(contentItem.PersistedContent);
- forDisplay.Errors = ModelState.ToErrorDictionary();
- return new ValidationErrorResult(forDisplay);
+ return ValidationProblem(forDisplay, ModelState);
}
// Create a scope here which will wrap all child data operations in a single transaction.
@@ -300,8 +296,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
// lastly, if it is not valid, add the model state to the outgoing object and throw a 403
if (!ModelState.IsValid)
{
- display.Errors = ModelState.ToErrorDictionary();
- return new ValidationErrorResult(display, StatusCodes.Status403Forbidden);
+ return ValidationProblem(display, ModelState, StatusCodes.Status403Forbidden);
}
// put the correct messages in
@@ -373,7 +368,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (created.Succeeded == false)
{
- return new ValidationErrorResult(created.Errors.ToErrorMessage());
+ return ValidationProblem(created.Errors.ToErrorMessage());
}
// now re-look up the member, which will now exist
@@ -460,7 +455,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
MemberIdentityUser identityMember = await _memberManager.FindByIdAsync(contentItem.Id.ToString());
if (identityMember == null)
{
- return new ValidationErrorResult("Member was not found");
+ return ValidationProblem("Member was not found");
}
// Handle unlocking with the member manager (takes care of other nuances)
@@ -469,7 +464,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
IdentityResult unlockResult = await _memberManager.SetLockoutEndDateAsync(identityMember, DateTimeOffset.Now.AddMinutes(-1));
if (unlockResult.Succeeded == false)
{
- return new ValidationErrorResult(
+ return ValidationProblem(
$"Could not unlock for member {contentItem.Id} - error {unlockResult.Errors.ToErrorMessage()}");
}
needsResync = true;
@@ -478,7 +473,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
// NOTE: This should not ever happen unless someone is mucking around with the request data.
// An admin cannot simply lock a user, they get locked out by password attempts, but an admin can unlock them
- return new ValidationErrorResult("An admin cannot lock a member");
+ return ValidationProblem("An admin cannot lock a member");
}
// If we're changing the password...
@@ -488,13 +483,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
IdentityResult validatePassword = await _memberManager.ValidatePasswordAsync(contentItem.Password.NewPassword);
if (validatePassword.Succeeded == false)
{
- return new ValidationErrorResult(validatePassword.Errors.ToErrorMessage());
+ return ValidationProblem(validatePassword.Errors.ToErrorMessage());
}
Attempt intId = identityMember.Id.TryConvertTo();
if (intId.Success == false)
{
- return new ValidationErrorResult("Member ID was not valid");
+ return ValidationProblem("Member ID was not valid");
}
var changingPasswordModel = new ChangingPasswordModel
@@ -513,7 +508,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError?.ErrorMessage ?? string.Empty);
}
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
needsResync = true;
@@ -622,7 +617,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
IdentityResult identityResult = await _memberManager.RemoveFromRolesAsync(identityMember, rolesToRemove);
if (!identityResult.Succeeded)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult(identityResult.Errors.ToErrorMessage());
+ return ValidationProblem(identityResult.Errors.ToErrorMessage());
}
hasChanges = true;
}
@@ -635,7 +630,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
IdentityResult identityResult = await _memberManager.AddToRolesAsync(identityMember, toAdd);
if (!identityResult.Succeeded)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult(identityResult.Errors.ToErrorMessage());
+ return ValidationProblem(identityResult.Errors.ToErrorMessage());
}
hasChanges = true;
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs
index 99dcf161ab..9fd95755f5 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs
@@ -70,14 +70,16 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
public ActionResult PostSavePackage(PackageDefinition model)
{
if (ModelState.IsValid == false)
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
//save it
if (!_packagingService.SaveCreatedPackage(model))
- return ValidationErrorResult.CreateNotificationValidationErrorResult(
+ {
+ return ValidationProblem(
model.Id == default
? $"A package with the name {model.Name} already exists"
: $"The package with id {model.Id} was not found");
+ }
_packagingService.ExportCreatedPackage(model);
@@ -108,7 +110,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var fullPath = _hostingEnvironment.MapPathWebRoot(package.PackagePath);
if (!System.IO.File.Exists(fullPath))
- return ValidationErrorResult.CreateNotificationValidationErrorResult("No file found for path " + package.PackagePath);
+ return ValidationProblem("No file found for path " + package.PackagePath);
var fileName = Path.GetFileName(package.PackagePath);
@@ -116,7 +118,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var cd = new System.Net.Mime.ContentDisposition
{
- FileName = WebUtility.UrlEncode(fileName),
+ FileName = WebUtility.UrlEncode(fileName),
Inline = false // false = prompt the user for downloading; true = browser to try to show the file inline
};
Response.Headers.Add("Content-Disposition", cd.ToString());
@@ -132,7 +134,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
public ActionResult GetInstalledPackageById(int id)
{
var pack = _packagingService.GetInstalledPackageById(id);
- if (pack == null) return NotFound();
+ if (pack == null)
+ return NotFound();
return pack;
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs b/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs
index 1c874732c4..da2205f59c 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs
@@ -187,7 +187,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (installType == PackageInstallType.AlreadyInstalled)
{
//this package is already installed
- return ValidationErrorResult.CreateNotificationValidationErrorResult(
+ return ValidationProblem(
_localizedTextService.Localize("packager/packageAlreadyInstalled"));
}
@@ -241,7 +241,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (installType == PackageInstallType.AlreadyInstalled)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult(
+ return ValidationProblem(
_localizedTextService.Localize("packager/packageAlreadyInstalled"));
}
@@ -267,7 +267,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
var packageMinVersion = packageInfo.UmbracoVersion;
if (_umbracoVersion.Version < packageMinVersion)
- return ValidationErrorResult.CreateNotificationValidationErrorResult(
+ return ValidationProblem(
_localizedTextService.Localize("packager/targetVersionMismatch", new[] {packageMinVersion.ToString()}));
}
@@ -286,7 +286,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
//save to the installedPackages.config, this will create a new entry with a new Id
if (!_packagingService.SaveInstalledPackage(packageDefinition))
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Could not save the package");
+ return ValidationProblem("Could not save the package");
model.Id = packageDefinition.Id;
break;
diff --git a/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs
index 187d59b446..9d95dee395 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs
@@ -154,7 +154,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
catch (Exception ex)
{
_logger.LogError(ex, "Error creating relation type with {Name}", relationType.Name);
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Error creating relation type.");
+ return ValidationProblem("Error creating relation type.");
}
}
@@ -169,7 +169,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (relationTypePersisted == null)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Relation type does not exist");
+ return ValidationProblem("Relation type does not exist");
}
_umbracoMapper.Map(relationType, relationTypePersisted);
@@ -185,7 +185,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
catch (Exception ex)
{
_logger.LogError(ex, "Error saving relation type with {Id}", relationType.Id);
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Something went wrong when saving the relation type");
+ return ValidationProblem("Something went wrong when saving the relation type");
}
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs
index 3891550f1e..a370f48ebe 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs
@@ -1,10 +1,17 @@
using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Infrastructure;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Web.BackOffice.Filters;
+using Umbraco.Cms.Web.Common.ActionsResults;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Cms.Web.Common.Controllers;
using Umbraco.Cms.Web.Common.Filters;
+using Umbraco.Extensions;
namespace Umbraco.Cms.Web.BackOffice.Controllers
{
@@ -25,6 +32,91 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
[MiddlewareFilter(typeof(UnhandledExceptionLoggerFilter))]
public abstract class UmbracoAuthorizedApiController : UmbracoApiController
{
+ ///
+ /// Returns a validation problem result for the and the
+ ///
+ ///
+ ///
+ ///
+ ///
+ protected virtual ActionResult ValidationProblem(IErrorModel model, ModelStateDictionary modelStateDictionary, int statusCode = StatusCodes.Status400BadRequest)
+ {
+ model.Errors = modelStateDictionary.ToErrorDictionary();
+ return ValidationProblem(model, statusCode);
+ }
+ ///
+ /// Overridden to return Umbraco compatible errors
+ ///
+ ///
+ ///
+ [NonAction]
+ public override ActionResult ValidationProblem(ModelStateDictionary modelStateDictionary)
+ {
+ return new ValidationErrorResult(new SimpleValidationModel(modelStateDictionary.ToErrorDictionary()));
+
+ //ValidationProblemDetails problemDetails = GetValidationProblemDetails(modelStateDictionary: modelStateDictionary);
+ //return new ValidationErrorResult(problemDetails);
+ }
+
+ // creates validation problem details instance.
+ // borrowed from netcore: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ControllerBase.cs#L1970
+ protected ValidationProblemDetails GetValidationProblemDetails(
+ string detail = null,
+ string instance = null,
+ int? statusCode = null,
+ string title = null,
+ string type = null,
+ [ActionResultObjectValue] ModelStateDictionary modelStateDictionary = null)
+ {
+ modelStateDictionary ??= ModelState;
+
+ ValidationProblemDetails validationProblem;
+ if (ProblemDetailsFactory == null)
+ {
+ // ProblemDetailsFactory may be null in unit testing scenarios. Improvise to make this more testable.
+ validationProblem = new ValidationProblemDetails(modelStateDictionary)
+ {
+ Detail = detail,
+ Instance = instance,
+ Status = statusCode,
+ Title = title,
+ Type = type,
+ };
+ }
+ else
+ {
+ validationProblem = ProblemDetailsFactory?.CreateValidationProblemDetails(
+ HttpContext,
+ modelStateDictionary,
+ statusCode: statusCode,
+ title: title,
+ type: type,
+ detail: detail,
+ instance: instance);
+ }
+
+ return validationProblem;
+ }
+
+ ///
+ /// Returns an Umbraco compatible validation problem for the given error message
+ ///
+ ///
+ ///
+ protected virtual ActionResult ValidationProblem(string errorMessage)
+ {
+ ValidationProblemDetails problemDetails = GetValidationProblemDetails(errorMessage);
+ return new ValidationErrorResult(problemDetails);
+ }
+
+ ///
+ /// Returns an Umbraco compatible validation problem for the object result
+ ///
+ ///
+ ///
+ ///
+ protected virtual ActionResult ValidationProblem(object value, int statusCode = StatusCodes.Status400BadRequest)
+ => new ValidationErrorResult(value, statusCode);
}
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/UserGroupsController.cs b/src/Umbraco.Web.BackOffice/Controllers/UserGroupsController.cs
index f5cdb94d37..971d7400de 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/UserGroupsController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/UserGroupsController.cs
@@ -10,7 +10,6 @@ using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
-using Umbraco.Cms.Web.BackOffice.ActionResults;
using Umbraco.Cms.Web.BackOffice.Filters;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
@@ -22,7 +21,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[Authorize(Policy = AuthorizationPolicies.SectionAccessUsers)]
[PrefixlessBodyModelValidator]
- public class UserGroupsController : UmbracoAuthorizedJsonController
+ public class UserGroupsController : BackOfficeNotificationsController
{
private readonly IUserService _userService;
private readonly IContentService _contentService;
@@ -202,10 +201,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
_userService.DeleteUserGroup(userGroup);
}
if (userGroups.Length > 1)
- return new UmbracoNotificationSuccessResponse(
- _localizedTextService.Localize("speechBubbles/deleteUserGroupsSuccess", new[] {userGroups.Length.ToString()}));
- return new UmbracoNotificationSuccessResponse(
- _localizedTextService.Localize("speechBubbles/deleteUserGroupSuccess", new[] {userGroups[0].Name}));
+ {
+ return Ok(_localizedTextService.Localize("speechBubbles/deleteUserGroupsSuccess", new[] {userGroups.Length.ToString()}));
+ }
+
+ return Ok(_localizedTextService.Localize("speechBubbles/deleteUserGroupSuccess", new[] {userGroups[0].Name}));
}
}
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs
index ee09b7d67b..ff2e087aa4 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs
@@ -51,7 +51,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
[Authorize(Policy = AuthorizationPolicies.SectionAccessUsers)]
[PrefixlessBodyModelValidator]
[IsCurrentUserModelFilter]
- public class UsersController : UmbracoAuthorizedJsonController
+ public class UsersController : BackOfficeNotificationsController
{
private readonly MediaFileManager _mediaFileManager;
private readonly ContentSettings _contentSettings;
@@ -128,7 +128,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
var urls = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator);
if (urls == null)
- return new ValidationErrorResult("Could not access Gravatar endpoint");
+ return ValidationProblem("Could not access Gravatar endpoint");
return urls;
}
@@ -345,7 +345,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (ModelState.IsValid == false)
{
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
if (_securitySettings.UsernameIsEmail)
@@ -362,7 +362,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (ModelState.IsValid == false)
{
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
//Perform authorization here to see if the current user can actually save this user with the info being requested
@@ -380,7 +380,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var created = await _userManager.CreateAsync(identityUser);
if (created.Succeeded == false)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult(created.Errors.ToErrorMessage());
+ return ValidationProblem(created.Errors.ToErrorMessage());
}
string resetPassword;
@@ -389,7 +389,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var result = await _userManager.AddPasswordAsync(identityUser, password);
if (result.Succeeded == false)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult(created.Errors.ToErrorMessage());
+ return ValidationProblem(created.Errors.ToErrorMessage());
}
resetPassword = password;
@@ -446,19 +446,19 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (ModelState.IsValid == false)
{
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
if (!_emailSender.CanSendRequiredEmail())
{
- return new ValidationErrorResult("No Email server is configured");
+ return ValidationProblem("No Email server is configured");
}
//Perform authorization here to see if the current user can actually save this user with the info being requested
var canSaveUser = _userEditorAuthorizationHelper.IsAuthorized(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, user, null, null, userSave.UserGroups);
if (canSaveUser == false)
{
- return new ValidationErrorResult(canSaveUser.Result, StatusCodes.Status401Unauthorized);
+ return ValidationProblem(canSaveUser.Result, StatusCodes.Status401Unauthorized);
}
if (user == null)
@@ -471,7 +471,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var created = await _userManager.CreateAsync(identityUser);
if (created.Succeeded == false)
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult(created.Errors.ToErrorMessage());
+ return ValidationProblem(created.Errors.ToErrorMessage());
}
//now re-look the user back up
@@ -513,7 +513,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
ModelState.AddModelError(
_securitySettings.UsernameIsEmail ? "Email" : "Username",
"A user with the username already exists");
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
return new ActionResult(user);
@@ -568,7 +568,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (ModelState.IsValid == false)
{
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
var intId = userSave.Id.TryConvertTo();
@@ -631,7 +631,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
}
if (hasErrors)
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
//merge the save data onto the user
var user = _umbracoMapper.Map(userSave, found);
@@ -664,7 +664,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (ModelState.IsValid == false)
{
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
Attempt intId = changingPasswordModel.Id.TryConvertTo();
@@ -684,12 +684,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
// if it's the current user, the current user cannot reset their own password without providing their old password
if (currentUser.Username == found.Username && string.IsNullOrEmpty(changingPasswordModel.OldPassword))
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("Password reset is not allowed without providing old password");
+ return ValidationProblem("Password reset is not allowed without providing old password");
}
if (!currentUser.IsAdmin() && found.IsAdmin())
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("The current user cannot change the password for the specified user");
+ return ValidationProblem("The current user cannot change the password for the specified user");
}
Attempt passwordChangeResult = await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _userManager);
@@ -706,7 +706,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError.ErrorMessage);
}
- return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
+ return ValidationProblem(ModelState);
}
@@ -720,7 +720,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var tryGetCurrentUserId = _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId();
if (tryGetCurrentUserId && userIds.Contains(tryGetCurrentUserId.Result))
{
- return ValidationErrorResult.CreateNotificationValidationErrorResult("The current user cannot disable itself");
+ return ValidationProblem("The current user cannot disable itself");
}
var users = _userService.GetUsersById(userIds).ToArray();
@@ -733,12 +733,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (users.Length > 1)
{
- return new UmbracoNotificationSuccessResponse(
- _localizedTextService.Localize("speechBubbles/disableUsersSuccess", new[] {userIds.Length.ToString()}));
+ return Ok(_localizedTextService.Localize("speechBubbles/disableUsersSuccess", new[] {userIds.Length.ToString()}));
}
- return new UmbracoNotificationSuccessResponse(
- _localizedTextService.Localize("speechBubbles/disableUserSuccess", new[] { users[0].Name }));
+ return Ok(_localizedTextService.Localize("speechBubbles/disableUserSuccess", new[] { users[0].Name }));
}
///
@@ -757,11 +755,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (users.Length > 1)
{
- return new UmbracoNotificationSuccessResponse(
+ return Ok(
_localizedTextService.Localize("speechBubbles/enableUsersSuccess", new[] { userIds.Length.ToString() }));
}
- return new UmbracoNotificationSuccessResponse(
+ return Ok(
_localizedTextService.Localize("speechBubbles/enableUserSuccess", new[] { users[0].Name }));
}
@@ -787,18 +785,18 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var unlockResult = await _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.Now.AddMinutes(-1));
if (unlockResult.Succeeded == false)
{
- return new ValidationErrorResult(
+ return ValidationProblem(
$"Could not unlock for user {u} - error {unlockResult.Errors.ToErrorMessage()}");
}
if (userIds.Length == 1)
{
- return new UmbracoNotificationSuccessResponse(
+ return Ok(
_localizedTextService.Localize("speechBubbles/unlockUserSuccess", new[] {user.Name}));
}
}
- return new UmbracoNotificationSuccessResponse(
+ return Ok(
_localizedTextService.Localize("speechBubbles/unlockUsersSuccess", new[] {(userIds.Length - notFound.Count).ToString()}));
}
@@ -816,7 +814,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
}
}
_userService.Save(users);
- return new UmbracoNotificationSuccessResponse(
+ return Ok(
_localizedTextService.Localize("speechBubbles/setUserGroupOnUsersSuccess"));
}
@@ -847,7 +845,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var userName = user.Name;
_userService.Delete(user, true);
- return new UmbracoNotificationSuccessResponse(
+ return Ok(
_localizedTextService.Localize("speechBubbles/deleteUserSuccess", new[] { userName }));
}
diff --git a/src/Umbraco.Web.BackOffice/Extensions/ModelStateExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/ModelStateExtensions.cs
index a48f46f605..a08cd20071 100644
--- a/src/Umbraco.Web.BackOffice/Extensions/ModelStateExtensions.cs
+++ b/src/Umbraco.Web.BackOffice/Extensions/ModelStateExtensions.cs
@@ -6,18 +6,11 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
using Umbraco.Cms.Web.BackOffice.PropertyEditors.Validation;
using Umbraco.Extensions;
-namespace Umbraco.Cms.Web.BackOffice.Extensions
+namespace Umbraco.Extensions
{
public static class ModelStateExtensions
{
- ///
- /// Checks if there are any model errors on any fields containing the prefix
- ///
- ///
- ///
- ///
- public static bool IsValid(this ModelStateDictionary state, string prefix) =>
- state.Where(v => v.Key.StartsWith(prefix + ".")).All(v => !v.Value.Errors.Any());
+
///
/// Adds the to the model state with the appropriate keys for property errors
@@ -171,42 +164,5 @@ namespace Umbraco.Cms.Web.BackOffice.Extensions
}
}
-
- public static IDictionary ToErrorDictionary(this ModelStateDictionary modelState)
- {
- var modelStateError = new Dictionary();
- foreach (KeyValuePair keyModelStatePair in modelState)
- {
- var key = keyModelStatePair.Key;
- ModelErrorCollection errors = keyModelStatePair.Value.Errors;
- if (errors != null && errors.Count > 0)
- {
- modelStateError.Add(key, errors.Select(error => error.ErrorMessage));
- }
- }
- return modelStateError;
- }
-
- ///
- /// Serializes the ModelState to JSON for JavaScript to interrogate the errors
- ///
- ///
- ///
- public static JsonResult ToJsonErrors(this ModelStateDictionary state) =>
- new JsonResult(new
- {
- success = state.IsValid.ToString().ToLower(),
- failureType = "ValidationError",
- validationErrors = from e in state
- where e.Value.Errors.Count > 0
- select new
- {
- name = e.Key,
- errors = e.Value.Errors.Select(x => x.ErrorMessage)
- .Concat(
- e.Value.Errors.Where(x => x.Exception != null).Select(x => x.Exception.Message))
- }
- });
-
}
}
diff --git a/src/Umbraco.Web.Common/ActionsResults/UmbracoProblemResult.cs b/src/Umbraco.Web.Common/ActionsResults/UmbracoProblemResult.cs
index e3279407fa..92f5d7aed7 100644
--- a/src/Umbraco.Web.Common/ActionsResults/UmbracoProblemResult.cs
+++ b/src/Umbraco.Web.Common/ActionsResults/UmbracoProblemResult.cs
@@ -1,8 +1,9 @@
-using System.Net;
+using System.Net;
using Microsoft.AspNetCore.Mvc;
namespace Umbraco.Cms.Web.Common.ActionsResults
{
+ // TODO: What is the purpose of this? Doesn't seem to add any benefit
public class UmbracoProblemResult : ObjectResult
{
public UmbracoProblemResult(string message, HttpStatusCode httpStatusCode = HttpStatusCode.InternalServerError) : base(new {Message = message})
diff --git a/src/Umbraco.Web.Common/ActionsResults/ValidationErrorResult.cs b/src/Umbraco.Web.Common/ActionsResults/ValidationErrorResult.cs
index 8fe0ef9326..378be18440 100644
--- a/src/Umbraco.Web.Common/ActionsResults/ValidationErrorResult.cs
+++ b/src/Umbraco.Web.Common/ActionsResults/ValidationErrorResult.cs
@@ -1,10 +1,19 @@
-using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.Common.ActionsResults
{
+ // TODO: This should probably follow the same conventions as in aspnet core and use ProblemDetails
+ // and ProblemDetails factory. See https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ControllerBase.cs#L1977
+ // ProblemDetails is explicitly checked for in the application model.
+ // In our base class UmbracoAuthorizedApiController the logic is there to create a ProblemDetails.
+ // However, to do this will require changing how angular deals with errors since the response will
+ // probably be different. Would just be better to follow the aspnet patterns.
+
///
/// Custom result to return a validation error message with required headers
///
@@ -13,6 +22,11 @@ namespace Umbraco.Cms.Web.Common.ActionsResults
///
public class ValidationErrorResult : ObjectResult
{
+ ///
+ /// Typically this should not be used and just use the ValidationProblem method on the base controller class.
+ ///
+ ///
+ ///
public static ValidationErrorResult CreateNotificationValidationErrorResult(string errorMessage)
{
var notificationModel = new SimpleNotificationModel
@@ -23,6 +37,9 @@ namespace Umbraco.Cms.Web.Common.ActionsResults
return new ValidationErrorResult(notificationModel);
}
+ public ValidationErrorResult(ModelStateDictionary modelState)
+ : this(new SimpleValidationModel(modelState.ToErrorDictionary())) { }
+
public ValidationErrorResult(object value, int statusCode) : base(value)
{
StatusCode = statusCode;
@@ -32,6 +49,7 @@ namespace Umbraco.Cms.Web.Common.ActionsResults
{
}
+ // TODO: Like here, shouldn't we use ProblemDetails?
public ValidationErrorResult(string errorMessage, int statusCode) : base(new { Message = errorMessage })
{
StatusCode = statusCode;
diff --git a/src/Umbraco.Web.Common/Extensions/ModelStateExtensions.cs b/src/Umbraco.Web.Common/Extensions/ModelStateExtensions.cs
new file mode 100644
index 0000000000..acc2858ece
--- /dev/null
+++ b/src/Umbraco.Web.Common/Extensions/ModelStateExtensions.cs
@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+
+namespace Umbraco.Extensions
+{
+ public static class ModelStateExtensions
+ {
+ ///
+ /// Checks if there are any model errors on any fields containing the prefix
+ ///
+ ///
+ ///
+ ///
+ public static bool IsValid(this ModelStateDictionary state, string prefix) =>
+ state.Where(v => v.Key.StartsWith(prefix + ".")).All(v => !v.Value.Errors.Any());
+
+ public static IDictionary ToErrorDictionary(this ModelStateDictionary modelState)
+ {
+ var modelStateError = new Dictionary();
+ foreach (KeyValuePair keyModelStatePair in modelState)
+ {
+ var key = keyModelStatePair.Key;
+ ModelErrorCollection errors = keyModelStatePair.Value.Errors;
+ if (errors != null && errors.Count > 0)
+ {
+ modelStateError.Add(key, errors.Select(error => error.ErrorMessage));
+ }
+ }
+ return modelStateError;
+ }
+
+ ///
+ /// Serializes the ModelState to JSON for JavaScript to interrogate the errors
+ ///
+ ///
+ ///
+ public static JsonResult ToJsonErrors(this ModelStateDictionary state) =>
+ new JsonResult(new
+ {
+ success = state.IsValid.ToString().ToLower(),
+ failureType = "ValidationError",
+ validationErrors = from e in state
+ where e.Value.Errors.Count > 0
+ select new
+ {
+ name = e.Key,
+ errors = e.Value.Errors.Select(x => x.ErrorMessage)
+ .Concat(
+ e.Value.Errors.Where(x => x.Exception != null).Select(x => x.Exception.Message))
+ }
+ });
+ }
+}