Merge branch 'v13/dev' into v13/feature/extension-model
# Conflicts: # src/Umbraco.Cms.Api.Management/Controllers/Package/PackageControllerBase.cs # src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs # src/Umbraco.Cms.Api.Management/ManagementApiComposer.cs # src/Umbraco.Core/CompatibilitySuppressions.xml # src/Umbraco.Infrastructure/CompatibilitySuppressions.xml # src/Umbraco.Infrastructure/Serialization/ContextualJsonSerializer.cs # src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -68,7 +68,11 @@ preserve.belle
|
||||
/build/docs.zip
|
||||
/build/ui-docs.zip
|
||||
/build/csharp-docs.zip
|
||||
/src/Umbraco.Cms.StaticAssets/wwwroot/umbraco/
|
||||
/src/Umbraco.Cms.StaticAssets/wwwroot/umbraco/backoffice
|
||||
/src/Umbraco.Cms.StaticAssets/wwwroot/umbraco/assets
|
||||
/src/Umbraco.Cms.StaticAssets/wwwroot/umbraco/js
|
||||
/src/Umbraco.Cms.StaticAssets/wwwroot/umbraco/lib
|
||||
/src/Umbraco.Cms.StaticAssets/wwwroot/umbraco/views
|
||||
|
||||
# Environment specific data
|
||||
/src/Umbraco.Web.UI.Client/[Bb]uild/
|
||||
|
||||
@@ -39,6 +39,7 @@ stages:
|
||||
## Build
|
||||
###############################################
|
||||
- stage: Build
|
||||
|
||||
variables:
|
||||
npm_config_cache: $(Pipeline.Workspace)/.npm_client
|
||||
jobs:
|
||||
@@ -47,6 +48,8 @@ stages:
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: true
|
||||
- task: NodeTool@0
|
||||
displayName: Use Node.js $(nodeVersion)
|
||||
inputs:
|
||||
@@ -110,7 +113,7 @@ stages:
|
||||
}
|
||||
}
|
||||
|
||||
foreach($csproj in Get-ChildItem –Path "src/" -Recurse -Filter *.csproj)
|
||||
foreach($csproj in Get-ChildItem -Recurse -Filter *.csproj)
|
||||
{
|
||||
dotnet pack $csproj --configuration $(buildConfiguration) --no-build --output $(Build.ArtifactStagingDirectory)/nupkg
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ public class ProblemDetailsBuilder
|
||||
{
|
||||
private string? _title;
|
||||
private string? _detail;
|
||||
private int _status = StatusCodes.Status400BadRequest;
|
||||
private string? _type;
|
||||
|
||||
public ProblemDetailsBuilder WithTitle(string title)
|
||||
@@ -22,12 +21,6 @@ public class ProblemDetailsBuilder
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProblemDetailsBuilder WithStatus(int status)
|
||||
{
|
||||
_status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProblemDetailsBuilder WithType(string type)
|
||||
{
|
||||
_type = type;
|
||||
@@ -39,7 +32,6 @@ public class ProblemDetailsBuilder
|
||||
{
|
||||
Title = _title,
|
||||
Detail = _detail,
|
||||
Status = _status,
|
||||
Type = _type ?? "Error",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.AuditLog;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("audit-log")]
|
||||
[ApiExplorerSettings(GroupName = "Audit Log")]
|
||||
[ApiVersion("1.0")]
|
||||
public class AuditLogControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.AuditLogs;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.New.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.AuditLog;
|
||||
|
||||
public class ByKeyAuditLogController : AuditLogControllerBase
|
||||
{
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IAuditLogViewModelFactory _auditLogViewModelFactory;
|
||||
|
||||
public ByKeyAuditLogController(IAuditService auditService, IAuditLogViewModelFactory auditLogViewModelFactory)
|
||||
{
|
||||
_auditService = auditService;
|
||||
_auditLogViewModelFactory = auditLogViewModelFactory;
|
||||
}
|
||||
|
||||
[HttpGet("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<AuditLogResponseModel>), StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> ByKey(Guid key, Direction orderDirection = Direction.Descending, DateTime? sinceDate = null, int skip = 0, int take = 100)
|
||||
{
|
||||
PagedModel<IAuditItem> result = await _auditService.GetItemsByKeyAsync(key, skip, take, orderDirection, sinceDate);
|
||||
IEnumerable<AuditLogResponseModel> mapped = _auditLogViewModelFactory.CreateAuditLogViewModel(result.Items);
|
||||
var viewModel = new PagedViewModel<AuditLogResponseModel>
|
||||
{
|
||||
Total = result.Total,
|
||||
Items = mapped,
|
||||
};
|
||||
|
||||
return Ok(viewModel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.AuditLogs;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.AuditLog;
|
||||
|
||||
public class ByTypeAuditLogController : AuditLogControllerBase
|
||||
{
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IAuditLogViewModelFactory _auditLogViewModelFactory;
|
||||
|
||||
public ByTypeAuditLogController(IAuditService auditService, IAuditLogViewModelFactory auditLogViewModelFactory)
|
||||
{
|
||||
_auditService = auditService;
|
||||
_auditLogViewModelFactory = auditLogViewModelFactory;
|
||||
}
|
||||
|
||||
[HttpGet("type/{logType}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<AuditLogResponseModel>), StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> ByType(AuditType logType, DateTime? sinceDate = null, int skip = 0, int take = 100)
|
||||
{
|
||||
IAuditItem[] result = _auditService.GetLogs(logType, sinceDate).ToArray();
|
||||
IEnumerable<AuditLogResponseModel> mapped = _auditLogViewModelFactory.CreateAuditLogViewModel(result.Skip(skip).Take(take));
|
||||
var viewModel = new PagedViewModel<AuditLogResponseModel>
|
||||
{
|
||||
Total = result.Length,
|
||||
Items = mapped,
|
||||
};
|
||||
|
||||
return await Task.FromResult(Ok(viewModel));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.AuditLogs;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Exceptions;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
using Umbraco.New.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.AuditLog;
|
||||
|
||||
public class CurrentUserAuditLogController : AuditLogControllerBase
|
||||
{
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IAuditLogViewModelFactory _auditLogViewModelFactory;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public CurrentUserAuditLogController(
|
||||
IAuditService auditService,
|
||||
IAuditLogViewModelFactory auditLogViewModelFactory,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
IUserService userService)
|
||||
{
|
||||
_auditService = auditService;
|
||||
_auditLogViewModelFactory = auditLogViewModelFactory;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<AuditLogWithUsernameResponseModel>), StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> CurrentUser(Direction orderDirection = Direction.Descending, DateTime? sinceDate = null, int skip = 0, int take = 100)
|
||||
{
|
||||
// FIXME: Pull out current backoffice user when its implemented.
|
||||
// var userId = _backOfficeSecurityAccessor.BackOfficeSecurity?.GetUserId().Result ?? -1;
|
||||
var userId = Constants.Security.SuperUserId;
|
||||
|
||||
IUser? user = _userService.GetUserById(userId);
|
||||
|
||||
if (user is null)
|
||||
{
|
||||
throw new PanicException("Could not find current user");
|
||||
}
|
||||
|
||||
PagedModel<IAuditItem> result = await _auditService.GetPagedItemsByUserAsync(
|
||||
user.Key,
|
||||
skip,
|
||||
take,
|
||||
orderDirection,
|
||||
null,
|
||||
sinceDate);
|
||||
|
||||
IEnumerable<AuditLogWithUsernameResponseModel> mapped = _auditLogViewModelFactory.CreateAuditLogWithUsernameViewModels(result.Items.Skip(skip).Take(take));
|
||||
var viewModel = new PagedViewModel<AuditLogWithUsernameResponseModel>
|
||||
{
|
||||
Total = result.Total,
|
||||
Items = mapped,
|
||||
};
|
||||
|
||||
return Ok(viewModel);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.Builders;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Document;
|
||||
|
||||
public class ByKeyDocumentController : DocumentControllerBase
|
||||
{
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IDocumentViewModelFactory _documentViewModelFactory;
|
||||
|
||||
public ByKeyDocumentController(IContentService contentService, IDocumentViewModelFactory documentViewModelFactory)
|
||||
{
|
||||
_contentService = contentService;
|
||||
_documentViewModelFactory = documentViewModelFactory;
|
||||
}
|
||||
|
||||
[HttpGet("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(DocumentViewModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ByKey(Guid key)
|
||||
{
|
||||
// FIXME: create and use an async get method here.
|
||||
IContent? content = _contentService.GetById(key);
|
||||
if (content == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
DocumentViewModel model = await _documentViewModelFactory.CreateViewModelAsync(content);
|
||||
return Ok(model);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Document;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute(Constants.UdiEntityType.Document)]
|
||||
[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Document))]
|
||||
public abstract class DocumentControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DocumentType;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.DocumentType;
|
||||
|
||||
public class ByKeyDocumentTypeController : DocumentTypeControllerBase
|
||||
{
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public ByKeyDocumentTypeController(IContentTypeService contentTypeService, IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_contentTypeService = contentTypeService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
[HttpGet("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(DocumentTypeViewModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ByKey(Guid key)
|
||||
{
|
||||
// FIXME: create and use an async get method here.
|
||||
IContentType? contentType = _contentTypeService.Get(key);
|
||||
if (contentType == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
DocumentTypeViewModel model = _umbracoMapper.Map<DocumentTypeViewModel>(contentType)!;
|
||||
return await Task.FromResult(Ok(model));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.DocumentType;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute(Constants.UdiEntityType.DocumentType)]
|
||||
[ApiExplorerSettings(GroupName = "Document Type")]
|
||||
public abstract class DocumentTypeControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.LogViewer;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Logging.Viewer;
|
||||
@@ -29,7 +30,7 @@ public class LogLevelCountLogViewerController : LogViewerControllerBase
|
||||
[HttpGet("level-count")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(LogLevelCountsViewModel), StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> LogLevelCounts(DateTime? startDate = null, DateTime? endDate = null)
|
||||
{
|
||||
Attempt<LogLevelCounts?, LogViewerOperationStatus> logLevelCountsAttempt =
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Package;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.New.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Package;
|
||||
|
||||
public class AllMigrationStatusPackageController : PackageControllerBase
|
||||
{
|
||||
private readonly IPackagingService _packagingService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public AllMigrationStatusPackageController(IPackagingService packagingService, IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_packagingService = packagingService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a paginated list of the migration status of each installed package.
|
||||
/// </summary>
|
||||
/// <param name="skip">The amount of items to skip.</param>
|
||||
/// <param name="take">The amount of items to take.</param>
|
||||
/// <returns>The paged result of the installed packages migration status.</returns>
|
||||
[HttpGet("migration-status")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<PackageMigrationStatusViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<PackageMigrationStatusViewModel>>> AllMigrationStatuses(int skip = 0, int take = 100)
|
||||
{
|
||||
PagedModel<InstalledPackage> migrationPlans = await _packagingService.GetInstalledPackagesFromMigrationPlansAsync(skip, take);
|
||||
|
||||
IEnumerable<PackageMigrationStatusViewModel> viewModels = _umbracoMapper.MapEnumerable<InstalledPackage, PackageMigrationStatusViewModel>(migrationPlans.Items);
|
||||
|
||||
return Ok(new PagedViewModel<PackageMigrationStatusViewModel>()
|
||||
{
|
||||
Total = migrationPlans.Total,
|
||||
Items = viewModels,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Package;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Package.Created;
|
||||
|
||||
public class AllCreatedPackageController : CreatedPackageControllerBase
|
||||
{
|
||||
private readonly IPackagingService _packagingService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public AllCreatedPackageController(IPackagingService packagingService, IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_packagingService = packagingService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a paginated list of all created packages.
|
||||
/// </summary>
|
||||
/// <param name="skip">The amount of items to skip.</param>
|
||||
/// <param name="take">The amount of items to take.</param>
|
||||
/// <returns>The paged result of the created packages.</returns>
|
||||
[HttpGet]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<PackageDefinitionViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<PackageDefinitionViewModel>>> All(int skip = 0, int take = 100)
|
||||
{
|
||||
IEnumerable<PackageDefinition> createdPackages = _packagingService
|
||||
.GetAllCreatedPackages()
|
||||
.WhereNotNull()
|
||||
.Skip(skip)
|
||||
.Take(take);
|
||||
|
||||
return await Task.FromResult(Ok(_umbracoMapper.Map<PagedViewModel<PackageDefinitionViewModel>>(createdPackages)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Package;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Package.Created;
|
||||
|
||||
public class ByKeyCreatedPackageController : CreatedPackageControllerBase
|
||||
{
|
||||
private readonly IPackagingService _packagingService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public ByKeyCreatedPackageController(IPackagingService packagingService, IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_packagingService = packagingService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a package by key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the package.</param>
|
||||
/// <returns>The package or not found result.</returns>
|
||||
[HttpGet("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(PackageDefinitionViewModel), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PackageDefinitionViewModel>> ByKey(Guid key)
|
||||
{
|
||||
PackageDefinition? package = await _packagingService.GetCreatedPackageByKeyAsync(key);
|
||||
|
||||
if (package is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(_umbracoMapper.Map<PackageDefinitionViewModel>(package));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Package;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Package.Created;
|
||||
|
||||
public class CreateCreatedPackageController : CreatedPackageControllerBase
|
||||
{
|
||||
private readonly IPackagingService _packagingService;
|
||||
private readonly IPackageDefinitionFactory _packageDefinitionFactory;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
public CreateCreatedPackageController(
|
||||
IPackagingService packagingService,
|
||||
IPackageDefinitionFactory packageDefinitionFactory,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
{
|
||||
_packagingService = packagingService;
|
||||
_packageDefinitionFactory = packageDefinitionFactory;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a package.
|
||||
/// </summary>
|
||||
/// <param name="packageCreateModel">The model containing the data for a new package.</param>
|
||||
/// <returns>The created package.</returns>
|
||||
[HttpPost]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||
public async Task<IActionResult> Create(PackageCreateModel packageCreateModel)
|
||||
{
|
||||
PackageDefinition packageDefinition = _packageDefinitionFactory.CreatePackageDefinition(packageCreateModel);
|
||||
|
||||
Attempt<PackageDefinition, PackageOperationStatus> result = await _packagingService.CreateCreatedPackageAsync(packageDefinition, CurrentUserId(_backOfficeSecurityAccessor));
|
||||
|
||||
return result.Success
|
||||
? CreatedAtAction<ByKeyCreatedPackageController>(controller => nameof(controller.ByKey), packageDefinition.PackageId)
|
||||
: PackageOperationStatusResult(result.Status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Package.Created;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("package/created")]
|
||||
[ApiExplorerSettings(GroupName = "Package")]
|
||||
[ApiVersion("1.0")]
|
||||
public class CreatedPackageControllerBase : PackageControllerBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Package.Created;
|
||||
|
||||
public class DeleteCreatedPackageController : CreatedPackageControllerBase
|
||||
{
|
||||
private readonly IPackagingService _packagingService;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
public DeleteCreatedPackageController(IPackagingService packagingService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
{
|
||||
_packagingService = packagingService;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a package with a given key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the package.</param>
|
||||
/// <returns>The result of the deletion.</returns>
|
||||
[HttpDelete("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> Delete(Guid key)
|
||||
{
|
||||
Attempt<PackageDefinition?, PackageOperationStatus> result =
|
||||
await _packagingService.DeleteCreatedPackageAsync(key, CurrentUserId(_backOfficeSecurityAccessor));
|
||||
|
||||
return result.Success
|
||||
? Ok()
|
||||
: PackageOperationStatusResult(result.Status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System.Net;
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Package.Created;
|
||||
|
||||
public class DownloadCreatedPackageController : CreatedPackageControllerBase
|
||||
{
|
||||
private readonly IPackagingService _packagingService;
|
||||
|
||||
public DownloadCreatedPackageController(IPackagingService packagingService) => _packagingService = packagingService;
|
||||
|
||||
/// <summary>
|
||||
/// Downloads a package XML or ZIP file.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the package.</param>
|
||||
/// <returns>The XML or ZIP file of the package or not found result.</returns>
|
||||
[HttpGet("{key:guid}/download")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> Download(Guid key)
|
||||
{
|
||||
PackageDefinition? package = await _packagingService.GetCreatedPackageByKeyAsync(key);
|
||||
|
||||
if (package is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
Stream? fileStream = _packagingService.GetPackageFileStream(package);
|
||||
if (fileStream is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var fileName = Path.GetFileName(package.PackagePath);
|
||||
Encoding encoding = Encoding.UTF8;
|
||||
|
||||
var contentDisposition = new ContentDisposition
|
||||
{
|
||||
FileName = WebUtility.UrlEncode(fileName),
|
||||
DispositionType = DispositionTypeNames.Attachment
|
||||
};
|
||||
|
||||
Response.Headers.Add("Content-Disposition", contentDisposition.ToString());
|
||||
|
||||
var result = new FileStreamResult(
|
||||
fileStream,
|
||||
new MediaTypeHeaderValue(MediaTypeNames.Application.Octet) { Charset = encoding.WebName });
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Package;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Package.Created;
|
||||
|
||||
public class UpdateCreatedPackageController : CreatedPackageControllerBase
|
||||
{
|
||||
private readonly IPackagingService _packagingService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
public UpdateCreatedPackageController(
|
||||
IPackagingService packagingService,
|
||||
IUmbracoMapper umbracoMapper,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
{
|
||||
_packagingService = packagingService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a package.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the package.</param>
|
||||
/// <param name="packageUpdateModel">The model containing the data for updating a package.</param>
|
||||
/// <returns>The created package.</returns>
|
||||
[HttpPut("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> Update(Guid key, PackageUpdateModel packageUpdateModel)
|
||||
{
|
||||
PackageDefinition? package = await _packagingService.GetCreatedPackageByKeyAsync(key);
|
||||
|
||||
if (package is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// Macros are not included!
|
||||
PackageDefinition packageDefinition = _umbracoMapper.Map(packageUpdateModel, package);
|
||||
|
||||
Attempt<PackageDefinition, PackageOperationStatus> result = await _packagingService.UpdateCreatedPackageAsync(packageDefinition, CurrentUserId(_backOfficeSecurityAccessor));
|
||||
|
||||
return result.Success
|
||||
? Ok()
|
||||
: PackageOperationStatusResult(result.Status);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.Builders;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Package;
|
||||
|
||||
@@ -9,4 +12,29 @@ namespace Umbraco.Cms.Api.Management.Controllers.Package;
|
||||
[ApiVersion("1.0")]
|
||||
public abstract class PackageControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
protected IActionResult PackageOperationStatusResult(PackageOperationStatus status) =>
|
||||
status switch
|
||||
{
|
||||
PackageOperationStatus.NotFound => NotFound("The package could not be found"),
|
||||
PackageOperationStatus.DuplicateItemName => Conflict(new ProblemDetailsBuilder()
|
||||
.WithTitle("Duplicate package name")
|
||||
.WithDetail("Another package already exists with the attempted name.")
|
||||
.Build()),
|
||||
PackageOperationStatus.InvalidName => BadRequest(new ProblemDetailsBuilder()
|
||||
.WithTitle("Invalid package name")
|
||||
.WithDetail("The attempted package name does not represent a valid name for a package.")
|
||||
.Build()),
|
||||
_ => StatusCode(StatusCodes.Status500InternalServerError, "Unknown package operation status")
|
||||
};
|
||||
|
||||
protected IActionResult PackageMigrationOperationStatusResult(PackageMigrationOperationStatus status) =>
|
||||
status switch
|
||||
{
|
||||
PackageMigrationOperationStatus.NotFound => NotFound("No migration plans were found for that package"),
|
||||
PackageMigrationOperationStatus.CancelledByFailedMigration => Conflict(new ProblemDetailsBuilder()
|
||||
.WithTitle("Package migration failed")
|
||||
.WithDetail("Check log for full details about the failed migration.")
|
||||
.Build()),
|
||||
_ => StatusCode(StatusCodes.Status500InternalServerError, "Unknown package migration operation status")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
using Umbraco.Cms.Infrastructure.Install;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Package;
|
||||
|
||||
public class RunMigrationPackageController : PackageControllerBase
|
||||
{
|
||||
private readonly PackageMigrationRunner _packageMigrationRunner;
|
||||
|
||||
public RunMigrationPackageController(PackageMigrationRunner packageMigrationRunner)
|
||||
=> _packageMigrationRunner = packageMigrationRunner;
|
||||
|
||||
/// <summary>
|
||||
/// Runs all migration plans for a package with a given name if any are pending.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the package.</param>
|
||||
/// <returns>The result of running the package migrations.</returns>
|
||||
[HttpPost("{name}/run-migration")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status409Conflict)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> RunMigrations(string name)
|
||||
{
|
||||
Attempt<bool, PackageMigrationOperationStatus> result = await _packageMigrationRunner.RunPendingPackageMigrations(name);
|
||||
|
||||
return result.Success
|
||||
? Ok()
|
||||
: PackageMigrationOperationStatusResult(result.Status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Profiling;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Profiling;
|
||||
|
||||
public class GetStatusProfilingController : ProfilingControllerBase
|
||||
{
|
||||
private readonly IWebProfilerService _webProfilerService;
|
||||
|
||||
public GetStatusProfilingController(IWebProfilerService webProfilerService) => _webProfilerService = webProfilerService;
|
||||
|
||||
[HttpGet("status")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProfilingStatusViewModel), StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> Status()
|
||||
{
|
||||
var result = await _webProfilerService.GetStatus();
|
||||
return result.Success
|
||||
? Ok(new ProfilingStatusViewModel(result.Result))
|
||||
: WebProfilerOperationStatusResult(result.Status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.Builders;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Profiling;
|
||||
|
||||
@@ -9,4 +12,15 @@ namespace Umbraco.Cms.Api.Management.Controllers.Profiling;
|
||||
[ApiExplorerSettings(GroupName = "Profiling")]
|
||||
public class ProfilingControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
|
||||
protected IActionResult WebProfilerOperationStatusResult(WebProfilerOperationStatus status) =>
|
||||
status switch
|
||||
{
|
||||
WebProfilerOperationStatus.ExecutingUserNotFound => Unauthorized(new ProblemDetailsBuilder()
|
||||
.WithTitle("Executing user not found")
|
||||
.WithDetail("Executing this action requires a signed in user.")
|
||||
.Build()),
|
||||
|
||||
_ => StatusCode(StatusCodes.Status500InternalServerError, "Unknown profiling operation status")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Profiling;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Profiling;
|
||||
|
||||
public class StatusProfilingController : ProfilingControllerBase
|
||||
{
|
||||
private readonly IHostingEnvironment _hosting;
|
||||
|
||||
public StatusProfilingController(IHostingEnvironment hosting) => _hosting = hosting;
|
||||
|
||||
[HttpGet("status")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProfilingStatusViewModel), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<ProfilingStatusViewModel>> Status()
|
||||
=> await Task.FromResult(Ok(new ProfilingStatusViewModel(_hosting.IsDebugMode)));
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Profiling;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Profiling;
|
||||
|
||||
public class UpdateStatusProfilingController : ProfilingControllerBase
|
||||
{
|
||||
private readonly IWebProfilerService _webProfilerService;
|
||||
|
||||
public UpdateStatusProfilingController(IWebProfilerService webProfilerService) => _webProfilerService = webProfilerService;
|
||||
|
||||
[HttpPut("status")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> Status(ProfilingStatusViewModel model)
|
||||
{
|
||||
var result = await _webProfilerService.SetStatus(model.Enabled);
|
||||
return result.Success
|
||||
? Ok()
|
||||
: WebProfilerOperationStatusResult(result.Status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,16 +27,16 @@ public class ByIdTrackedReferenceController : TrackedReferenceControllerBase
|
||||
/// Used by info tabs on content, media etc. and for the delete and unpublish of single items.
|
||||
/// This is basically finding parents of relations.
|
||||
/// </remarks>
|
||||
[HttpGet("{id:int}")]
|
||||
[HttpGet("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<RelationItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<RelationItemViewModel>>> Get(
|
||||
int id,
|
||||
long skip,
|
||||
long take,
|
||||
bool? filterMustBeIsDependency)
|
||||
Guid key,
|
||||
long skip = 0,
|
||||
long take = 20,
|
||||
bool filterMustBeIsDependency = false)
|
||||
{
|
||||
PagedModel<RelationItemModel> relationItems = _trackedReferencesService.GetPagedRelationsForItem(id, skip, take, filterMustBeIsDependency ?? false);
|
||||
PagedModel<RelationItemModel> relationItems = await _trackedReferencesService.GetPagedRelationsForItemAsync(key, skip, take, filterMustBeIsDependency);
|
||||
|
||||
var pagedViewModel = new PagedViewModel<RelationItemViewModel>
|
||||
{
|
||||
|
||||
@@ -28,12 +28,12 @@ public class DescendantsTrackedReferenceController : TrackedReferenceControllerB
|
||||
/// kind of relation.
|
||||
/// This is basically finding the descending items which are children in relations.
|
||||
/// </remarks>
|
||||
[HttpGet("descendants/{parentId:int}")]
|
||||
[HttpGet("descendants/{parentKey:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<RelationItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<RelationItemViewModel>>> Descendants(int parentId, long skip, long take, bool? filterMustBeIsDependency)
|
||||
public async Task<ActionResult<PagedViewModel<RelationItemViewModel>>> Descendants(Guid parentKey, long skip, long take, bool filterMustBeIsDependency = true)
|
||||
{
|
||||
PagedModel<RelationItemModel> relationItems = _trackedReferencesSkipTakeService.GetPagedDescendantsInReferences(parentId, skip, take, filterMustBeIsDependency ?? true);
|
||||
PagedModel<RelationItemModel> relationItems = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(parentKey, skip, take, filterMustBeIsDependency);
|
||||
var pagedViewModel = new PagedViewModel<RelationItemViewModel>
|
||||
{
|
||||
Total = relationItems.Total,
|
||||
|
||||
@@ -21,7 +21,7 @@ public class ItemsTrackedReferenceController : TrackedReferenceControllerBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a page list of the items used in any kind of relation from selected integer ids.
|
||||
/// Gets a page list of the items used in any kind of relation from selected keys.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Used when bulk deleting content/media and bulk unpublishing content (delete and unpublish on List view).
|
||||
@@ -30,9 +30,9 @@ public class ItemsTrackedReferenceController : TrackedReferenceControllerBase
|
||||
[HttpGet("item")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<RelationItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<RelationItemViewModel>>> GetPagedReferencedItems([FromQuery]int[] ids, long skip, long take, bool? filterMustBeIsDependency)
|
||||
public async Task<ActionResult<PagedViewModel<RelationItemViewModel>>> GetPagedReferencedItems([FromQuery(Name="key")]SortedSet<Guid> keys, long skip = 0, long take = 20, bool filterMustBeIsDependency = true)
|
||||
{
|
||||
PagedModel<RelationItemModel> relationItems = _trackedReferencesSkipTakeService.GetPagedItemsWithRelations(ids, skip, take, filterMustBeIsDependency ?? true);
|
||||
PagedModel<RelationItemModel> relationItems = await _trackedReferencesSkipTakeService.GetPagedItemsWithRelationsAsync(keys, skip, take, filterMustBeIsDependency);
|
||||
var pagedViewModel = new PagedViewModel<RelationItemViewModel>
|
||||
{
|
||||
Total = relationItems.Total,
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.UserGroups;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.UserGroups;
|
||||
|
||||
|
||||
public class ByKeyUserGroupController : UserGroupsControllerBase
|
||||
{
|
||||
private readonly IUserGroupService _userGroupService;
|
||||
private readonly IUserGroupViewModelFactory _userGroupViewModelFactory;
|
||||
|
||||
public ByKeyUserGroupController(
|
||||
IUserGroupService userGroupService,
|
||||
IUserGroupViewModelFactory userGroupViewModelFactory)
|
||||
{
|
||||
_userGroupService = userGroupService;
|
||||
_userGroupViewModelFactory = userGroupViewModelFactory;
|
||||
}
|
||||
|
||||
[HttpGet("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(UserGroupViewModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<UserGroupViewModel>> ByKey(Guid key)
|
||||
{
|
||||
IUserGroup? userGroup = await _userGroupService.GetAsync(key);
|
||||
|
||||
if (userGroup is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return await _userGroupViewModelFactory.CreateAsync(userGroup);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.UserGroups;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.UserGroups;
|
||||
|
||||
public class CreateUserGroupController : UserGroupsControllerBase
|
||||
{
|
||||
private readonly IUserGroupService _userGroupService;
|
||||
private readonly IUserGroupViewModelFactory _userGroupViewModelFactory;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
public CreateUserGroupController(
|
||||
IUserGroupService userGroupService,
|
||||
IUserGroupViewModelFactory userGroupViewModelFactory,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
{
|
||||
_userGroupService = userGroupService;
|
||||
_userGroupViewModelFactory = userGroupViewModelFactory;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||
public async Task<IActionResult> Create(UserGroupSaveModel userGroupSaveModel)
|
||||
{
|
||||
// FIXME: Comment this in when auth is in place and we can get a currently logged in user.
|
||||
// IUser? currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;
|
||||
// if (currentUser is null)
|
||||
// {
|
||||
// return UserGroupOperationStatusResult(UserGroupOperationStatus.MissingUser);
|
||||
// }
|
||||
|
||||
Attempt<IUserGroup, UserGroupOperationStatus> userGroupCreationAttempt = await _userGroupViewModelFactory.CreateAsync(userGroupSaveModel);
|
||||
if (userGroupCreationAttempt.Success is false)
|
||||
{
|
||||
return UserGroupOperationStatusResult(userGroupCreationAttempt.Status);
|
||||
}
|
||||
|
||||
IUserGroup group = userGroupCreationAttempt.Result;
|
||||
|
||||
Attempt<IUserGroup, UserGroupOperationStatus> result = await _userGroupService.CreateAsync(group, /*currentUser.Id*/ -1);
|
||||
return result.Success
|
||||
? CreatedAtAction<ByKeyUserGroupController>(controller => nameof(controller.ByKey), group.Key)
|
||||
: UserGroupOperationStatusResult(result.Status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.UserGroups;
|
||||
|
||||
public class DeleteUserGroupController : UserGroupsControllerBase
|
||||
{
|
||||
private readonly IUserGroupService _userGroupService;
|
||||
|
||||
public DeleteUserGroupController(IUserGroupService userGroupService)
|
||||
{
|
||||
_userGroupService = userGroupService;
|
||||
}
|
||||
|
||||
[HttpDelete("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> Delete(Guid key)
|
||||
{
|
||||
Attempt<UserGroupOperationStatus> result = await _userGroupService.DeleteAsync(key);
|
||||
|
||||
return result.Success
|
||||
? Ok()
|
||||
: UserGroupOperationStatusResult(result.Result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.UserGroups;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.New.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.UserGroups;
|
||||
|
||||
public class GetAllUserGroupController : UserGroupsControllerBase
|
||||
{
|
||||
private readonly IUserGroupService _userGroupService;
|
||||
private readonly IUserGroupViewModelFactory _userViewModelFactory;
|
||||
|
||||
public GetAllUserGroupController(
|
||||
IUserGroupService userGroupService,
|
||||
IUserGroupViewModelFactory userViewModelFactory)
|
||||
{
|
||||
_userGroupService = userGroupService;
|
||||
_userViewModelFactory = userViewModelFactory;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<UserGroupViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<UserGroupViewModel>>> GetAll(int skip = 0, int take = 100)
|
||||
{
|
||||
// FIXME: In the old controller this endpoint had a switch "onlyCurrentUserGroup"
|
||||
// If this was enabled we'd only return the groups the current user was in
|
||||
// and even if it was set to false we'd still remove the admin group.
|
||||
// We still need to have this functionality, however, it does not belong here.
|
||||
// Instead we should implement this functionality on the CurrentUserController
|
||||
PagedModel<IUserGroup> userGroups = await _userGroupService.GetAllAsync(skip, take);
|
||||
|
||||
var viewModels = (await _userViewModelFactory.CreateMultipleAsync(userGroups.Items)).ToList();
|
||||
return new PagedViewModel<UserGroupViewModel> { Total = userGroups.Total, Items = viewModels };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.UserGroups;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.UserGroups;
|
||||
|
||||
public class UpdateUserGroupController : UserGroupsControllerBase
|
||||
{
|
||||
private readonly IUserGroupService _userGroupService;
|
||||
private readonly IUserGroupViewModelFactory _userGroupViewModelFactory;
|
||||
|
||||
public UpdateUserGroupController(
|
||||
IUserGroupService userGroupService,
|
||||
IUserGroupViewModelFactory userGroupViewModelFactory)
|
||||
{
|
||||
_userGroupService = userGroupService;
|
||||
_userGroupViewModelFactory = userGroupViewModelFactory;
|
||||
}
|
||||
|
||||
[HttpPut("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> Update(Guid key, UserGroupUpdateModel dataTypeUpdateModel)
|
||||
{
|
||||
IUserGroup? existingUserGroup = await _userGroupService.GetAsync(key);
|
||||
|
||||
if (existingUserGroup is null)
|
||||
{
|
||||
return UserGroupOperationStatusResult(UserGroupOperationStatus.NotFound);
|
||||
}
|
||||
|
||||
Attempt<IUserGroup, UserGroupOperationStatus> userGroupUpdateAttempt = await _userGroupViewModelFactory.UpdateAsync(existingUserGroup, dataTypeUpdateModel);
|
||||
if (userGroupUpdateAttempt.Success is false)
|
||||
{
|
||||
return UserGroupOperationStatusResult(userGroupUpdateAttempt.Status);
|
||||
}
|
||||
|
||||
IUserGroup userGroup = userGroupUpdateAttempt.Result;
|
||||
Attempt<IUserGroup, UserGroupOperationStatus> result = await _userGroupService.UpdateAsync(userGroup, -1);
|
||||
|
||||
return result.Success
|
||||
? Ok()
|
||||
: UserGroupOperationStatusResult(result.Status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.Builders;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.UserGroups;
|
||||
|
||||
// TODO: This needs to be an authorized controller.
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("user-groups")]
|
||||
[ApiExplorerSettings(GroupName = "User Groups")]
|
||||
[ApiVersion("1.0")]
|
||||
public class UserGroupsControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
protected IActionResult UserGroupOperationStatusResult(UserGroupOperationStatus status) =>
|
||||
status switch
|
||||
{
|
||||
UserGroupOperationStatus.NotFound => NotFound("The user group could not be found"),
|
||||
UserGroupOperationStatus.AlreadyExists => Conflict(new ProblemDetailsBuilder()
|
||||
.WithTitle("User group already exists")
|
||||
.WithDetail("The user group exists already.")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.DuplicateAlias => Conflict(new ProblemDetailsBuilder()
|
||||
.WithTitle("Duplicate alias")
|
||||
.WithDetail("A user group already exists with the attempted alias.")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.MissingUser => Unauthorized(new ProblemDetailsBuilder()
|
||||
.WithTitle("Missing user")
|
||||
.WithDetail("A performing user was not found when attempting to create the user group.")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.IsSystemUserGroup => BadRequest(new ProblemDetailsBuilder()
|
||||
.WithTitle("System user group")
|
||||
.WithDetail("The operation is not allowed on a system user group.")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.UnauthorizedMissingUserSection => Unauthorized(new ProblemDetailsBuilder()
|
||||
.WithTitle("Unauthorized")
|
||||
.WithDetail("The performing user does not have access to the required section")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.UnauthorizedMissingSections => Unauthorized(new ProblemDetailsBuilder()
|
||||
.WithTitle("Unauthorized section")
|
||||
.WithDetail("The specified allowed section contained a section the performing user doesn't have access to.")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.UnauthorizedStartNodes => Unauthorized(new ProblemDetailsBuilder()
|
||||
.WithTitle("Unauthorized start node")
|
||||
.WithDetail("The specified start nodes contained a start node the performing user doesn't have access to.")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.UnauthorizedMissingUserGroup => Unauthorized(new ProblemDetailsBuilder()
|
||||
.WithTitle("User not in user group")
|
||||
.WithDetail("The current user is not in the user group")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.CancelledByNotification => BadRequest(new ProblemDetailsBuilder()
|
||||
.WithTitle("Cancelled by notification")
|
||||
.WithDetail("A notification handler prevented the language operation.")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.DocumentStartNodeKeyNotFound => NotFound(new ProblemDetailsBuilder()
|
||||
.WithTitle("Document start node key not found")
|
||||
.WithDetail("The assigned document start node does not exists.")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.MediaStartNodeKeyNotFound => NotFound(new ProblemDetailsBuilder()
|
||||
.WithTitle("Media start node key not found")
|
||||
.WithDetail("The assigned media start node does not exists.")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.LanguageNotFound => NotFound(new ProblemDetailsBuilder()
|
||||
.WithTitle("Language not found")
|
||||
.WithDetail("The specified language cannot be found.")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.NameTooLong => BadRequest(new ProblemDetailsBuilder()
|
||||
.WithTitle("Name too long")
|
||||
.WithDetail("User Group name is too long.")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.AliasTooLong => BadRequest(new ProblemDetailsBuilder()
|
||||
.WithTitle("Alias too long")
|
||||
.WithDetail("The user group alias is too long.")
|
||||
.Build()),
|
||||
UserGroupOperationStatus.MissingName => BadRequest(new ProblemDetailsBuilder()
|
||||
.WithTitle("Missing user group name.")
|
||||
.WithDetail("The user group name is required, and cannot be an empty string.")
|
||||
.Build()),
|
||||
_ => StatusCode(StatusCodes.Status500InternalServerError, "Unknown user group operation status."),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
|
||||
internal static class AuditLogBuilderExtensions
|
||||
{
|
||||
internal static IUmbracoBuilder AddAuditLogs(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddTransient<IAuditLogViewModelFactory, AuditLogViewModelFactory>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.Mapping.Document;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
|
||||
internal static class DocumentBuilderExtensions
|
||||
{
|
||||
internal static IUmbracoBuilder AddDocuments(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddTransient<IDocumentViewModelFactory, DocumentViewModelFactory>();
|
||||
builder.Services.AddTransient<IContentUrlFactory, ContentUrlFactory>();
|
||||
|
||||
builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>().Add<DocumentMapDefinition>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Umbraco.Cms.Api.Management.Mapping.DocumentType;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
|
||||
internal static class DocumentTypeBuilderExtensions
|
||||
{
|
||||
internal static IUmbracoBuilder AddDocumentTypes(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>().Add<DocumentTypeMapDefinition>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using Umbraco.Cms.Api.Management.Mapping.Package;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.Mapping.Package;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
|
||||
@@ -8,7 +10,9 @@ internal static class PackageBuilderExtensions
|
||||
{
|
||||
internal static IUmbracoBuilder AddPackages(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>().Add<PackageManifestViewModelMapDefinition>();
|
||||
builder.Services.AddTransient<IPackageDefinitionFactory, PackageDefinitionFactory>();
|
||||
|
||||
builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>().Add<PackageViewModelMapDefinition>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
|
||||
internal static class UserGroupsBuilderExtensions
|
||||
{
|
||||
internal static IUmbracoBuilder AddUserGroups(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddTransient<IUserGroupViewModelFactory, UserGroupViewModelFactory>();
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.AuditLogs;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Media;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public class AuditLogViewModelFactory : IAuditLogViewModelFactory
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly AppCaches _appCaches;
|
||||
private readonly MediaFileManager _mediaFileManager;
|
||||
private readonly IImageUrlGenerator _imageUrlGenerator;
|
||||
private readonly IEntityService _entityService;
|
||||
|
||||
public AuditLogViewModelFactory(IUserService userService, AppCaches appCaches, MediaFileManager mediaFileManager, IImageUrlGenerator imageUrlGenerator, IEntityService entityService)
|
||||
{
|
||||
_userService = userService;
|
||||
_appCaches = appCaches;
|
||||
_mediaFileManager = mediaFileManager;
|
||||
_imageUrlGenerator = imageUrlGenerator;
|
||||
_entityService = entityService;
|
||||
}
|
||||
|
||||
public IEnumerable<AuditLogResponseModel> CreateAuditLogViewModel(IEnumerable<IAuditItem> auditItems) => auditItems.Select(CreateAuditLogViewModel);
|
||||
|
||||
public IEnumerable<AuditLogWithUsernameResponseModel> CreateAuditLogWithUsernameViewModels(IEnumerable<IAuditItem> auditItems) => auditItems.Select(CreateAuditLogWithUsernameViewModel);
|
||||
|
||||
private AuditLogWithUsernameResponseModel CreateAuditLogWithUsernameViewModel(IAuditItem auditItem)
|
||||
{
|
||||
IEntitySlim? entitySlim = _entityService.Get(auditItem.Id);
|
||||
|
||||
var target = new AuditLogWithUsernameResponseModel
|
||||
{
|
||||
Comment = auditItem.Comment,
|
||||
EntityType = auditItem.EntityType,
|
||||
EntityKey = entitySlim?.Key,
|
||||
LogType = auditItem.AuditType,
|
||||
Parameters = auditItem.Parameters,
|
||||
Timestamp = auditItem.CreateDate,
|
||||
};
|
||||
|
||||
IUser? user = _userService.GetUserById(auditItem.UserId);
|
||||
if (user is null)
|
||||
{
|
||||
throw new ArgumentException($"Could not find user with id {auditItem.UserId}");
|
||||
}
|
||||
|
||||
target.UserKey = user.Key;
|
||||
target.UserAvatars = user.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator);
|
||||
target.UserName = user.Name;
|
||||
return target;
|
||||
}
|
||||
|
||||
private AuditLogResponseModel CreateAuditLogViewModel(IAuditItem auditItem)
|
||||
{
|
||||
IEntitySlim? entitySlim = _entityService.Get(auditItem.Id);
|
||||
var target = new AuditLogResponseModel
|
||||
{
|
||||
Comment = auditItem.Comment,
|
||||
EntityType = auditItem.EntityType,
|
||||
EntityKey = entitySlim?.Key,
|
||||
LogType = auditItem.AuditType,
|
||||
Parameters = auditItem.Parameters,
|
||||
Timestamp = auditItem.CreateDate,
|
||||
};
|
||||
|
||||
IUser? user = _userService.GetUserById(auditItem.UserId);
|
||||
if (user is null)
|
||||
{
|
||||
throw new ArgumentException($"Could not find user with id {auditItem.UserId}");
|
||||
}
|
||||
|
||||
target.UserKey = user.Key;
|
||||
return target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public class ContentUrlFactory : IContentUrlFactory
|
||||
{
|
||||
private readonly IPublishedRouter _publishedRouter;
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly ILanguageService _languageService;
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IVariationContextAccessor _variationContextAccessor;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly UriUtility _uriUtility;
|
||||
private readonly IPublishedUrlProvider _publishedUrlProvider;
|
||||
|
||||
public ContentUrlFactory(
|
||||
IPublishedRouter publishedRouter,
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
ILanguageService languageService,
|
||||
ILocalizedTextService localizedTextService,
|
||||
IContentService contentService,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
ILoggerFactory loggerFactory,
|
||||
UriUtility uriUtility,
|
||||
IPublishedUrlProvider publishedUrlProvider)
|
||||
{
|
||||
_publishedRouter = publishedRouter;
|
||||
_umbracoContextAccessor = umbracoContextAccessor;
|
||||
_languageService = languageService;
|
||||
_localizedTextService = localizedTextService;
|
||||
_contentService = contentService;
|
||||
_variationContextAccessor = variationContextAccessor;
|
||||
_loggerFactory = loggerFactory;
|
||||
_uriUtility = uriUtility;
|
||||
_publishedUrlProvider = publishedUrlProvider;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ContentUrlInfo>> GetUrlsAsync(IContent content)
|
||||
{
|
||||
IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();
|
||||
|
||||
UrlInfo[] urlInfos = (await content.GetContentUrlsAsync(
|
||||
_publishedRouter,
|
||||
umbracoContext,
|
||||
_languageService,
|
||||
_localizedTextService,
|
||||
_contentService,
|
||||
_variationContextAccessor,
|
||||
_loggerFactory.CreateLogger<IContent>(),
|
||||
_uriUtility,
|
||||
_publishedUrlProvider)).ToArray();
|
||||
|
||||
return urlInfos
|
||||
.Where(urlInfo => urlInfo.IsUrl)
|
||||
.Select(urlInfo => new ContentUrlInfo { Culture = urlInfo.Culture, Url = urlInfo.Text })
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public class DocumentViewModelFactory : IDocumentViewModelFactory
|
||||
{
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
private readonly IContentUrlFactory _contentUrlFactory;
|
||||
private readonly IFileService _fileService;
|
||||
|
||||
public DocumentViewModelFactory(
|
||||
IUmbracoMapper umbracoMapper,
|
||||
IContentUrlFactory contentUrlFactory,
|
||||
IFileService fileService)
|
||||
{
|
||||
_umbracoMapper = umbracoMapper;
|
||||
_contentUrlFactory = contentUrlFactory;
|
||||
_fileService = fileService;
|
||||
}
|
||||
|
||||
public async Task<DocumentViewModel> CreateViewModelAsync(IContent content)
|
||||
{
|
||||
DocumentViewModel viewModel = _umbracoMapper.Map<DocumentViewModel>(content)!;
|
||||
|
||||
viewModel.Urls = await _contentUrlFactory.GetUrlsAsync(content);
|
||||
|
||||
viewModel.TemplateKey = content.TemplateId.HasValue
|
||||
? _fileService.GetTemplate(content.TemplateId.Value)?.Key
|
||||
: null;
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.AuditLogs;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public interface IAuditLogViewModelFactory
|
||||
{
|
||||
IEnumerable<AuditLogResponseModel> CreateAuditLogViewModel(IEnumerable<IAuditItem> auditItems);
|
||||
|
||||
IEnumerable<AuditLogWithUsernameResponseModel> CreateAuditLogWithUsernameViewModels(IEnumerable<IAuditItem> auditItems);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public interface IContentUrlFactory
|
||||
{
|
||||
Task<IEnumerable<ContentUrlInfo>> GetUrlsAsync(IContent content);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public interface IDocumentViewModelFactory
|
||||
{
|
||||
Task<DocumentViewModel> CreateViewModelAsync(IContent content);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Package;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public interface IPackageDefinitionFactory
|
||||
{
|
||||
PackageDefinition CreatePackageDefinition(PackageCreateModel packageCreateModel);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.UserGroups;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
/// <summary>
|
||||
/// A factory for creating <see cref="UserGroupViewModel"/>
|
||||
/// </summary>
|
||||
public interface IUserGroupViewModelFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a <see cref="UserGroupViewModel"/> based on a <see cref="UserGroup"/>
|
||||
/// </summary>
|
||||
/// <param name="userGroup"></param>
|
||||
/// <returns></returns>
|
||||
Task<UserGroupViewModel> CreateAsync(IUserGroup userGroup);
|
||||
|
||||
/// <summary>
|
||||
/// Creates multiple <see cref="UserGroupViewModel"/> base on multiple <see cref="UserGroup"/>
|
||||
/// </summary>
|
||||
/// <param name="userGroups"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<UserGroupViewModel>> CreateMultipleAsync(IEnumerable<IUserGroup> userGroups);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IUserGroup"/> based on a <see cref="UserGroupSaveModel"/>
|
||||
/// </summary>
|
||||
/// <param name="saveModel"></param>
|
||||
/// <returns>An attempt indicating if the operation was a success as well as a more detailed <see cref="UserGroupOperationStatus"/>.</returns>
|
||||
Task<Attempt<IUserGroup, UserGroupOperationStatus>> CreateAsync(UserGroupSaveModel saveModel);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the values of an update model to fit with the existing backoffice implementations, and maps it to an existing user group.
|
||||
/// </summary>
|
||||
/// <param name="current">Existing user group to map to.</param>
|
||||
/// <param name="update">Update model containing the new values.</param>
|
||||
/// <returns>An attempt indicating if the operation was a success as well as a more detailed <see cref="UserGroupOperationStatus"/>.</returns>
|
||||
Task<Attempt<IUserGroup, UserGroupOperationStatus>> UpdateAsync(IUserGroup current, UserGroupUpdateModel update);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Package;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
internal class PackageDefinitionFactory : IPackageDefinitionFactory
|
||||
{
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public PackageDefinitionFactory(IUmbracoMapper umbracoMapper) => _umbracoMapper = umbracoMapper;
|
||||
|
||||
public PackageDefinition CreatePackageDefinition(PackageCreateModel packageCreateModel)
|
||||
{
|
||||
// Macros are not included!
|
||||
PackageDefinition packageDefinition = _umbracoMapper.Map<PackageDefinition>(packageCreateModel)!;
|
||||
|
||||
// Temp Id, PackageId and PackagePath for the newly created package
|
||||
packageDefinition.Id = 0;
|
||||
packageDefinition.PackageId = Guid.Empty;
|
||||
packageDefinition.PackagePath = string.Empty;
|
||||
|
||||
return packageDefinition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
using Umbraco.Cms.Api.Management.Mapping;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.UserGroups;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class UserGroupViewModelFactory : IUserGroupViewModelFactory
|
||||
{
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly IShortStringHelper _shortStringHelper;
|
||||
private readonly ILanguageService _languageService;
|
||||
|
||||
public UserGroupViewModelFactory(
|
||||
IEntityService entityService,
|
||||
IShortStringHelper shortStringHelper,
|
||||
ILanguageService languageService)
|
||||
{
|
||||
_entityService = entityService;
|
||||
_shortStringHelper = shortStringHelper;
|
||||
_languageService = languageService;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<UserGroupViewModel> CreateAsync(IUserGroup userGroup)
|
||||
{
|
||||
Guid? contentStartNodeKey = GetKeyFromId(userGroup.StartContentId, UmbracoObjectTypes.Document);
|
||||
Guid? mediaStartNodeKey = GetKeyFromId(userGroup.StartMediaId, UmbracoObjectTypes.Media);
|
||||
Attempt<IEnumerable<string>, UserGroupOperationStatus> languageIsoCodesMappingAttempt = await MapLanguageIdsToIsoCodeAsync(userGroup.AllowedLanguages);
|
||||
|
||||
// We've gotten this data from the database, so the mapping should not fail
|
||||
if (languageIsoCodesMappingAttempt.Success is false)
|
||||
{
|
||||
throw new InvalidOperationException($"Unknown language ID in User Group: {userGroup.Name}");
|
||||
}
|
||||
|
||||
return new UserGroupViewModel
|
||||
{
|
||||
Name = userGroup.Name ?? string.Empty,
|
||||
Key = userGroup.Key,
|
||||
DocumentStartNodeKey = contentStartNodeKey,
|
||||
MediaStartNodeKey = mediaStartNodeKey,
|
||||
Icon = userGroup.Icon,
|
||||
Languages = languageIsoCodesMappingAttempt.Result,
|
||||
HasAccessToAllLanguages = userGroup.HasAccessToAllLanguages,
|
||||
Permissions = userGroup.PermissionNames,
|
||||
Sections = userGroup.AllowedSections.Select(SectionMapper.GetName),
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IEnumerable<UserGroupViewModel>> CreateMultipleAsync(IEnumerable<IUserGroup> userGroups)
|
||||
{
|
||||
var userGroupViewModels = new List<UserGroupViewModel>();
|
||||
foreach (IUserGroup userGroup in userGroups)
|
||||
{
|
||||
userGroupViewModels.Add(await CreateAsync(userGroup));
|
||||
}
|
||||
|
||||
return userGroupViewModels;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<IUserGroup, UserGroupOperationStatus>> CreateAsync(UserGroupSaveModel saveModel)
|
||||
{
|
||||
var cleanedName = saveModel.Name.CleanForXss('[', ']', '(', ')', ':');
|
||||
|
||||
var group = new UserGroup(_shortStringHelper)
|
||||
{
|
||||
Name = cleanedName,
|
||||
Alias = cleanedName,
|
||||
Icon = saveModel.Icon,
|
||||
HasAccessToAllLanguages = saveModel.HasAccessToAllLanguages,
|
||||
PermissionNames = saveModel.Permissions,
|
||||
};
|
||||
|
||||
Attempt<UserGroupOperationStatus> assignmentAttempt = AssignStartNodesToUserGroup(saveModel, group);
|
||||
if (assignmentAttempt.Success is false)
|
||||
{
|
||||
return Attempt.FailWithStatus<IUserGroup, UserGroupOperationStatus>(assignmentAttempt.Result, group);
|
||||
}
|
||||
|
||||
foreach (var section in saveModel.Sections)
|
||||
{
|
||||
group.AddAllowedSection(SectionMapper.GetAlias(section));
|
||||
}
|
||||
|
||||
Attempt<IEnumerable<int>, UserGroupOperationStatus> languageIsoCodeMappingAttempt = await MapLanguageIsoCodesToIdsAsync(saveModel.Languages);
|
||||
if (languageIsoCodeMappingAttempt.Success is false)
|
||||
{
|
||||
return Attempt.FailWithStatus<IUserGroup, UserGroupOperationStatus>(languageIsoCodeMappingAttempt.Status, group);
|
||||
}
|
||||
|
||||
foreach (var languageId in languageIsoCodeMappingAttempt.Result)
|
||||
{
|
||||
group.AddAllowedLanguage(languageId);
|
||||
}
|
||||
|
||||
return Attempt.SucceedWithStatus<IUserGroup, UserGroupOperationStatus>(UserGroupOperationStatus.Success, group);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<IUserGroup, UserGroupOperationStatus>> UpdateAsync(IUserGroup current, UserGroupUpdateModel update)
|
||||
{
|
||||
Attempt<UserGroupOperationStatus> assignmentAttempt = AssignStartNodesToUserGroup(update, current);
|
||||
if (assignmentAttempt.Success is false)
|
||||
{
|
||||
return Attempt.FailWithStatus(assignmentAttempt.Result, current);
|
||||
}
|
||||
|
||||
current.ClearAllowedLanguages();
|
||||
Attempt<IEnumerable<int>, UserGroupOperationStatus> languageIdsMappingAttempt = await MapLanguageIsoCodesToIdsAsync(update.Languages);
|
||||
if (languageIdsMappingAttempt.Success is false)
|
||||
{
|
||||
return Attempt.FailWithStatus(languageIdsMappingAttempt.Status, current);
|
||||
}
|
||||
|
||||
foreach (var languageId in languageIdsMappingAttempt.Result)
|
||||
{
|
||||
current.AddAllowedLanguage(languageId);
|
||||
}
|
||||
|
||||
current.ClearAllowedSections();
|
||||
foreach (var sectionName in update.Sections)
|
||||
{
|
||||
current.AddAllowedSection(SectionMapper.GetAlias(sectionName));
|
||||
}
|
||||
|
||||
current.Name = update.Name.CleanForXss('[', ']', '(', ')', ':');
|
||||
current.Icon = update.Icon;
|
||||
current.HasAccessToAllLanguages = update.HasAccessToAllLanguages;
|
||||
current.PermissionNames = update.Permissions;
|
||||
|
||||
|
||||
return Attempt.SucceedWithStatus(UserGroupOperationStatus.Success, current);
|
||||
}
|
||||
|
||||
private async Task<Attempt<IEnumerable<string>, UserGroupOperationStatus>> MapLanguageIdsToIsoCodeAsync(IEnumerable<int> ids)
|
||||
{
|
||||
IEnumerable<ILanguage> languages = await _languageService.GetAllAsync();
|
||||
string[] isoCodes = languages
|
||||
.Where(x => ids.Contains(x.Id))
|
||||
.Select(x => x.IsoCode)
|
||||
.ToArray();
|
||||
|
||||
return isoCodes.Length == ids.Count()
|
||||
? Attempt.SucceedWithStatus<IEnumerable<string>, UserGroupOperationStatus>(UserGroupOperationStatus.Success, isoCodes)
|
||||
: Attempt.FailWithStatus<IEnumerable<string>, UserGroupOperationStatus>(UserGroupOperationStatus.LanguageNotFound, isoCodes);
|
||||
}
|
||||
|
||||
private async Task<Attempt<IEnumerable<int>, UserGroupOperationStatus>> MapLanguageIsoCodesToIdsAsync(IEnumerable<string> isoCodes)
|
||||
{
|
||||
IEnumerable<ILanguage> languages = await _languageService.GetAllAsync();
|
||||
int[] languageIds = languages
|
||||
.Where(x => isoCodes.Contains(x.IsoCode))
|
||||
.Select(x => x.Id)
|
||||
.ToArray();
|
||||
|
||||
return languageIds.Length == isoCodes.Count()
|
||||
? Attempt.SucceedWithStatus<IEnumerable<int>, UserGroupOperationStatus>(UserGroupOperationStatus.Success, languageIds)
|
||||
: Attempt.FailWithStatus<IEnumerable<int>, UserGroupOperationStatus>(UserGroupOperationStatus.LanguageNotFound, languageIds);
|
||||
}
|
||||
|
||||
private Attempt<UserGroupOperationStatus> AssignStartNodesToUserGroup(UserGroupBase source, IUserGroup target)
|
||||
{
|
||||
if (source.DocumentStartNodeKey is not null)
|
||||
{
|
||||
var contentId = GetIdFromKey(source.DocumentStartNodeKey.Value, UmbracoObjectTypes.Document);
|
||||
|
||||
if (contentId is null)
|
||||
{
|
||||
return Attempt.Fail(UserGroupOperationStatus.DocumentStartNodeKeyNotFound);
|
||||
}
|
||||
|
||||
target.StartContentId = contentId;
|
||||
}
|
||||
|
||||
if (source.MediaStartNodeKey is not null)
|
||||
{
|
||||
var mediaId = GetIdFromKey(source.MediaStartNodeKey.Value, UmbracoObjectTypes.Media);
|
||||
|
||||
if (mediaId is null)
|
||||
{
|
||||
return Attempt.Fail(UserGroupOperationStatus.MediaStartNodeKeyNotFound);
|
||||
}
|
||||
|
||||
target.StartMediaId = mediaId;
|
||||
}
|
||||
|
||||
return Attempt.Succeed(UserGroupOperationStatus.Success);
|
||||
}
|
||||
|
||||
private Guid? GetKeyFromId(int? id, UmbracoObjectTypes objectType)
|
||||
{
|
||||
if (id is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Attempt<Guid> attempt = _entityService.GetKey(id.Value, objectType);
|
||||
if (attempt.Success is false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return attempt.Result;
|
||||
}
|
||||
|
||||
private int? GetIdFromKey(Guid key, UmbracoObjectTypes objectType)
|
||||
{
|
||||
Attempt<int> attempt = _entityService.GetId(key, objectType);
|
||||
|
||||
if (attempt.Success is false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return attempt.Result;
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,9 @@ public class ManagementApiComposer : IComposer
|
||||
.AddUpgrader()
|
||||
.AddSearchManagement()
|
||||
.AddTrees()
|
||||
.AddAuditLogs()
|
||||
.AddDocuments()
|
||||
.AddDocumentTypes()
|
||||
.AddLanguages()
|
||||
.AddDictionary()
|
||||
.AddFileUpload()
|
||||
@@ -35,6 +38,7 @@ public class ManagementApiComposer : IComposer
|
||||
.AddDataTypes()
|
||||
.AddTemplates()
|
||||
.AddLogViewer()
|
||||
.AddUserGroups()
|
||||
.AddPackages()
|
||||
.AddBackOfficeAuthentication()
|
||||
.AddApiVersioning()
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Mapping.Content;
|
||||
|
||||
public abstract class ContentMapDefinition<TContent, TValueViewModel, TVariantViewModel>
|
||||
where TContent : IContentBase
|
||||
where TValueViewModel : ValueViewModelBase, new()
|
||||
where TVariantViewModel : VariantViewModelBase, new()
|
||||
{
|
||||
private readonly PropertyEditorCollection _propertyEditorCollection;
|
||||
|
||||
protected ContentMapDefinition(PropertyEditorCollection propertyEditorCollection) => _propertyEditorCollection = propertyEditorCollection;
|
||||
|
||||
protected delegate void ValueViewModelMapping(IDataEditor propertyEditor, TValueViewModel variantViewModel);
|
||||
|
||||
protected delegate void VariantViewModelMapping(string? culture, string? segment, TVariantViewModel variantViewModel);
|
||||
|
||||
protected IEnumerable<TValueViewModel> MapValueViewModels(TContent source, ValueViewModelMapping? additionalPropertyMapping = null) =>
|
||||
source
|
||||
.Properties
|
||||
.SelectMany(property => property
|
||||
.Values
|
||||
.Select(propertyValue =>
|
||||
{
|
||||
IDataEditor? propertyEditor = _propertyEditorCollection[property.PropertyType.PropertyEditorAlias];
|
||||
if (propertyEditor == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var variantViewModel = new TValueViewModel
|
||||
{
|
||||
Culture = propertyValue.Culture,
|
||||
Segment = propertyValue.Segment,
|
||||
Alias = property.Alias,
|
||||
Value = propertyEditor.GetValueEditor().ToEditor(property, propertyValue.Culture, propertyValue.Segment)
|
||||
};
|
||||
additionalPropertyMapping?.Invoke(propertyEditor, variantViewModel);
|
||||
return variantViewModel;
|
||||
}))
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
|
||||
protected IEnumerable<TVariantViewModel> MapVariantViewModels(TContent source, VariantViewModelMapping? additionalVariantMapping = null)
|
||||
{
|
||||
IPropertyValue[] propertyValues = source.Properties.SelectMany(propertyCollection => propertyCollection.Values).ToArray();
|
||||
var cultures = source.AvailableCultures.DefaultIfEmpty(null).ToArray();
|
||||
var segments = propertyValues.Select(property => property.Segment).Distinct().DefaultIfEmpty(null).ToArray();
|
||||
|
||||
return cultures
|
||||
.SelectMany(culture => segments.Select(segment =>
|
||||
{
|
||||
var variantViewModel = new TVariantViewModel
|
||||
{
|
||||
Culture = culture,
|
||||
Segment = segment,
|
||||
Name = source.GetCultureName(culture) ?? string.Empty,
|
||||
CreateDate = source.CreateDate, // apparently there is no culture specific creation date
|
||||
UpdateDate = culture == null
|
||||
? source.UpdateDate
|
||||
: source.GetUpdateDate(culture) ?? source.UpdateDate,
|
||||
};
|
||||
additionalVariantMapping?.Invoke(culture, segment, variantViewModel);
|
||||
return variantViewModel;
|
||||
}))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Mapping.ContentType;
|
||||
|
||||
public abstract class ContentTypeMapDefinition<TContentType, TPropertyTypeViewModel, TPropertyTypeContainerViewModel>
|
||||
where TContentType : IContentTypeBase
|
||||
where TPropertyTypeViewModel : PropertyTypeViewModelBase, new()
|
||||
where TPropertyTypeContainerViewModel : PropertyTypeContainerViewModelBase, new()
|
||||
{
|
||||
protected IEnumerable<TPropertyTypeViewModel> MapPropertyTypes(TContentType source)
|
||||
{
|
||||
// create a mapping table between properties and their associated groups
|
||||
var groupKeysByPropertyKeys = source
|
||||
.PropertyGroups
|
||||
.SelectMany(propertyGroup => (propertyGroup.PropertyTypes?.ToArray() ?? Array.Empty<PropertyType>())
|
||||
.Select(propertyType => new { GroupKey = propertyGroup.Key, PropertyTypeKey = propertyType.Key }))
|
||||
.ToDictionary(map => map.PropertyTypeKey, map => map.GroupKey);
|
||||
|
||||
return source.PropertyTypes.Select(propertyType =>
|
||||
new TPropertyTypeViewModel
|
||||
{
|
||||
Key = propertyType.Key,
|
||||
ContainerKey = groupKeysByPropertyKeys.ContainsKey(propertyType.Key)
|
||||
? groupKeysByPropertyKeys[propertyType.Key]
|
||||
: null,
|
||||
Name = propertyType.Name,
|
||||
Alias = propertyType.Alias,
|
||||
Description = propertyType.Description,
|
||||
DataTypeKey = propertyType.DataTypeKey,
|
||||
VariesByCulture = propertyType.VariesByCulture(),
|
||||
VariesBySegment = propertyType.VariesBySegment(),
|
||||
Validation = new PropertyTypeValidation
|
||||
{
|
||||
Mandatory = propertyType.Mandatory,
|
||||
MandatoryMessage = propertyType.MandatoryMessage,
|
||||
RegEx = propertyType.ValidationRegExp,
|
||||
RegExMessage = propertyType.ValidationRegExpMessage
|
||||
},
|
||||
Appearance = new PropertyTypeAppearance
|
||||
{
|
||||
LabelOnTop = propertyType.LabelOnTop
|
||||
}
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
protected IEnumerable<TPropertyTypeContainerViewModel> MapPropertyTypeContainers(TContentType source)
|
||||
{
|
||||
// create a mapping table between property group aliases and keys
|
||||
var groupKeysByGroupAliases = source
|
||||
.PropertyGroups
|
||||
.ToDictionary(propertyGroup => propertyGroup.Alias, propertyGroup => propertyGroup.Key);
|
||||
|
||||
Guid? ParentGroupKey(PropertyGroup group)
|
||||
{
|
||||
var path = group.Alias.Split(Constants.CharArrays.ForwardSlash);
|
||||
return path.Length == 1 || groupKeysByGroupAliases.TryGetValue(path.First(), out Guid parentGroupKey) == false
|
||||
? null
|
||||
: parentGroupKey;
|
||||
}
|
||||
|
||||
return source
|
||||
.PropertyGroups
|
||||
.Select(propertyGroup =>
|
||||
new TPropertyTypeContainerViewModel
|
||||
{
|
||||
Key = propertyGroup.Key,
|
||||
ParentKey = ParentGroupKey(propertyGroup),
|
||||
Type = propertyGroup.Type.ToString(),
|
||||
SortOrder = propertyGroup.SortOrder,
|
||||
Name = propertyGroup.Name,
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using Umbraco.Cms.Api.Management.Mapping.Content;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Mapping.Document;
|
||||
|
||||
public class DocumentMapDefinition : ContentMapDefinition<IContent, DocumentValueViewModel, DocumentVariantViewModel>, IMapDefinition
|
||||
{
|
||||
public DocumentMapDefinition(PropertyEditorCollection propertyEditorCollection)
|
||||
: base(propertyEditorCollection)
|
||||
{
|
||||
}
|
||||
|
||||
public void DefineMaps(IUmbracoMapper mapper)
|
||||
=> mapper.Define<IContent, DocumentViewModel>((_, _) => new DocumentViewModel(), Map);
|
||||
|
||||
// Umbraco.Code.MapAll -Urls -TemplateKey
|
||||
private void Map(IContent source, DocumentViewModel target, MapperContext context)
|
||||
{
|
||||
target.Key = source.Key;
|
||||
target.ContentTypeKey = source.ContentType.Key;
|
||||
target.Values = MapValueViewModels(source);
|
||||
target.Variants = MapVariantViewModels(
|
||||
source,
|
||||
(culture, _, documentVariantViewModel) =>
|
||||
{
|
||||
documentVariantViewModel.State = GetSavedState(source, culture);
|
||||
documentVariantViewModel.PublishDate = culture == null
|
||||
? source.PublishDate
|
||||
: source.GetPublishDate(culture);
|
||||
});
|
||||
}
|
||||
|
||||
private ContentState GetSavedState(IContent content, string? culture)
|
||||
{
|
||||
if (content.Id <= 0 || (culture != null && content.IsCultureAvailable(culture) == false))
|
||||
{
|
||||
return ContentState.NotCreated;
|
||||
}
|
||||
|
||||
var isDraft = content.PublishedState == PublishedState.Unpublished ||
|
||||
(culture != null && content.IsCulturePublished(culture) == false);
|
||||
if (isDraft)
|
||||
{
|
||||
return ContentState.Draft;
|
||||
}
|
||||
|
||||
var isEdited = culture != null
|
||||
? content.IsCultureEdited(culture)
|
||||
: content.Edited;
|
||||
|
||||
return isEdited ? ContentState.PublishedPendingChanges : ContentState.Published;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using Umbraco.Cms.Api.Management.Mapping.ContentType;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DocumentType;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Extensions;
|
||||
using ContentTypeSort = Umbraco.Cms.Api.Management.ViewModels.ContentType.ContentTypeSort;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Mapping.DocumentType;
|
||||
|
||||
public class DocumentTypeMapDefinition : ContentTypeMapDefinition<IContentType, DocumentTypePropertyTypeViewModel, DocumentTypePropertyTypeContainerViewModel>, IMapDefinition
|
||||
{
|
||||
public void DefineMaps(IUmbracoMapper mapper)
|
||||
=> mapper.Define<IContentType, DocumentTypeViewModel>((_, _) => new DocumentTypeViewModel(), Map);
|
||||
|
||||
// Umbraco.Code.MapAll
|
||||
private void Map(IContentType source, DocumentTypeViewModel target, MapperContext context)
|
||||
{
|
||||
target.Key = source.Key;
|
||||
target.Alias = source.Alias;
|
||||
target.Name = source.Name ?? string.Empty;
|
||||
target.Description = source.Description;
|
||||
target.Icon = source.Icon ?? string.Empty;
|
||||
target.AllowedAsRoot = source.AllowedAsRoot;
|
||||
target.VariesByCulture = source.VariesByCulture();
|
||||
target.VariesBySegment = source.VariesBySegment();
|
||||
target.IsElement = source.IsElement;
|
||||
target.Containers = MapPropertyTypeContainers(source);
|
||||
target.Properties = MapPropertyTypes(source);
|
||||
|
||||
if (source.AllowedContentTypes != null)
|
||||
{
|
||||
target.AllowedContentTypes = source.AllowedContentTypes.Select(contentTypeSort
|
||||
=> new ContentTypeSort { Key = contentTypeSort.Key, SortOrder = contentTypeSort.SortOrder });
|
||||
}
|
||||
|
||||
if (source.AllowedTemplates != null)
|
||||
{
|
||||
target.AllowedTemplateKeys = source.AllowedTemplates.Select(template => template.Key);
|
||||
}
|
||||
|
||||
target.DefaultTemplateKey = source.DefaultTemplate?.Key;
|
||||
|
||||
if (source.HistoryCleanup != null)
|
||||
{
|
||||
target.Cleanup = new ContentTypeCleanup
|
||||
{
|
||||
PreventCleanup = source.HistoryCleanup.PreventCleanup,
|
||||
KeepAllVersionsNewerThanDays = source.HistoryCleanup.KeepAllVersionsNewerThanDays,
|
||||
KeepLatestVersionPerDayForDays = source.HistoryCleanup.KeepLatestVersionPerDayForDays
|
||||
};
|
||||
}
|
||||
|
||||
target.Compositions = source.ContentTypeComposition.Select(contentType => new ContentTypeComposition
|
||||
{
|
||||
Key = contentType.Key,
|
||||
CompositionType = contentType.Id == source.ParentId
|
||||
? ContentTypeCompositionType.Inheritance
|
||||
: ContentTypeCompositionType.Composition
|
||||
}).ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Package;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Mapping.Package;
|
||||
|
||||
public class PackageViewModelMapDefinition : IMapDefinition
|
||||
{
|
||||
public void DefineMaps(IUmbracoMapper mapper)
|
||||
{
|
||||
mapper.Define<PackageModelBase, PackageDefinition>((_, _) => new PackageDefinition(), Map);
|
||||
mapper.Define<PackageDefinition, PackageDefinitionViewModel>(
|
||||
(_, _) => new PackageDefinitionViewModel
|
||||
{
|
||||
Name = string.Empty,
|
||||
PackagePath = string.Empty
|
||||
},
|
||||
Map);
|
||||
mapper.Define<InstalledPackage, PackageMigrationStatusViewModel>((_, _) => new PackageMigrationStatusViewModel { PackageName = string.Empty }, Map);
|
||||
mapper.Define<IEnumerable<PackageDefinition>, PagedViewModel<PackageDefinitionViewModel>>((_, _) => new PagedViewModel<PackageDefinitionViewModel>(), Map);
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -Id -PackageId -PackagePath -Macros
|
||||
private static void Map(PackageModelBase source, PackageDefinition target, MapperContext context)
|
||||
{
|
||||
target.Name = source.Name;
|
||||
target.ContentLoadChildNodes = source.ContentLoadChildNodes;
|
||||
target.ContentNodeId = source.ContentNodeId;
|
||||
target.Languages = source.Languages;
|
||||
target.DictionaryItems = source.DictionaryItems;
|
||||
target.Templates = source.Templates;
|
||||
target.PartialViews = source.PartialViews;
|
||||
target.DocumentTypes = source.DocumentTypes;
|
||||
target.MediaTypes = source.MediaTypes;
|
||||
target.Stylesheets = source.Stylesheets;
|
||||
target.Scripts = source.Scripts;
|
||||
target.DataTypes = source.DataTypes;
|
||||
target.MediaUdis = source.MediaKeys.Select(x => new GuidUdi(Constants.UdiEntityType.Media, x)).ToList();
|
||||
target.MediaLoadChildNodes = source.MediaLoadChildNodes;
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll
|
||||
private static void Map(PackageDefinition source, PackageDefinitionViewModel target, MapperContext context)
|
||||
{
|
||||
target.Key = source.PackageId;
|
||||
target.Name = source.Name;
|
||||
target.PackagePath = source.PackagePath;
|
||||
target.ContentNodeId = source.ContentNodeId;
|
||||
target.ContentLoadChildNodes = source.ContentLoadChildNodes;
|
||||
target.MediaKeys = source.MediaUdis.Select(x => x.Guid).ToList();
|
||||
target.MediaLoadChildNodes = source.MediaLoadChildNodes;
|
||||
target.DocumentTypes = source.DocumentTypes;
|
||||
target.MediaTypes = source.MediaTypes;
|
||||
target.DataTypes = source.DataTypes;
|
||||
target.Templates = source.Templates;
|
||||
target.PartialViews = source.PartialViews;
|
||||
target.Stylesheets = source.Stylesheets;
|
||||
target.Scripts = source.Scripts;
|
||||
target.Languages = source.Languages;
|
||||
target.DictionaryItems = source.DictionaryItems;
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll
|
||||
private static void Map(InstalledPackage source, PackageMigrationStatusViewModel target, MapperContext context)
|
||||
{
|
||||
if (source.PackageName is not null)
|
||||
{
|
||||
target.PackageName = source.PackageName;
|
||||
}
|
||||
|
||||
target.HasPendingMigrations = source.HasPendingMigrations;
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll
|
||||
private static void Map(IEnumerable<PackageDefinition> source, PagedViewModel<PackageDefinitionViewModel> target, MapperContext context)
|
||||
{
|
||||
PackageDefinition[] definitions = source.ToArray();
|
||||
target.Items = context.MapEnumerable<PackageDefinition, PackageDefinitionViewModel>(definitions);
|
||||
target.Total = definitions.Length;
|
||||
}
|
||||
}
|
||||
54
src/Umbraco.Cms.Api.Management/Mapping/SectionMapper.cs
Normal file
54
src/Umbraco.Cms.Api.Management/Mapping/SectionMapper.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
namespace Umbraco.Cms.Api.Management.Mapping;
|
||||
|
||||
/// <summary>
|
||||
/// Maps from the old section aliases to the new section names.
|
||||
/// This is static since it's expected to be removed, so might as well make the clean up work as easy as possible.
|
||||
/// FIXME: This is a temporary thing until permissions is fleshed out and section is either migrated to some form of permission
|
||||
/// </summary>
|
||||
public static class SectionMapper
|
||||
{
|
||||
private static readonly List<SectionMapping> _sectionMappings = new()
|
||||
{
|
||||
new SectionMapping { Alias = "content", Name = "Umb.Section.Content" },
|
||||
new SectionMapping { Alias = "media", Name = "Umb.Section.Media" },
|
||||
new SectionMapping { Alias = "member", Name = "Umb.Section.Members" },
|
||||
new SectionMapping { Alias = "settings", Name = "Umb.Section.Settings" },
|
||||
new SectionMapping { Alias = "packages", Name = "Umb.Section.Packages" },
|
||||
new SectionMapping { Alias = "translation", Name = "Umb.Section.Translation" },
|
||||
new SectionMapping { Alias = "users", Name = "Umb.Section.Users" },
|
||||
new SectionMapping { Alias = "forms", Name = "Umb.Section.Forms" },
|
||||
};
|
||||
|
||||
public static string GetName(string alias)
|
||||
{
|
||||
SectionMapping? mapping = _sectionMappings.FirstOrDefault(x => x.Alias == alias);
|
||||
|
||||
if (mapping is not null)
|
||||
{
|
||||
return mapping.Name;
|
||||
}
|
||||
|
||||
// If we can't find it we just fall back to the alias
|
||||
return alias;
|
||||
}
|
||||
|
||||
public static string GetAlias(string name)
|
||||
{
|
||||
SectionMapping? mapping = _sectionMappings.FirstOrDefault(x => x.Name == name);
|
||||
|
||||
if (mapping is not null)
|
||||
{
|
||||
return mapping.Alias;
|
||||
}
|
||||
|
||||
// If we can't find it we just fall back to the name
|
||||
return name;
|
||||
}
|
||||
|
||||
private class SectionMapping
|
||||
{
|
||||
public required string Alias { get; init; }
|
||||
|
||||
public required string Name { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ public class TrackedReferenceViewModelsMapDefinition : IMapDefinition
|
||||
target.RelationTypeIsBidirectional = source.RelationTypeIsBidirectional;
|
||||
target.RelationTypeIsDependency = source.RelationTypeIsDependency;
|
||||
target.RelationTypeName = source.RelationTypeName;
|
||||
target.NodePublished = source.NodePublished;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.AuditLogs;
|
||||
|
||||
public class AuditLogBaseModel
|
||||
{
|
||||
public Guid UserKey { get; set; }
|
||||
|
||||
public Guid? EntityKey { get; set; }
|
||||
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
public AuditType LogType { get; set; }
|
||||
|
||||
public string? EntityType { get; set; }
|
||||
|
||||
public string? Comment { get; set; }
|
||||
|
||||
public string? Parameters { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.AuditLogs;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class AuditLogResponseModel : AuditLogBaseModel
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.AuditLogs;
|
||||
|
||||
public class AuditLogWithUsernameResponseModel : AuditLogBaseModel
|
||||
{
|
||||
public string? UserName { get; set; }
|
||||
|
||||
public string[]? UserAvatars { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
/// <summary>
|
||||
/// The saved state of a content item
|
||||
/// </summary>
|
||||
public enum ContentState
|
||||
{
|
||||
/// <summary>
|
||||
/// The item isn't created yet
|
||||
/// </summary>
|
||||
NotCreated = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The item is saved but isn't published
|
||||
/// </summary>
|
||||
Draft = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The item is published and there are no pending changes
|
||||
/// </summary>
|
||||
Published = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The item is published and there are pending changes
|
||||
/// </summary>
|
||||
PublishedPendingChanges = 4,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
public class ContentUrlInfo
|
||||
{
|
||||
public required string? Culture { get; init; }
|
||||
|
||||
public required string Url { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
public abstract class ContentViewModelBase<TValueViewModelBase, TVariantViewModel>
|
||||
where TValueViewModelBase : ValueViewModelBase
|
||||
where TVariantViewModel : VariantViewModelBase
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
|
||||
public Guid ContentTypeKey { get; set; }
|
||||
|
||||
public IEnumerable<TValueViewModelBase> Values { get; set; } = Array.Empty<TValueViewModelBase>();
|
||||
|
||||
public IEnumerable<TVariantViewModel> Variants { get; set; } = Array.Empty<TVariantViewModel>();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
public abstract class ValueViewModelBase
|
||||
{
|
||||
public string? Culture { get; set; }
|
||||
|
||||
public string? Segment { get; set; }
|
||||
|
||||
public string Alias { get; set; } = string.Empty;
|
||||
|
||||
public object? Value { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
public abstract class VariantViewModelBase
|
||||
{
|
||||
public string? Culture { get; set; }
|
||||
|
||||
public string? Segment { get; set; }
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public DateTime CreateDate { get; set; }
|
||||
|
||||
public DateTime UpdateDate { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public class ContentTypeCleanup
|
||||
{
|
||||
public bool PreventCleanup { get; init; }
|
||||
|
||||
public int? KeepAllVersionsNewerThanDays { get; init; }
|
||||
|
||||
public int? KeepLatestVersionPerDayForDays { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public class ContentTypeComposition
|
||||
{
|
||||
public required Guid Key { get; init; }
|
||||
|
||||
public required ContentTypeCompositionType CompositionType { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public enum ContentTypeCompositionType
|
||||
{
|
||||
Composition,
|
||||
Inheritance
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public class ContentTypeSort
|
||||
{
|
||||
public required Guid Key { get; init; }
|
||||
|
||||
public required int SortOrder { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public abstract class ContentTypeViewModelBase<TPropertyType, TPropertyTypeContainer>
|
||||
where TPropertyType : PropertyTypeViewModelBase
|
||||
where TPropertyTypeContainer : PropertyTypeContainerViewModelBase
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
|
||||
public string Alias { get; set; } = string.Empty;
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string Icon { get; set; } = string.Empty;
|
||||
|
||||
public bool AllowedAsRoot { get; set; }
|
||||
|
||||
public bool VariesByCulture { get; set; }
|
||||
|
||||
public bool VariesBySegment { get; set; }
|
||||
|
||||
public bool IsElement { get; set; }
|
||||
|
||||
public IEnumerable<TPropertyType> Properties { get; set; } = Array.Empty<TPropertyType>();
|
||||
|
||||
public IEnumerable<TPropertyTypeContainer> Containers { get; set; } = Array.Empty<TPropertyTypeContainer>();
|
||||
|
||||
public IEnumerable<ContentTypeSort> AllowedContentTypes { get; set; } = Array.Empty<ContentTypeSort>();
|
||||
|
||||
public IEnumerable<ContentTypeComposition> Compositions { get; set; } = Array.Empty<ContentTypeComposition>();
|
||||
|
||||
public ContentTypeCleanup Cleanup { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public class PropertyTypeAppearance
|
||||
{
|
||||
public bool LabelOnTop { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public abstract class PropertyTypeContainerViewModelBase
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
|
||||
public Guid? ParentKey { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
// NOTE: This needs to be a string because it can be anything in the future (= not necessarily limited to "tab" or "group")
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public class PropertyTypeValidation
|
||||
{
|
||||
public bool Mandatory { get; set; }
|
||||
|
||||
public string? MandatoryMessage { get; set; }
|
||||
|
||||
public string? RegEx { get; set; }
|
||||
|
||||
public string? RegExMessage { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public abstract class PropertyTypeViewModelBase
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
|
||||
public Guid? ContainerKey { get; set; }
|
||||
|
||||
public string Alias { get; set; } = string.Empty;
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public Guid DataTypeKey { get; set; }
|
||||
|
||||
public bool VariesByCulture { get; set; }
|
||||
|
||||
public bool VariesBySegment { get; set; }
|
||||
|
||||
public PropertyTypeValidation Validation { get; set; } = new();
|
||||
|
||||
public PropertyTypeAppearance Appearance { get; set; } = new();
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.DataType;
|
||||
|
||||
public class DataTypeViewModel : DataTypeModelBase
|
||||
public class DataTypeViewModel : DataTypeModelBase, INamedEntityViewModel
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Dictionary;
|
||||
|
||||
public class DictionaryItemViewModel : DictionaryItemModelBase
|
||||
public class DictionaryItemViewModel : DictionaryItemModelBase, INamedEntityViewModel
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
|
||||
public class DocumentValueViewModel : ValueViewModelBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
|
||||
public class DocumentVariantViewModel : VariantViewModelBase
|
||||
{
|
||||
public ContentState State { get; set; }
|
||||
|
||||
public DateTime? PublishDate { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
|
||||
public class DocumentViewModel : ContentViewModelBase<DocumentValueViewModel, DocumentVariantViewModel>
|
||||
{
|
||||
public IEnumerable<ContentUrlInfo> Urls { get; set; } = Array.Empty<ContentUrlInfo>();
|
||||
|
||||
public Guid? TemplateKey { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.DocumentType;
|
||||
|
||||
public class DocumentTypePropertyTypeContainerViewModel : PropertyTypeContainerViewModelBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.DocumentType;
|
||||
|
||||
public class DocumentTypePropertyTypeViewModel : PropertyTypeViewModelBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.DocumentType;
|
||||
|
||||
public class DocumentTypeViewModel : ContentTypeViewModelBase<DocumentTypePropertyTypeViewModel, DocumentTypePropertyTypeContainerViewModel>
|
||||
{
|
||||
public IEnumerable<Guid> AllowedTemplateKeys { get; set; } = Array.Empty<Guid>();
|
||||
|
||||
public Guid? DefaultTemplateKey { get; set; }
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Folder;
|
||||
|
||||
public class FolderViewModel : FolderModelBase
|
||||
public class FolderViewModel : FolderModelBase, INamedEntityViewModel
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels;
|
||||
|
||||
public interface INamedEntityViewModel
|
||||
{
|
||||
Guid Key { get; }
|
||||
|
||||
string Name { get;}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Package;
|
||||
|
||||
public class PackageCreateModel : PackageModelBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Package;
|
||||
|
||||
public class PackageDefinitionViewModel : PackageModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the key.
|
||||
/// </summary>
|
||||
public Guid Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the full path to the package's XML file.
|
||||
/// </summary>
|
||||
[ReadOnly(true)]
|
||||
public required string PackagePath { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Package;
|
||||
|
||||
public class PackageMigrationStatusViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the package.
|
||||
/// </summary>
|
||||
public required string PackageName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the package has any pending migrations to run.
|
||||
/// </summary>
|
||||
public bool HasPendingMigrations { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Package;
|
||||
|
||||
public class PackageModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id of the selected content node.
|
||||
/// </summary>
|
||||
public string? ContentNodeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to load all child nodes of the selected content node.
|
||||
/// </summary>
|
||||
public bool ContentLoadChildNodes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of media keys for the selected media items.
|
||||
/// </summary>
|
||||
public IList<Guid> MediaKeys { get; set; } = new List<Guid>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to load all child nodes of the selected media items.
|
||||
/// </summary>
|
||||
public bool MediaLoadChildNodes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of ids for the selected document types.
|
||||
/// </summary>
|
||||
public IList<string> DocumentTypes { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of ids for the selected media types.
|
||||
/// </summary>
|
||||
public IList<string> MediaTypes { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of ids for the selected data types.
|
||||
/// </summary>
|
||||
public IList<string> DataTypes { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of ids for the selected templates.
|
||||
/// </summary>
|
||||
public IList<string> Templates { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of relative paths for the selected partial views.
|
||||
/// </summary>
|
||||
public IList<string> PartialViews { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of names for the selected stylesheets.
|
||||
/// </summary>
|
||||
public IList<string> Stylesheets { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of names for the selected scripts.
|
||||
/// </summary>
|
||||
public IList<string> Scripts { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of ids for the selected languages.
|
||||
/// </summary>
|
||||
public IList<string> Languages { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of ids for the selected dictionary items.
|
||||
/// </summary>
|
||||
public IList<string> DictionaryItems { get; set; } = new List<string>();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Package;
|
||||
|
||||
public class PackageUpdateModel : PackageModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the full path to the package's XML file.
|
||||
/// </summary>
|
||||
[ReadOnly(true)]
|
||||
public required string PackagePath { get; set; }
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.RecycleBin;
|
||||
|
||||
public class RecycleBinItemViewModel
|
||||
public class RecycleBinItemViewModel : INamedEntityViewModel
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Template;
|
||||
|
||||
public class TemplateViewModel : TemplateModelBase
|
||||
public class TemplateViewModel : TemplateModelBase, INamedEntityViewModel
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ public class RelationItemViewModel
|
||||
|
||||
public string? NodeType { get; set; }
|
||||
|
||||
public bool? NodePublished { get; set; }
|
||||
|
||||
public string? ContentTypeIcon { get; set; }
|
||||
|
||||
public string? ContentTypeAlias { get; set; }
|
||||
@@ -19,4 +21,5 @@ public class RelationItemViewModel
|
||||
public bool RelationTypeIsBidirectional { get; set; }
|
||||
|
||||
public bool RelationTypeIsDependency { get; set; }
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
|
||||
public class EntityTreeItemViewModel : TreeItemViewModel
|
||||
public class EntityTreeItemViewModel : TreeItemViewModel, INamedEntityViewModel
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.UserGroups;
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Base class for front-end representation of a User Group.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Contains all the properties shared between Save, Update, Representation, etc...
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class UserGroupBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the user groups
|
||||
/// </summary>
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The Icon for the user group
|
||||
/// </summary>
|
||||
public string? Icon { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The sections that the user group has access to
|
||||
/// </summary>
|
||||
public required IEnumerable<string> Sections { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The languages that the user group has access to
|
||||
/// </summary>
|
||||
public required IEnumerable<string> Languages { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag indicating if the user group gives access to all languages, regardless of <see cref="UserGroupBase.Languages"/>.
|
||||
/// </summary>
|
||||
public required bool HasAccessToAllLanguages { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the document that should act as root node for the user group
|
||||
/// <remarks>
|
||||
/// This can be overwritten by a different user group if a user is a member of multiple groups
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
public Guid? DocumentStartNodeKey { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The Key of the media that should act as root node for the user group
|
||||
/// <remarks>
|
||||
/// This can be overwritten by a different user group if a user is a member of multiple groups
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
public Guid? MediaStartNodeKey { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Ad-hoc list of permissions provided, and maintained by the front-end. The server has no concept of what these mean.
|
||||
/// </summary>
|
||||
public required ISet<string> Permissions { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.UserGroups;
|
||||
|
||||
public class UserGroupSaveModel : UserGroupBase
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.UserGroups;
|
||||
|
||||
public class UserGroupUpdateModel : UserGroupBase
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.UserGroups;
|
||||
|
||||
public class UserGroupViewModel : UserGroupBase, INamedEntityViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The key identifier for the user group.
|
||||
/// </summary>
|
||||
public required Guid Key { get; init; }
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user