Merge pull request #10534 from umbraco/v9/task/cleanup-validation-results
Streamlines response handling in controllers
This commit is contained in:
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// returns a 200 OK response with a notification message
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
protected OkObjectResult Ok(string message)
|
||||
{
|
||||
var notificationModel = new SimpleNotificationModel
|
||||
{
|
||||
Message = message
|
||||
};
|
||||
notificationModel.AddSuccessNotification(message, string.Empty);
|
||||
|
||||
return new OkObjectResult(notificationModel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to ensure that the error message is an error notification message
|
||||
/// </summary>
|
||||
/// <param name="errorMessage"></param>
|
||||
/// <returns></returns>
|
||||
protected override ActionResult ValidationProblem(string errorMessage)
|
||||
=> ValidationProblem(errorMessage, string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a notofication validation problem with a header and message
|
||||
/// </summary>
|
||||
/// <param name="errorHeader"></param>
|
||||
/// <param name="errorMessage"></param>
|
||||
/// <returns></returns>
|
||||
protected ActionResult ValidationProblem(string errorHeader, string errorMessage)
|
||||
{
|
||||
var notificationModel = new SimpleNotificationModel
|
||||
{
|
||||
Message = errorMessage
|
||||
};
|
||||
notificationModel.AddErrorNotification(errorHeader, errorMessage);
|
||||
return new ValidationErrorResult(notificationModel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to ensure that all queued notifications are sent to the back office
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[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());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
|
||||
[PrefixlessBodyModelValidator]
|
||||
public abstract class ContentTypeControllerBase<TContentType> : UmbracoAuthorizedJsonController
|
||||
public abstract class ContentTypeControllerBase<TContentType> : 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<TContentTypeSave, TContentTypeDisplay>(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<TContentTypeDisplay, TContentTypeSave, TPropertyType>(ex, contentTypeSave, ct, ctId);
|
||||
if (responseEx != null) return new ValidationErrorResult(responseEx);
|
||||
if (responseEx != null) return ValidationProblem(responseEx);
|
||||
}
|
||||
|
||||
var exResult = CreateCompositionValidationExceptionIfInvalid<TContentTypeSave, TPropertyType, TContentTypeDisplay>(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<TContentTypeSave, TPropertyType, TContentTypeDisplay>(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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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<int> 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<string, string> { { "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<string, string> { { "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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Language> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<IEnumerable<LogTemplate>>(_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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<MediaItemDisplay>(contentItem.PersistedContent);
|
||||
forDisplay.Errors = ModelState.ToErrorDictionary();
|
||||
return new ValidationErrorResult(forDisplay);
|
||||
MediaItemDisplay forDisplay = _umbracoMapper.Map<MediaItemDisplay>(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"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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<MemberDisplay>(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<int> intId = identityMember.Id.TryConvertTo<int>();
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -70,14 +70,16 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
public ActionResult<PackageDefinition> 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<PackageDefinition> GetInstalledPackageById(int id)
|
||||
{
|
||||
var pack = _packagingService.GetInstalledPackageById(id);
|
||||
if (pack == null) return NotFound();
|
||||
if (pack == null)
|
||||
return NotFound();
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a validation problem result for the <see cref="IErrorModel"/> and the <see cref="ModelStateDictionary"/>
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="modelStateDictionary"></param>
|
||||
/// <param name="statusCode"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual ActionResult ValidationProblem(IErrorModel model, ModelStateDictionary modelStateDictionary, int statusCode = StatusCodes.Status400BadRequest)
|
||||
{
|
||||
model.Errors = modelStateDictionary.ToErrorDictionary();
|
||||
return ValidationProblem(model, statusCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to return Umbraco compatible errors
|
||||
/// </summary>
|
||||
/// <param name="modelStateDictionary"></param>
|
||||
/// <returns></returns>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an Umbraco compatible validation problem for the given error message
|
||||
/// </summary>
|
||||
/// <param name="errorMessage"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual ActionResult ValidationProblem(string errorMessage)
|
||||
{
|
||||
ValidationProblemDetails problemDetails = GetValidationProblemDetails(errorMessage);
|
||||
return new ValidationErrorResult(problemDetails);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an Umbraco compatible validation problem for the object result
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="statusCode"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual ActionResult ValidationProblem(object value, int statusCode = StatusCodes.Status400BadRequest)
|
||||
=> new ValidationErrorResult(value, statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IUser>(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<int>();
|
||||
@@ -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<int> intId = changingPasswordModel.Id.TryConvertTo<int>();
|
||||
@@ -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<PasswordChangedModel> 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 }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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 }));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if there are any model errors on any fields containing the prefix
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="prefix"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsValid(this ModelStateDictionary state, string prefix) =>
|
||||
state.Where(v => v.Key.StartsWith(prefix + ".")).All(v => !v.Value.Errors.Any());
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <see cref="ValidationResult"/> to the model state with the appropriate keys for property errors
|
||||
@@ -171,42 +164,5 @@ namespace Umbraco.Cms.Web.BackOffice.Extensions
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static IDictionary<string, object> ToErrorDictionary(this ModelStateDictionary modelState)
|
||||
{
|
||||
var modelStateError = new Dictionary<string, object>();
|
||||
foreach (KeyValuePair<string, ModelStateEntry> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the ModelState to JSON for JavaScript to interrogate the errors
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
/// <returns></returns>
|
||||
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))
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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.
|
||||
|
||||
/// <summary>
|
||||
/// Custom result to return a validation error message with required headers
|
||||
/// </summary>
|
||||
@@ -13,6 +22,11 @@ namespace Umbraco.Cms.Web.Common.ActionsResults
|
||||
/// </remarks>
|
||||
public class ValidationErrorResult : ObjectResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Typically this should not be used and just use the ValidationProblem method on the base controller class.
|
||||
/// </summary>
|
||||
/// <param name="errorMessage"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
|
||||
55
src/Umbraco.Web.Common/Extensions/ModelStateExtensions.cs
Normal file
55
src/Umbraco.Web.Common/Extensions/ModelStateExtensions.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if there are any model errors on any fields containing the prefix
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="prefix"></param>
|
||||
/// <returns></returns>
|
||||
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<string, object> ToErrorDictionary(this ModelStateDictionary modelState)
|
||||
{
|
||||
var modelStateError = new Dictionary<string, object>();
|
||||
foreach (KeyValuePair<string, ModelStateEntry> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the ModelState to JSON for JavaScript to interrogate the errors
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
/// <returns></returns>
|
||||
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))
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user