Compare commits
180
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b2fd1253b | ||
|
|
024697f3bf | ||
|
|
d920e93d1e | ||
|
|
853c1acf7e | ||
|
|
0ad020f0ce | ||
|
|
fc1455e0d8 | ||
|
|
15a8b4066a | ||
|
|
38f6e2b0da | ||
|
|
14f60a108a | ||
|
|
375a0b4388 | ||
|
|
6300ccc211 | ||
|
|
5b6f544d2a | ||
|
|
e932fa5404 | ||
|
|
83e580c3a7 | ||
|
|
31ee35e721 | ||
|
|
cf0f3f1380 | ||
|
|
31c3f5ae0c | ||
|
|
c38faec74b | ||
|
|
1e521760df | ||
|
|
8b759cf3a4 | ||
|
|
29c0151460 | ||
|
|
cc9c33bfe6 | ||
|
|
6edffd9f08 | ||
|
|
981f173a79 | ||
|
|
432dda8c47 | ||
|
|
a9fc88d7e6 | ||
|
|
7495c3c7b2 | ||
|
|
f9496e8067 | ||
|
|
7b10d39d66 | ||
|
|
97b3023e14 | ||
|
|
ba0028c03d | ||
|
|
6f38a57c8a | ||
|
|
a0e3ca601e | ||
|
|
1a9d8e3a5d | ||
|
|
76f27726c5 | ||
|
|
d9d66465f6 | ||
|
|
947afdbc1e | ||
|
|
134c8006c0 | ||
|
|
86e7343f99 | ||
|
|
84d0ae3ea2 | ||
|
|
aa76f782ef | ||
|
|
06a2a500b3 | ||
|
|
17a5218a58 | ||
|
|
4c9084238c | ||
|
|
9c03422222 | ||
|
|
b61b9dbd5e | ||
|
|
d9a31d337a | ||
|
|
822c8374ef | ||
|
|
4239f0fc06 | ||
|
|
db43799d0f | ||
|
|
2723e4f77c | ||
|
|
f926316751 | ||
|
|
b991976fd3 | ||
|
|
c0f820a05c | ||
|
|
f6d93ef0ba | ||
|
|
3d1e17b07f | ||
|
|
ae423decc3 | ||
|
|
1d9a031c1c | ||
|
|
4078e83634 | ||
|
|
4b016317f9 | ||
|
|
1f4c19d484 | ||
|
|
fd77074d57 | ||
|
|
bf74636f26 | ||
|
|
1aff4c69f0 | ||
|
|
c139015019 | ||
|
|
90a34e17ca | ||
|
|
806f10e52a | ||
|
|
788e5cd678 | ||
|
|
ebd5fb4d0c | ||
|
|
5f3ce28032 | ||
|
|
ad7053af36 | ||
|
|
6247f54976 | ||
|
|
3e6b9313e5 | ||
|
|
7d41791543 | ||
|
|
157b7d14eb | ||
|
|
f3658bf356 | ||
|
|
e4b3104399 | ||
|
|
9db62a2c0c | ||
|
|
3cd6fcfe67 | ||
|
|
3b736d8c73 | ||
|
|
bbfe40d733 | ||
|
|
9d30d5b11c | ||
|
|
11c19847cf | ||
|
|
8e0912cbf1 | ||
|
|
bf89eae07f | ||
|
|
19e873c7ad | ||
|
|
d4acd53fd5 | ||
|
|
85fc189ee8 | ||
|
|
a48a70595a | ||
|
|
0f539088fa | ||
|
|
80233a7850 | ||
|
|
1dd10eabd4 | ||
|
|
e7a52de097 | ||
|
|
40a2d75282 | ||
|
|
7fb9df9b19 | ||
|
|
1720692d3d | ||
|
|
b798925ddb | ||
|
|
f4caabaa98 | ||
|
|
b1dbd80a07 | ||
|
|
882a0427e4 | ||
|
|
d978a10b61 | ||
|
|
7200b7b623 | ||
|
|
f200da48d8 | ||
|
|
fc815db80b | ||
|
|
c5a3778ce7 | ||
|
|
8c1dfe5431 | ||
|
|
4cd43b264d | ||
|
|
3e835cd949 | ||
|
|
88b1b9de13 | ||
|
|
98e0615338 | ||
|
|
e18cdab185 | ||
|
|
26907f202f | ||
|
|
13d95f31f2 | ||
|
|
c422c82f21 | ||
|
|
ae87f9ccd4 | ||
|
|
3359562e32 | ||
|
|
0e3c8dab8f | ||
|
|
4d8406bb64 | ||
|
|
2bc549bbc4 | ||
|
|
e8b7d9daee | ||
|
|
48ac4b546e | ||
|
|
eb5c57fb94 | ||
|
|
468ca9ae94 | ||
|
|
95cd6797cc | ||
|
|
6f63837dbc | ||
|
|
6dc4eca607 | ||
|
|
aca908c549 | ||
|
|
685a05827e | ||
|
|
07080604b9 | ||
|
|
85176d1bf6 | ||
|
|
52bf6bc412 | ||
|
|
47e09efec6 | ||
|
|
9761ef899b | ||
|
|
164868f32a | ||
|
|
a71ebe1902 | ||
|
|
94f0add4d9 | ||
|
|
9c88326681 | ||
|
|
95e89f8481 | ||
|
|
b25bcdc436 | ||
|
|
028da4545e | ||
|
|
37035e6e7f | ||
|
|
f8c35bb7e9 | ||
|
|
056434fa27 | ||
|
|
e32eb06ddf | ||
|
|
76dd141d86 | ||
|
|
54601d42e2 | ||
|
|
619e025e7a | ||
|
|
609e9f43e1 | ||
|
|
ccc70fc786 | ||
|
|
b5bffdfada | ||
|
|
0dd4443b75 | ||
|
|
10f37494b6 | ||
|
|
2711ac07ac | ||
|
|
80e092069b | ||
|
|
c23e2eb6f2 | ||
|
|
25837e66fc | ||
|
|
3be766fe2a | ||
|
|
34649db5c9 | ||
|
|
4c679a5f4c | ||
|
|
36c66177fc | ||
|
|
ad443c7b13 | ||
|
|
0b6fe91fd3 | ||
|
|
ee496d2730 | ||
|
|
384b9bdb65 | ||
|
|
63113c4552 | ||
|
|
39cad5b2ea | ||
|
|
30633fe728 | ||
|
|
394210a8f7 | ||
|
|
45b0e43b89 | ||
|
|
c21da38c32 | ||
|
|
ef751ca699 | ||
|
|
52debc3010 | ||
|
|
a38a9b29a3 | ||
|
|
e346ac2bee | ||
|
|
425901636e | ||
|
|
d9f17b36c6 | ||
|
|
5fe5a16158 | ||
|
|
131c9cda6f | ||
|
|
e999d3977c | ||
|
|
f7854b8c95 |
+1
@@ -0,0 +1 @@
|
||||
../src/Umbraco.Web.UI.Client/.vscode/lit.code-snippets
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"unprovide"
|
||||
]
|
||||
}
|
||||
@@ -45,8 +45,8 @@
|
||||
<PackageVersion Include="Asp.Versioning.Mvc" Version="8.1.0" />
|
||||
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
|
||||
<PackageVersion Include="Dazinator.Extensions.FileProviders" Version="2.0.0" />
|
||||
<PackageVersion Include="Examine" Version="3.7.0" />
|
||||
<PackageVersion Include="Examine.Core" Version="3.7.0" />
|
||||
<PackageVersion Include="Examine" Version="3.7.1" />
|
||||
<PackageVersion Include="Examine.Core" Version="3.7.1" />
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.11.74" />
|
||||
<PackageVersion Include="JsonPatch.Net" Version="3.1.1" />
|
||||
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.8" />
|
||||
@@ -100,4 +100,4 @@
|
||||
<!-- Examine.Lucene brings in a vulnerable version of Lucene.Net.Replicator -->
|
||||
<PackageVersion Include="Lucene.Net.Replicator" Version="4.8.0-beta00017" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "src/Umbraco.Web.UI.Client"
|
||||
},
|
||||
{
|
||||
"path": "src/Umbraco.Web.UI.Login"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -829,6 +829,8 @@ stages:
|
||||
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.myGetDeploy}}))
|
||||
jobs:
|
||||
- job:
|
||||
pool:
|
||||
vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available.
|
||||
displayName: Push to pre-release feed
|
||||
steps:
|
||||
- checkout: none
|
||||
@@ -890,6 +892,8 @@ stages:
|
||||
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.nuGetDeploy}}))
|
||||
jobs:
|
||||
- job:
|
||||
pool:
|
||||
vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available.
|
||||
displayName: Push to NuGet
|
||||
steps:
|
||||
- checkout: none
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ public class ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions :
|
||||
Id = AuthSchemeName,
|
||||
}
|
||||
},
|
||||
new string[] { }
|
||||
[]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -185,7 +185,7 @@ internal sealed class ApiMediaQueryService : IApiMediaQueryService
|
||||
}
|
||||
|
||||
|
||||
private Attempt<PagedModel<Guid>, ApiMediaQueryOperationStatus> PagedResult(IEnumerable<IPublishedContent> children, int skip, int take)
|
||||
private static Attempt<PagedModel<Guid>, ApiMediaQueryOperationStatus> PagedResult(IEnumerable<IPublishedContent> children, int skip, int take)
|
||||
{
|
||||
IPublishedContent[] childrenAsArray = children as IPublishedContent[] ?? children.ToArray();
|
||||
var result = new PagedModel<Guid>
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
using Asp.Versioning;
|
||||
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.TrackedReferences;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.DataType.References;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class ReferencedByDataTypeController : DataTypeControllerBase
|
||||
{
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
private readonly IRelationTypePresentationFactory _relationTypePresentationFactory;
|
||||
|
||||
public ReferencedByDataTypeController(IDataTypeService dataTypeService, IRelationTypePresentationFactory relationTypePresentationFactory)
|
||||
{
|
||||
_dataTypeService = dataTypeService;
|
||||
_relationTypePresentationFactory = relationTypePresentationFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a paged list of references for the current data type, so you can see where it is being used.
|
||||
/// </summary>
|
||||
[HttpGet("{id:guid}/referenced-by")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<IReferenceResponseModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<IReferenceResponseModel>>> ReferencedBy(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
int skip = 0,
|
||||
int take = 20)
|
||||
{
|
||||
PagedModel<RelationItemModel> relationItems = await _dataTypeService.GetPagedRelationsAsync(id, skip, take);
|
||||
|
||||
var pagedViewModel = new PagedViewModel<IReferenceResponseModel>
|
||||
{
|
||||
Total = relationItems.Total,
|
||||
Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItems.Items),
|
||||
};
|
||||
|
||||
return pagedViewModel;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
@@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.DataType;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[Obsolete("Please use ReferencedByDataTypeController and the referenced-by endpoint. Scheduled for removal in Umbraco 17.")]
|
||||
public class ReferencesDataTypeController : DataTypeControllerBase
|
||||
{
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Controllers.Content;
|
||||
@@ -140,6 +140,10 @@ public abstract class DocumentControllerBase : ContentControllerBase
|
||||
.WithDetail(
|
||||
"An unspecified error occurred while (un)publishing. Please check the logs for additional information.")
|
||||
.Build()),
|
||||
ContentPublishingOperationStatus.TaskResultNotFound => NotFound(problemDetailsBuilder
|
||||
.WithTitle("The result of the submitted task could not be found")
|
||||
.Build()),
|
||||
|
||||
_ => StatusCode(StatusCodes.Status500InternalServerError, "Unknown content operation status."),
|
||||
});
|
||||
|
||||
|
||||
+9
-4
@@ -35,7 +35,7 @@ public class PublishDocumentWithDescendantsController : DocumentControllerBase
|
||||
|
||||
[HttpPut("{id:guid}/publish-with-descendants")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(PublishWithDescendantsResultModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> PublishWithDescendants(CancellationToken cancellationToken, Guid id, PublishDocumentWithDescendantsRequestModel requestModel)
|
||||
@@ -54,10 +54,15 @@ public class PublishDocumentWithDescendantsController : DocumentControllerBase
|
||||
id,
|
||||
requestModel.Cultures,
|
||||
BuildPublishBranchFilter(requestModel),
|
||||
CurrentUserKey(_backOfficeSecurityAccessor));
|
||||
CurrentUserKey(_backOfficeSecurityAccessor),
|
||||
true);
|
||||
|
||||
return attempt.Success
|
||||
? Ok()
|
||||
return attempt.Success && attempt.Result.AcceptedTaskId.HasValue
|
||||
? Ok(new PublishWithDescendantsResultModel
|
||||
{
|
||||
TaskId = attempt.Result.AcceptedTaskId.Value,
|
||||
IsComplete = false
|
||||
})
|
||||
: DocumentPublishingOperationStatusResult(attempt.Status, failedBranchItems: attempt.Result.FailedItems);
|
||||
}
|
||||
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Actions;
|
||||
using Umbraco.Cms.Core.Models.ContentPublishing;
|
||||
using Umbraco.Cms.Core.Security.Authorization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Document;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class PublishDocumentWithDescendantsResultController : DocumentControllerBase
|
||||
{
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly IContentPublishingService _contentPublishingService;
|
||||
|
||||
public PublishDocumentWithDescendantsResultController(
|
||||
IAuthorizationService authorizationService,
|
||||
IContentPublishingService contentPublishingService)
|
||||
{
|
||||
_authorizationService = authorizationService;
|
||||
_contentPublishingService = contentPublishingService;
|
||||
}
|
||||
|
||||
[HttpGet("{id:guid}/publish-with-descendants/result/{taskId:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PublishWithDescendantsResultModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> PublishWithDescendantsResult(CancellationToken cancellationToken, Guid id, Guid taskId)
|
||||
{
|
||||
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
|
||||
User,
|
||||
ContentPermissionResource.Branch(ActionPublish.ActionLetter, id),
|
||||
AuthorizationPolicies.ContentPermissionByResource);
|
||||
|
||||
if (!authorizationResult.Succeeded)
|
||||
{
|
||||
return Forbidden();
|
||||
}
|
||||
|
||||
// Check if the publishing task has completed, if not, return the status.
|
||||
var isPublishing = await _contentPublishingService.IsPublishingBranchAsync(taskId);
|
||||
if (isPublishing)
|
||||
{
|
||||
return Ok(new PublishWithDescendantsResultModel
|
||||
{
|
||||
TaskId = taskId,
|
||||
IsComplete = false
|
||||
});
|
||||
};
|
||||
|
||||
// If completed, get the result and return the status.
|
||||
Attempt<ContentPublishingBranchResult, ContentPublishingOperationStatus> attempt = await _contentPublishingService.GetPublishBranchResultAsync(taskId);
|
||||
return attempt.Success
|
||||
? Ok(new PublishWithDescendantsResultModel
|
||||
{
|
||||
TaskId = taskId,
|
||||
IsComplete = true
|
||||
})
|
||||
: DocumentPublishingOperationStatusResult(attempt.Status, failedBranchItems: attempt.Result.FailedItems);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
using Asp.Versioning;
|
||||
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.TrackedReferences;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Document.RecycleBin;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class ReferencedByDocumentRecycleBinController : DocumentRecycleBinControllerBase
|
||||
{
|
||||
private readonly ITrackedReferencesService _trackedReferencesService;
|
||||
private readonly IRelationTypePresentationFactory _relationTypePresentationFactory;
|
||||
|
||||
public ReferencedByDocumentRecycleBinController(
|
||||
IEntityService entityService,
|
||||
IDocumentPresentationFactory documentPresentationFactory,
|
||||
ITrackedReferencesService trackedReferencesService,
|
||||
IRelationTypePresentationFactory relationTypePresentationFactory)
|
||||
: base(entityService, documentPresentationFactory)
|
||||
{
|
||||
_trackedReferencesService = trackedReferencesService;
|
||||
_relationTypePresentationFactory = relationTypePresentationFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a paged list of tracked references for all items in the document recycle bin, so you can see where an item is being used.
|
||||
/// </summary>
|
||||
[HttpGet("referenced-by")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<IReferenceResponseModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<IReferenceResponseModel>>> ReferencedBy(
|
||||
CancellationToken cancellationToken,
|
||||
int skip = 0,
|
||||
int take = 20)
|
||||
{
|
||||
PagedModel<RelationItemModel> relationItems = await _trackedReferencesService.GetPagedRelationsForRecycleBinAsync(UmbracoObjectTypes.Document, skip, take, true);
|
||||
|
||||
var pagedViewModel = new PagedViewModel<IReferenceResponseModel>
|
||||
{
|
||||
Total = relationItems.Total,
|
||||
Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItems.Items),
|
||||
};
|
||||
|
||||
return pagedViewModel;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
@@ -45,6 +45,6 @@ public class AreReferencedDocumentController : DocumentControllerBase
|
||||
Items = _umbracoMapper.MapEnumerable<Guid, ReferenceByIdModel>(distinctByKeyItemsWithReferencedRelations.Items),
|
||||
};
|
||||
|
||||
return await Task.FromResult(pagedViewModel);
|
||||
return pagedViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -45,6 +45,6 @@ public class ReferencedByDocumentController : DocumentControllerBase
|
||||
Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItems.Items),
|
||||
};
|
||||
|
||||
return await Task.FromResult(pagedViewModel);
|
||||
return pagedViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
@@ -45,6 +45,6 @@ public class ReferencedDescendantsDocumentController : DocumentControllerBase
|
||||
Items = _umbracoMapper.MapEnumerable<RelationItemModel, ReferenceByIdModel>(relationItems.Items),
|
||||
};
|
||||
|
||||
return await Task.FromResult(pagedViewModel);
|
||||
return pagedViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -4,6 +4,7 @@ using Umbraco.Cms.Api.Management.Controllers.Tree;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Api.Management.Services.Entities;
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
@@ -33,7 +34,7 @@ public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBa
|
||||
AppCaches appCaches,
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
|
||||
IDocumentPresentationFactory documentPresentationFactory)
|
||||
: base(entityService, userStartNodeEntitiesService, dataTypeService)
|
||||
: base(entityService, userStartNodeEntitiesService, dataTypeService)
|
||||
{
|
||||
_publicAccessService = publicAccessService;
|
||||
_appCaches = appCaches;
|
||||
@@ -52,6 +53,8 @@ public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBa
|
||||
if (entity is IDocumentEntitySlim documentEntitySlim)
|
||||
{
|
||||
responseModel.IsProtected = _publicAccessService.IsProtected(entity.Path);
|
||||
responseModel.Ancestors = EntityService.GetPathKeys(entity, omitSelf: true)
|
||||
.Select(x => new ReferenceByIdModel(x));
|
||||
responseModel.IsTrashed = entity.Trashed;
|
||||
responseModel.Id = entity.Key;
|
||||
responseModel.CreateDate = entity.CreateDate;
|
||||
|
||||
+25
-2
@@ -1,11 +1,14 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
@@ -16,15 +19,32 @@ public class ValidateCreateDocumentController : CreateDocumentControllerBase
|
||||
{
|
||||
private readonly IDocumentEditingPresentationFactory _documentEditingPresentationFactory;
|
||||
private readonly IContentEditingService _contentEditingService;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")]
|
||||
public ValidateCreateDocumentController(
|
||||
IAuthorizationService authorizationService,
|
||||
IDocumentEditingPresentationFactory documentEditingPresentationFactory,
|
||||
IContentEditingService contentEditingService)
|
||||
: this(
|
||||
authorizationService,
|
||||
documentEditingPresentationFactory,
|
||||
contentEditingService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IBackOfficeSecurityAccessor>())
|
||||
{
|
||||
}
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public ValidateCreateDocumentController(
|
||||
IAuthorizationService authorizationService,
|
||||
IDocumentEditingPresentationFactory documentEditingPresentationFactory,
|
||||
IContentEditingService contentEditingService,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
: base(authorizationService)
|
||||
{
|
||||
_documentEditingPresentationFactory = documentEditingPresentationFactory;
|
||||
_contentEditingService = contentEditingService;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
}
|
||||
|
||||
[HttpPost("validate")]
|
||||
@@ -36,7 +56,10 @@ public class ValidateCreateDocumentController : CreateDocumentControllerBase
|
||||
=> await HandleRequest(requestModel, async () =>
|
||||
{
|
||||
ContentCreateModel model = _documentEditingPresentationFactory.MapCreateModel(requestModel);
|
||||
Attempt<ContentValidationResult, ContentEditingOperationStatus> result = await _contentEditingService.ValidateCreateAsync(model);
|
||||
Attempt<ContentValidationResult, ContentEditingOperationStatus> result =
|
||||
await _contentEditingService.ValidateCreateAsync(
|
||||
model,
|
||||
CurrentUserKey(_backOfficeSecurityAccessor));
|
||||
|
||||
return result.Success
|
||||
? Ok()
|
||||
|
||||
+26
-2
@@ -1,11 +1,14 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
@@ -17,15 +20,32 @@ public class ValidateUpdateDocumentController : UpdateDocumentControllerBase
|
||||
{
|
||||
private readonly IContentEditingService _contentEditingService;
|
||||
private readonly IDocumentEditingPresentationFactory _documentEditingPresentationFactory;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")]
|
||||
public ValidateUpdateDocumentController(
|
||||
IAuthorizationService authorizationService,
|
||||
IContentEditingService contentEditingService,
|
||||
IDocumentEditingPresentationFactory documentEditingPresentationFactory)
|
||||
: this(
|
||||
authorizationService,
|
||||
contentEditingService,
|
||||
documentEditingPresentationFactory,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IBackOfficeSecurityAccessor>())
|
||||
{
|
||||
}
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public ValidateUpdateDocumentController(
|
||||
IAuthorizationService authorizationService,
|
||||
IContentEditingService contentEditingService,
|
||||
IDocumentEditingPresentationFactory documentEditingPresentationFactory,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
: base(authorizationService)
|
||||
{
|
||||
_contentEditingService = contentEditingService;
|
||||
_documentEditingPresentationFactory = documentEditingPresentationFactory;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
}
|
||||
|
||||
[HttpPut("{id:guid}/validate")]
|
||||
@@ -62,7 +82,11 @@ public class ValidateUpdateDocumentController : UpdateDocumentControllerBase
|
||||
=> await HandleRequest(id, requestModel, async () =>
|
||||
{
|
||||
ValidateContentUpdateModel model = _documentEditingPresentationFactory.MapValidateUpdateModel(requestModel);
|
||||
Attempt<ContentValidationResult, ContentEditingOperationStatus> result = await _contentEditingService.ValidateUpdateAsync(id, model);
|
||||
Attempt<ContentValidationResult, ContentEditingOperationStatus> result =
|
||||
await _contentEditingService.ValidateUpdateAsync(
|
||||
id,
|
||||
model,
|
||||
CurrentUserKey(_backOfficeSecurityAccessor));
|
||||
|
||||
return result.Success
|
||||
? Ok()
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DocumentBlueprint;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.DocumentBlueprint;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocuments)]
|
||||
public class ScaffoldDocumentBlueprintController : DocumentBlueprintControllerBase
|
||||
{
|
||||
private readonly IContentBlueprintEditingService _contentBlueprintEditingService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public ScaffoldDocumentBlueprintController(IContentBlueprintEditingService contentBlueprintEditingService, IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_contentBlueprintEditingService = contentBlueprintEditingService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
[HttpGet("{id:guid}/scaffold")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(DocumentBlueprintResponseModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> Scaffold(CancellationToken cancellationToken, Guid id)
|
||||
{
|
||||
IContent? blueprint = await _contentBlueprintEditingService.GetScaffoldedAsync(id);
|
||||
return blueprint is not null
|
||||
? Ok(_umbracoMapper.Map<DocumentBlueprintResponseModel>(blueprint))
|
||||
: DocumentBlueprintNotFound();
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
using Asp.Versioning;
|
||||
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.TrackedReferences;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Media.RecycleBin;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class ReferencedByMediaRecycleBinController : MediaRecycleBinControllerBase
|
||||
{
|
||||
private readonly ITrackedReferencesService _trackedReferencesService;
|
||||
private readonly IRelationTypePresentationFactory _relationTypePresentationFactory;
|
||||
|
||||
public ReferencedByMediaRecycleBinController(
|
||||
IEntityService entityService,
|
||||
IMediaPresentationFactory mediaPresentationFactory,
|
||||
ITrackedReferencesService trackedReferencesService,
|
||||
IRelationTypePresentationFactory relationTypePresentationFactory)
|
||||
: base(entityService, mediaPresentationFactory)
|
||||
{
|
||||
_trackedReferencesService = trackedReferencesService;
|
||||
_relationTypePresentationFactory = relationTypePresentationFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a paged list of tracked references for all items in the media recycle bin, so you can see where an item is being used.
|
||||
/// </summary>
|
||||
[HttpGet("referenced-by")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<IReferenceResponseModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<IReferenceResponseModel>>> ReferencedBy(
|
||||
CancellationToken cancellationToken,
|
||||
int skip = 0,
|
||||
int take = 20)
|
||||
{
|
||||
PagedModel<RelationItemModel> relationItems = await _trackedReferencesService.GetPagedRelationsForRecycleBinAsync(UmbracoObjectTypes.Media, skip, take, true);
|
||||
|
||||
var pagedViewModel = new PagedViewModel<IReferenceResponseModel>
|
||||
{
|
||||
Total = relationItems.Total,
|
||||
Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItems.Items),
|
||||
};
|
||||
|
||||
return pagedViewModel;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
@@ -46,6 +46,6 @@ public class AreReferencedMediaController : MediaControllerBase
|
||||
Items = _umbracoMapper.MapEnumerable<Guid, ReferenceByIdModel>(distinctByKeyItemsWithReferencedRelations.Items),
|
||||
};
|
||||
|
||||
return await Task.FromResult(pagedViewModel);
|
||||
return pagedViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -45,6 +45,6 @@ public class ReferencedByMediaController : MediaControllerBase
|
||||
Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItems.Items),
|
||||
};
|
||||
|
||||
return await Task.FromResult(pagedViewModel);
|
||||
return pagedViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
@@ -45,6 +45,6 @@ public class ReferencedDescendantsMediaController : MediaControllerBase
|
||||
Items = _umbracoMapper.MapEnumerable<RelationItemModel, ReferenceByIdModel>(relationItems.Items),
|
||||
};
|
||||
|
||||
return await Task.FromResult(pagedViewModel);
|
||||
return pagedViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Member.References;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class AreReferencedMemberController : MemberControllerBase
|
||||
{
|
||||
private readonly ITrackedReferencesService _trackedReferencesSkipTakeService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public AreReferencedMemberController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_trackedReferencesSkipTakeService = trackedReferencesSkipTakeService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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).
|
||||
/// This is basically finding children of relations.
|
||||
/// </remarks>
|
||||
// [HttpGet("item")]
|
||||
[HttpGet("are-referenced")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<ReferenceByIdModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<ReferenceByIdModel>>> GetPagedReferencedItems(
|
||||
CancellationToken cancellationToken,
|
||||
[FromQuery(Name="id")] HashSet<Guid> ids,
|
||||
int skip = 0,
|
||||
int take = 20)
|
||||
{
|
||||
PagedModel<Guid> distinctByKeyItemsWithReferencedRelations = await _trackedReferencesSkipTakeService.GetPagedKeysWithDependentReferencesAsync(ids, Constants.ObjectTypes.Member, skip, take);
|
||||
var pagedViewModel = new PagedViewModel<ReferenceByIdModel>
|
||||
{
|
||||
Total = distinctByKeyItemsWithReferencedRelations.Total,
|
||||
Items = _umbracoMapper.MapEnumerable<Guid, ReferenceByIdModel>(distinctByKeyItemsWithReferencedRelations.Items),
|
||||
};
|
||||
|
||||
return pagedViewModel;
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
using Asp.Versioning;
|
||||
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.TrackedReferences;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Member.References;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class ReferencedByMemberController : MemberControllerBase
|
||||
{
|
||||
private readonly ITrackedReferencesService _trackedReferencesService;
|
||||
private readonly IRelationTypePresentationFactory _relationTypePresentationFactory;
|
||||
|
||||
public ReferencedByMemberController(ITrackedReferencesService trackedReferencesService, IRelationTypePresentationFactory relationTypePresentationFactory)
|
||||
{
|
||||
_trackedReferencesService = trackedReferencesService;
|
||||
_relationTypePresentationFactory = relationTypePresentationFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a page list of tracked references for the current item, so you can see where an item is being used.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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:guid}/referenced-by")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<IReferenceResponseModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<IReferenceResponseModel>>> ReferencedBy(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
int skip = 0,
|
||||
int take = 20)
|
||||
{
|
||||
PagedModel<RelationItemModel> relationItems = await _trackedReferencesService.GetPagedRelationsForItemAsync(id, skip, take, true);
|
||||
|
||||
var pagedViewModel = new PagedViewModel<IReferenceResponseModel>
|
||||
{
|
||||
Total = relationItems.Total,
|
||||
Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItems.Items),
|
||||
};
|
||||
|
||||
return pagedViewModel;
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Member.References;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class ReferencedDescendantsMemberController : MemberControllerBase
|
||||
{
|
||||
private readonly ITrackedReferencesService _trackedReferencesSkipTakeService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public ReferencedDescendantsMemberController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_trackedReferencesSkipTakeService = trackedReferencesSkipTakeService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a page list of the child nodes of the current item used in any kind of relation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Used when deleting and unpublishing a single item to check if this item has any descending items that are in any
|
||||
/// kind of relation.
|
||||
/// This is basically finding the descending items which are children in relations.
|
||||
/// </remarks>
|
||||
[HttpGet("{id:guid}/referenced-descendants")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<ReferenceByIdModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<ReferenceByIdModel>>> ReferencedDescendants(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
int skip = 0,
|
||||
int take = 20)
|
||||
{
|
||||
PagedModel<RelationItemModel> relationItems = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(id, skip, take, true);
|
||||
var pagedViewModel = new PagedViewModel<ReferenceByIdModel>
|
||||
{
|
||||
Total = relationItems.Total,
|
||||
Items = _umbracoMapper.MapEnumerable<RelationItemModel, ReferenceByIdModel>(relationItems.Items),
|
||||
};
|
||||
|
||||
return pagedViewModel;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@ public class RebuildPublishedCacheController : PublishedCacheControllerBase
|
||||
{
|
||||
var problemDetails = new ProblemDetails
|
||||
{
|
||||
Title = "Database cache can not be rebuilt",
|
||||
Title = "Database cache cannot be rebuilt",
|
||||
Detail = $"The database cache is in the process of rebuilding.",
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Type = "Error",
|
||||
|
||||
@@ -21,6 +21,10 @@ public abstract class SecurityControllerBase : ManagementApiControllerBase
|
||||
.WithTitle("The password reset token was invalid")
|
||||
.WithDetail("The specified password reset token was either used already or wrong.")
|
||||
.Build()),
|
||||
UserOperationStatus.CancelledByNotification => BadRequest(problemDetailsBuilder
|
||||
.WithTitle("Cancelled by notification")
|
||||
.WithDetail("A notification handler prevented the user operation.")
|
||||
.Build()),
|
||||
UserOperationStatus.UnknownFailure => BadRequest(problemDetailsBuilder
|
||||
.WithTitle("Unknown failure")
|
||||
.WithDetail(errorMessageResult?.Error?.ErrorMessage ?? "The error was unknown")
|
||||
|
||||
+4
-1
@@ -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;
|
||||
@@ -17,6 +17,9 @@ public abstract class TemporaryFileControllerBase : ManagementApiControllerBase
|
||||
.WithTitle("File extension not allowed")
|
||||
.WithDetail("The file extension is not allowed.")
|
||||
.Build()),
|
||||
TemporaryFileOperationStatus.InvalidFileName => BadRequest(problemDetailsBuilder
|
||||
.WithTitle("The provided file name is not valid")
|
||||
.Build()),
|
||||
TemporaryFileOperationStatus.KeyAlreadyUsed => BadRequest(problemDetailsBuilder
|
||||
.WithTitle("Key already used")
|
||||
.WithDetail("The specified key is already used.")
|
||||
|
||||
@@ -42,11 +42,21 @@ public abstract class UserStartNodeTreeControllerBase<TItem> : EntityTreeControl
|
||||
|
||||
protected override IEntitySlim[] GetPagedChildEntities(Guid parentKey, int skip, int take, out long totalItems)
|
||||
{
|
||||
IEntitySlim[] children = base.GetPagedChildEntities(parentKey, skip, take, out totalItems);
|
||||
return UserHasRootAccess() || IgnoreUserStartNodes()
|
||||
? children
|
||||
// Keeping the correct totalItems amount from GetPagedChildEntities
|
||||
: CalculateAccessMap(() => _userStartNodeEntitiesService.ChildUserAccessEntities(children, UserStartNodePaths), out _);
|
||||
if (UserHasRootAccess() || IgnoreUserStartNodes())
|
||||
{
|
||||
return base.GetPagedChildEntities(parentKey, skip, take, out totalItems);
|
||||
}
|
||||
|
||||
IEnumerable<UserAccessEntity> userAccessEntities = _userStartNodeEntitiesService.ChildUserAccessEntities(
|
||||
ItemObjectType,
|
||||
UserStartNodePaths,
|
||||
parentKey,
|
||||
skip,
|
||||
take,
|
||||
ItemOrdering,
|
||||
out totalItems);
|
||||
|
||||
return CalculateAccessMap(() => userAccessEntities, out _);
|
||||
}
|
||||
|
||||
protected override TItem[] MapTreeItemViewModels(Guid? parentKey, IEntitySlim[] entities)
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.User;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.User;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[Authorize(Policy = AuthorizationPolicies.RequireAdminAccess)]
|
||||
public class ConfigurationUserController : UserControllerBase
|
||||
{
|
||||
private readonly IUserPresentationFactory _userPresentationFactory;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
@@ -5,11 +6,13 @@ using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Webhook.Logs;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Webhook.Logs;
|
||||
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Webhook}")]
|
||||
[ApiExplorerSettings(GroupName = "Webhook")]
|
||||
[Authorize(Policy = AuthorizationPolicies.TreeAccessWebhooks)]
|
||||
public class WebhookLogControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
protected PagedViewModel<WebhookLogResponseModel> CreatePagedWebhookLogResponseModel(PagedModel<WebhookLog> logs, IWebhookPresentationFactory webhookPresentationFactory)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document.Collection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
@@ -9,11 +12,23 @@ namespace Umbraco.Cms.Api.Management.Factories;
|
||||
public class DocumentCollectionPresentationFactory : ContentCollectionPresentationFactory<IContent, DocumentCollectionResponseModel, DocumentValueResponseModel, DocumentVariantResponseModel>, IDocumentCollectionPresentationFactory
|
||||
{
|
||||
private readonly IPublicAccessService _publicAccessService;
|
||||
private readonly IEntityService _entityService;
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Scheduled for removal in V17.")]
|
||||
public DocumentCollectionPresentationFactory(IUmbracoMapper mapper, IPublicAccessService publicAccessService)
|
||||
: base(mapper)
|
||||
: this(
|
||||
mapper,
|
||||
publicAccessService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IEntityService>())
|
||||
{
|
||||
}
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public DocumentCollectionPresentationFactory(IUmbracoMapper mapper, IPublicAccessService publicAccessService, IEntityService entityService)
|
||||
: base(mapper)
|
||||
{
|
||||
_publicAccessService = publicAccessService;
|
||||
_entityService = entityService;
|
||||
}
|
||||
|
||||
protected override Task SetUnmappedProperties(ListViewPagedModel<IContent> contentCollection, List<DocumentCollectionResponseModel> collectionResponseModels)
|
||||
@@ -27,6 +42,8 @@ public class DocumentCollectionPresentationFactory : ContentCollectionPresentati
|
||||
}
|
||||
|
||||
item.IsProtected = _publicAccessService.IsProtected(matchingContentItem).Success;
|
||||
item.Ancestors = _entityService.GetPathKeys(matchingContentItem, omitSelf: true)
|
||||
.Select(x => new ReferenceByIdModel(x));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.RedirectUrlManagement;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
@@ -22,6 +22,12 @@ public class RedirectUrlPresentationFactory : IRedirectUrlPresentationFactory
|
||||
|
||||
var originalUrl = _publishedUrlProvider.GetUrlFromRoute(source.ContentId, source.Url, source.Culture);
|
||||
|
||||
// Even if the URL could not be extracted from the route, if we have a path as a the route for the original URL, we should display it.
|
||||
if (originalUrl == "#" && source.Url.StartsWith('/'))
|
||||
{
|
||||
originalUrl = source.Url;
|
||||
}
|
||||
|
||||
return new RedirectUrlResponseModel
|
||||
{
|
||||
OriginalUrl = originalUrl,
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Infrastructure.Scoping;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
@@ -9,21 +14,73 @@ namespace Umbraco.Cms.Api.Management.Factories;
|
||||
public class RelationTypePresentationFactory : IRelationTypePresentationFactory
|
||||
{
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
private readonly IEntityRepository _entityRepository;
|
||||
private readonly IDocumentPresentationFactory _documentPresentationFactory;
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
|
||||
[Obsolete("Please use the non obsoleted constructor. Scheduled for removal in v17")]
|
||||
public RelationTypePresentationFactory(IUmbracoMapper umbracoMapper)
|
||||
: this(
|
||||
umbracoMapper,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IEntityRepository>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentPresentationFactory>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IScopeProvider>())
|
||||
{
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<IReferenceResponseModel>> CreateReferenceResponseModelsAsync(IEnumerable<RelationItemModel> relationItemModels)
|
||||
public RelationTypePresentationFactory(
|
||||
IUmbracoMapper umbracoMapper,
|
||||
IEntityRepository entityRepository,
|
||||
IDocumentPresentationFactory documentPresentationFactory,
|
||||
IScopeProvider scopeProvider)
|
||||
{
|
||||
IReferenceResponseModel[] result = relationItemModels.Select(relationItemModel => relationItemModel.NodeType switch
|
||||
{
|
||||
Constants.UdiEntityType.Document => _umbracoMapper.Map<DocumentReferenceResponseModel>(relationItemModel),
|
||||
Constants.UdiEntityType.Media => _umbracoMapper.Map<MediaReferenceResponseModel>(relationItemModel),
|
||||
_ => _umbracoMapper.Map<DefaultReferenceResponseModel>(relationItemModel) as IReferenceResponseModel,
|
||||
}).WhereNotNull().ToArray();
|
||||
_umbracoMapper = umbracoMapper;
|
||||
_entityRepository = entityRepository;
|
||||
_documentPresentationFactory = documentPresentationFactory;
|
||||
_scopeProvider = scopeProvider;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<IReferenceResponseModel>> CreateReferenceResponseModelsAsync(
|
||||
IEnumerable<RelationItemModel> relationItemModels)
|
||||
{
|
||||
IReadOnlyCollection<RelationItemModel> relationItemModelsCollection = relationItemModels.ToArray();
|
||||
|
||||
Guid[] documentKeys = relationItemModelsCollection
|
||||
.Where(item => item.NodeType is Constants.UdiEntityType.Document)
|
||||
.Select(item => item.NodeKey).Distinct().ToArray();
|
||||
|
||||
using IScope scope = _scopeProvider.CreateScope(autoComplete: true);
|
||||
|
||||
var slimEntities = _entityRepository.GetAll(Constants.ObjectTypes.Document, documentKeys).ToList();
|
||||
|
||||
IReferenceResponseModel[] result = relationItemModelsCollection.Select(relationItemModel =>
|
||||
relationItemModel.NodeType switch
|
||||
{
|
||||
Constants.ReferenceType.Document => MapDocumentReference(relationItemModel, slimEntities),
|
||||
Constants.ReferenceType.Media => _umbracoMapper.Map<MediaReferenceResponseModel>(relationItemModel),
|
||||
Constants.ReferenceType.Member => _umbracoMapper.Map<MemberReferenceResponseModel>(relationItemModel),
|
||||
Constants.ReferenceType.DocumentTypePropertyType => _umbracoMapper.Map<DocumentTypePropertyTypeReferenceResponseModel>(relationItemModel),
|
||||
Constants.ReferenceType.MediaTypePropertyType => _umbracoMapper.Map<MediaTypePropertyTypeReferenceResponseModel>(relationItemModel),
|
||||
Constants.ReferenceType.MemberTypePropertyType => _umbracoMapper.Map<MemberTypePropertyTypeReferenceResponseModel>(relationItemModel),
|
||||
_ => _umbracoMapper.Map<DefaultReferenceResponseModel>(relationItemModel),
|
||||
}).WhereNotNull().ToArray();
|
||||
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
private IReferenceResponseModel? MapDocumentReference(RelationItemModel relationItemModel,
|
||||
List<IEntitySlim> slimEntities)
|
||||
{
|
||||
DocumentReferenceResponseModel? documentReferenceResponseModel =
|
||||
_umbracoMapper.Map<DocumentReferenceResponseModel>(relationItemModel);
|
||||
if (documentReferenceResponseModel is not null
|
||||
&& slimEntities.FirstOrDefault(e => e.Key == relationItemModel.NodeKey) is DocumentEntitySlim
|
||||
matchingSlimDocument)
|
||||
{
|
||||
documentReferenceResponseModel.Variants =
|
||||
_documentPresentationFactory.CreateVariantsItemResponseModels(matchingSlimDocument);
|
||||
}
|
||||
|
||||
return documentReferenceResponseModel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Api.Management.Security;
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.User;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.User.Current;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.User.Item;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.UserGroup;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.UserGroup.Permissions;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Mail;
|
||||
using Umbraco.Cms.Core.Media;
|
||||
@@ -20,7 +24,6 @@ namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public class UserPresentationFactory : IUserPresentationFactory
|
||||
{
|
||||
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly AppCaches _appCaches;
|
||||
private readonly MediaFileManager _mediaFileManager;
|
||||
@@ -31,7 +34,10 @@ public class UserPresentationFactory : IUserPresentationFactory
|
||||
private readonly IPasswordConfigurationPresentationFactory _passwordConfigurationPresentationFactory;
|
||||
private readonly IBackOfficeExternalLoginProviders _externalLoginProviders;
|
||||
private readonly SecuritySettings _securitySettings;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IContentService _contentService;
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")]
|
||||
public UserPresentationFactory(
|
||||
IEntityService entityService,
|
||||
AppCaches appCaches,
|
||||
@@ -43,6 +49,35 @@ public class UserPresentationFactory : IUserPresentationFactory
|
||||
IPasswordConfigurationPresentationFactory passwordConfigurationPresentationFactory,
|
||||
IOptionsSnapshot<SecuritySettings> securitySettings,
|
||||
IBackOfficeExternalLoginProviders externalLoginProviders)
|
||||
: this(
|
||||
entityService,
|
||||
appCaches,
|
||||
mediaFileManager,
|
||||
imageUrlGenerator,
|
||||
userGroupPresentationFactory,
|
||||
absoluteUrlBuilder,
|
||||
emailSender,
|
||||
passwordConfigurationPresentationFactory,
|
||||
securitySettings,
|
||||
externalLoginProviders,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IUserService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IContentService>())
|
||||
{
|
||||
}
|
||||
|
||||
public UserPresentationFactory(
|
||||
IEntityService entityService,
|
||||
AppCaches appCaches,
|
||||
MediaFileManager mediaFileManager,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IUserGroupPresentationFactory userGroupPresentationFactory,
|
||||
IAbsoluteUrlBuilder absoluteUrlBuilder,
|
||||
IEmailSender emailSender,
|
||||
IPasswordConfigurationPresentationFactory passwordConfigurationPresentationFactory,
|
||||
IOptionsSnapshot<SecuritySettings> securitySettings,
|
||||
IBackOfficeExternalLoginProviders externalLoginProviders,
|
||||
IUserService userService,
|
||||
IContentService contentService)
|
||||
{
|
||||
_entityService = entityService;
|
||||
_appCaches = appCaches;
|
||||
@@ -54,6 +89,8 @@ public class UserPresentationFactory : IUserPresentationFactory
|
||||
_externalLoginProviders = externalLoginProviders;
|
||||
_securitySettings = securitySettings.Value;
|
||||
_absoluteUrlBuilder = absoluteUrlBuilder;
|
||||
_userService = userService;
|
||||
_contentService = contentService;
|
||||
}
|
||||
|
||||
public UserResponseModel CreateResponseModel(IUser user)
|
||||
@@ -195,7 +232,7 @@ public class UserPresentationFactory : IUserPresentationFactory
|
||||
var contentStartNodeIds = user.CalculateContentStartNodeIds(_entityService, _appCaches);
|
||||
var documentStartNodeKeys = GetKeysFromIds(contentStartNodeIds, UmbracoObjectTypes.Document);
|
||||
|
||||
var permissions = presentationGroups.SelectMany(x => x.Permissions).ToHashSet();
|
||||
var permissions = GetAggregatedGranularPermissions(user, presentationGroups);
|
||||
var fallbackPermissions = presentationGroups.SelectMany(x => x.FallbackPermissions).ToHashSet();
|
||||
|
||||
var hasAccessToAllLanguages = presentationGroups.Any(x => x.HasAccessToAllLanguages);
|
||||
@@ -225,6 +262,42 @@ public class UserPresentationFactory : IUserPresentationFactory
|
||||
});
|
||||
}
|
||||
|
||||
private HashSet<IPermissionPresentationModel> GetAggregatedGranularPermissions(IUser user, IEnumerable<UserGroupResponseModel> presentationGroups)
|
||||
{
|
||||
var permissions = presentationGroups.SelectMany(x => x.Permissions).ToHashSet();
|
||||
|
||||
// The raw permission data consists of several permissions for each document. We want to aggregate this server-side so
|
||||
// we return one set of aggregate permissions per document that the client will use.
|
||||
|
||||
// Get the unique document keys that have granular permissions.
|
||||
IEnumerable<Guid> documentKeysWithGranularPermissions = permissions
|
||||
.Where(x => x is DocumentPermissionPresentationModel)
|
||||
.Cast<DocumentPermissionPresentationModel>()
|
||||
.Select(x => x.Document.Id)
|
||||
.Distinct();
|
||||
|
||||
var aggregatedPermissions = new HashSet<IPermissionPresentationModel>();
|
||||
foreach (Guid documentKey in documentKeysWithGranularPermissions)
|
||||
{
|
||||
// Retrieve the path of the document.
|
||||
var path = _contentService.GetById(documentKey)?.Path;
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// With the path we can call the same logic as used server-side for authorizing access to resources.
|
||||
EntityPermissionSet permissionsForPath = _userService.GetPermissionsForPath(user, path);
|
||||
aggregatedPermissions.Add(new DocumentPermissionPresentationModel
|
||||
{
|
||||
Document = new ReferenceByIdModel(documentKey),
|
||||
Verbs = permissionsForPath.GetAllPermissions()
|
||||
});
|
||||
}
|
||||
|
||||
return aggregatedPermissions;
|
||||
}
|
||||
|
||||
public async Task<CalculatedUserStartNodesResponseModel> CreateCalculatedUserStartNodesResponseModelAsync(IUser user)
|
||||
{
|
||||
var mediaStartNodeIds = user.CalculateMediaStartNodeIds(_entityService, _appCaches);
|
||||
|
||||
@@ -67,7 +67,7 @@ public class DocumentMapDefinition : ContentMapDefinition<IContent, DocumentValu
|
||||
target.IsTrashed = source.Trashed;
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -IsProtected
|
||||
// Umbraco.Code.MapAll -IsProtected -Ancestors
|
||||
private void Map(IContent source, DocumentCollectionResponseModel target, MapperContext context)
|
||||
{
|
||||
target.Id = source.Key;
|
||||
|
||||
+67
-2
@@ -1,4 +1,4 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
@@ -11,12 +11,16 @@ public class TrackedReferenceViewModelsMapDefinition : IMapDefinition
|
||||
{
|
||||
mapper.Define<RelationItemModel, DocumentReferenceResponseModel>((source, context) => new DocumentReferenceResponseModel(), Map);
|
||||
mapper.Define<RelationItemModel, MediaReferenceResponseModel>((source, context) => new MediaReferenceResponseModel(), Map);
|
||||
mapper.Define<RelationItemModel, MemberReferenceResponseModel>((source, context) => new MemberReferenceResponseModel(), Map);
|
||||
mapper.Define<RelationItemModel, DocumentTypePropertyTypeReferenceResponseModel>((source, context) => new DocumentTypePropertyTypeReferenceResponseModel(), Map);
|
||||
mapper.Define<RelationItemModel, MediaTypePropertyTypeReferenceResponseModel>((source, context) => new MediaTypePropertyTypeReferenceResponseModel(), Map);
|
||||
mapper.Define<RelationItemModel, MemberTypePropertyTypeReferenceResponseModel>((source, context) => new MemberTypePropertyTypeReferenceResponseModel(), Map);
|
||||
mapper.Define<RelationItemModel, DefaultReferenceResponseModel>((source, context) => new DefaultReferenceResponseModel(), Map);
|
||||
mapper.Define<RelationItemModel, ReferenceByIdModel>((source, context) => new ReferenceByIdModel(), Map);
|
||||
mapper.Define<Guid, ReferenceByIdModel>((source, context) => new ReferenceByIdModel(), Map);
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll
|
||||
// Umbraco.Code.MapAll -Variants
|
||||
private void Map(RelationItemModel source, DocumentReferenceResponseModel target, MapperContext context)
|
||||
{
|
||||
target.Id = source.NodeKey;
|
||||
@@ -24,6 +28,7 @@ public class TrackedReferenceViewModelsMapDefinition : IMapDefinition
|
||||
target.Published = source.NodePublished;
|
||||
target.DocumentType = new TrackedReferenceDocumentType
|
||||
{
|
||||
Id = source.ContentTypeKey,
|
||||
Alias = source.ContentTypeAlias,
|
||||
Icon = source.ContentTypeIcon,
|
||||
Name = source.ContentTypeName,
|
||||
@@ -37,6 +42,66 @@ public class TrackedReferenceViewModelsMapDefinition : IMapDefinition
|
||||
target.Name = source.NodeName;
|
||||
target.MediaType = new TrackedReferenceMediaType
|
||||
{
|
||||
Id = source.ContentTypeKey,
|
||||
Alias = source.ContentTypeAlias,
|
||||
Icon = source.ContentTypeIcon,
|
||||
Name = source.ContentTypeName,
|
||||
};
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll
|
||||
private void Map(RelationItemModel source, MemberReferenceResponseModel target, MapperContext context)
|
||||
{
|
||||
target.Id = source.NodeKey;
|
||||
target.Name = source.NodeName;
|
||||
target.MemberType = new TrackedReferenceMemberType
|
||||
{
|
||||
Id = source.ContentTypeKey,
|
||||
Alias = source.ContentTypeAlias,
|
||||
Icon = source.ContentTypeIcon,
|
||||
Name = source.ContentTypeName,
|
||||
};
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll
|
||||
private void Map(RelationItemModel source, DocumentTypePropertyTypeReferenceResponseModel target, MapperContext context)
|
||||
{
|
||||
target.Id = source.NodeKey;
|
||||
target.Name = source.NodeName;
|
||||
target.Alias = source.NodeAlias;
|
||||
target.DocumentType = new TrackedReferenceDocumentType
|
||||
{
|
||||
Id = source.ContentTypeKey,
|
||||
Alias = source.ContentTypeAlias,
|
||||
Icon = source.ContentTypeIcon,
|
||||
Name = source.ContentTypeName,
|
||||
};
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll
|
||||
private void Map(RelationItemModel source, MediaTypePropertyTypeReferenceResponseModel target, MapperContext context)
|
||||
{
|
||||
target.Id = source.NodeKey;
|
||||
target.Name = source.NodeName;
|
||||
target.Alias = source.NodeAlias;
|
||||
target.MediaType = new TrackedReferenceMediaType
|
||||
{
|
||||
Id = source.ContentTypeKey,
|
||||
Alias = source.ContentTypeAlias,
|
||||
Icon = source.ContentTypeIcon,
|
||||
Name = source.ContentTypeName,
|
||||
};
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll
|
||||
private void Map(RelationItemModel source, MemberTypePropertyTypeReferenceResponseModel target, MapperContext context)
|
||||
{
|
||||
target.Id = source.NodeKey;
|
||||
target.Name = source.NodeName;
|
||||
target.Alias = source.NodeAlias;
|
||||
target.MemberType = new TrackedReferenceMemberType
|
||||
{
|
||||
Id = source.ContentTypeKey,
|
||||
Alias = source.ContentTypeAlias,
|
||||
Icon = source.ContentTypeIcon,
|
||||
Name = source.ContentTypeName,
|
||||
|
||||
@@ -816,6 +816,70 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/data-type/{id}/referenced-by": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Data Type"
|
||||
],
|
||||
"operationId": "GetDataTypeByIdReferencedBy",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skip",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "take",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 20
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PagedIReferenceResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/data-type/{id}/references": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -872,6 +936,7 @@
|
||||
"description": "The authenticated user does not have access to this resource"
|
||||
}
|
||||
},
|
||||
"deprecated": true,
|
||||
"security": [
|
||||
{
|
||||
"Backoffice User": [ ]
|
||||
@@ -3420,6 +3485,66 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/document-blueprint/{id}/scaffold": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Document Blueprint"
|
||||
],
|
||||
"operationId": "GetDocumentBlueprintByIdScaffold",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentBlueprintResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/document-blueprint/folder": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -6245,6 +6370,13 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isElement",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skip",
|
||||
"in": "query",
|
||||
@@ -8895,6 +9027,17 @@
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PublishWithDescendantsResultModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
@@ -8975,6 +9118,89 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/document/{id}/publish-with-descendants/result/{taskId}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Document"
|
||||
],
|
||||
"operationId": "GetDocumentByIdPublishWithDescendantsResultByTaskId",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "taskId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PublishWithDescendantsResultModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/document/{id}/published": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -10069,6 +10295,13 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "trashed",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skip",
|
||||
"in": "query",
|
||||
@@ -10597,6 +10830,61 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/recycle-bin/document/referenced-by": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Document"
|
||||
],
|
||||
"operationId": "GetRecycleBinDocumentReferencedBy",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "skip",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "take",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 20
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PagedIReferenceResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/recycle-bin/document/root": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -15766,6 +16054,13 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "trashed",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skip",
|
||||
"in": "query",
|
||||
@@ -17741,6 +18036,61 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/recycle-bin/media/referenced-by": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Media"
|
||||
],
|
||||
"operationId": "GetRecycleBinMediaReferencedBy",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "skip",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "take",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 20
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PagedIReferenceResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/recycle-bin/media/root": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -20170,6 +20520,134 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/member/{id}/referenced-by": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Member"
|
||||
],
|
||||
"operationId": "GetMemberByIdReferencedBy",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skip",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "take",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 20
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PagedIReferenceResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/member/{id}/referenced-descendants": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Member"
|
||||
],
|
||||
"operationId": "GetMemberByIdReferencedDescendants",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skip",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "take",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 20
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PagedReferenceByIdModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/member/{id}/validate": {
|
||||
"put": {
|
||||
"tags": [
|
||||
@@ -20312,6 +20790,73 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/member/are-referenced": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Member"
|
||||
],
|
||||
"operationId": "GetMemberAreReferenced",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"uniqueItems": true,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skip",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "take",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 20
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PagedReferenceByIdModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/member/configuration": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -36696,7 +37241,6 @@
|
||||
"type": "string"
|
||||
},
|
||||
"translation": {
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
@@ -36872,6 +37416,7 @@
|
||||
},
|
||||
"DocumentCollectionResponseModel": {
|
||||
"required": [
|
||||
"ancestors",
|
||||
"documentType",
|
||||
"id",
|
||||
"isProtected",
|
||||
@@ -36927,6 +37472,16 @@
|
||||
"isProtected": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"ancestors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ReferenceByIdModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"updater": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
@@ -37126,7 +37681,8 @@
|
||||
"required": [
|
||||
"$type",
|
||||
"documentType",
|
||||
"id"
|
||||
"id",
|
||||
"variants"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -37151,6 +37707,16 @@
|
||||
"$ref": "#/components/schemas/TrackedReferenceDocumentTypeModel"
|
||||
}
|
||||
]
|
||||
},
|
||||
"variants": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentVariantItemResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -37229,6 +37795,7 @@
|
||||
},
|
||||
"DocumentTreeItemResponseModel": {
|
||||
"required": [
|
||||
"ancestors",
|
||||
"createDate",
|
||||
"documentType",
|
||||
"hasChildren",
|
||||
@@ -37268,6 +37835,16 @@
|
||||
"isProtected": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"ancestors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ReferenceByIdModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"documentType": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -37513,6 +38090,45 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DocumentTypePropertyTypeReferenceResponseModel": {
|
||||
"required": [
|
||||
"$type",
|
||||
"documentType",
|
||||
"id"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"$type": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"alias": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"documentType": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/TrackedReferenceDocumentTypeModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"discriminator": {
|
||||
"propertyName": "$type",
|
||||
"mapping": {
|
||||
"DocumentTypePropertyTypeReferenceResponseModel": "#/components/schemas/DocumentTypePropertyTypeReferenceResponseModel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DocumentTypePropertyTypeResponseModel": {
|
||||
"required": [
|
||||
"alias",
|
||||
@@ -39577,6 +40193,45 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"MediaTypePropertyTypeReferenceResponseModel": {
|
||||
"required": [
|
||||
"$type",
|
||||
"id",
|
||||
"mediaType"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"$type": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"alias": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"mediaType": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/TrackedReferenceMediaTypeModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"discriminator": {
|
||||
"propertyName": "$type",
|
||||
"mapping": {
|
||||
"MediaTypePropertyTypeReferenceResponseModel": "#/components/schemas/MediaTypePropertyTypeReferenceResponseModel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MediaTypePropertyTypeResponseModel": {
|
||||
"required": [
|
||||
"alias",
|
||||
@@ -40084,6 +40739,41 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"MemberReferenceResponseModel": {
|
||||
"required": [
|
||||
"$type",
|
||||
"id",
|
||||
"memberType"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"$type": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"memberType": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/TrackedReferenceMemberTypeModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"discriminator": {
|
||||
"propertyName": "$type",
|
||||
"mapping": {
|
||||
"MemberReferenceResponseModel": "#/components/schemas/MemberReferenceResponseModel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MemberResponseModel": {
|
||||
"required": [
|
||||
"email",
|
||||
@@ -40320,6 +41010,45 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"MemberTypePropertyTypeReferenceResponseModel": {
|
||||
"required": [
|
||||
"$type",
|
||||
"id",
|
||||
"memberType"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"$type": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"alias": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"memberType": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/TrackedReferenceMemberTypeModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"discriminator": {
|
||||
"propertyName": "$type",
|
||||
"mapping": {
|
||||
"MemberTypePropertyTypeReferenceResponseModel": "#/components/schemas/MemberTypePropertyTypeReferenceResponseModel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MemberTypePropertyTypeResponseModel": {
|
||||
"required": [
|
||||
"alias",
|
||||
@@ -41492,8 +42221,20 @@
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentReferenceResponseModel"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentTypePropertyTypeReferenceResponseModel"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/MediaReferenceResponseModel"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/MediaTypePropertyTypeReferenceResponseModel"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/MemberReferenceResponseModel"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/MemberTypePropertyTypeReferenceResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -42867,6 +43608,23 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PublishWithDescendantsResultModel": {
|
||||
"required": [
|
||||
"isComplete",
|
||||
"taskId"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"taskId": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"isComplete": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PublishedDocumentResponseModel": {
|
||||
"required": [
|
||||
"documentType",
|
||||
@@ -44131,8 +44889,15 @@
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TrackedReferenceDocumentTypeModel": {
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
@@ -44149,8 +44914,40 @@
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TrackedReferenceMediaTypeModel": {
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"alias": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TrackedReferenceMemberTypeModel": {
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ public abstract class BackOfficeSecurityRequirementsOperationFilterBase : IOpera
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = ManagementApiConfiguration.ApiSecurityName
|
||||
}
|
||||
}, new string[] { }
|
||||
}, []
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -16,9 +16,41 @@ public interface IUserStartNodeEntitiesService
|
||||
/// <remarks>
|
||||
/// The returned entities may include entities that outside of the user start node scope, but are needed to
|
||||
/// for browsing to the actual user start nodes. These entities will be marked as "no access" entities.
|
||||
///
|
||||
/// This method does not support pagination, because it must load all entities explicitly in order to calculate
|
||||
/// the correct result, given that user start nodes can be descendants of root nodes. Consumers need to apply
|
||||
/// pagination to the result if applicable.
|
||||
/// </remarks>
|
||||
IEnumerable<UserAccessEntity> RootUserAccessEntities(UmbracoObjectTypes umbracoObjectType, int[] userStartNodeIds);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the applicable child entities for a given object type for users without root access.
|
||||
/// </summary>
|
||||
/// <param name="umbracoObjectType">The object type.</param>
|
||||
/// <param name="userStartNodePaths">The calculated start node paths for the user.</param>
|
||||
/// <param name="parentKey">The key of the parent.</param>
|
||||
/// <param name="skip">The number of applicable children to skip.</param>
|
||||
/// <param name="take">The number of applicable children to take.</param>
|
||||
/// <param name="ordering">The ordering to apply when fetching and paginating the children.</param>
|
||||
/// <param name="totalItems">The total number of applicable children available.</param>
|
||||
/// <returns>A list of child entities applicable for the user.</returns>
|
||||
/// <remarks>
|
||||
/// The returned entities may include entities that outside of the user start node scope, but are needed to
|
||||
/// for browsing to the actual user start nodes. These entities will be marked as "no access" entities.
|
||||
/// </remarks>
|
||||
IEnumerable<UserAccessEntity> ChildUserAccessEntities(
|
||||
UmbracoObjectTypes umbracoObjectType,
|
||||
string[] userStartNodePaths,
|
||||
Guid parentKey,
|
||||
int skip,
|
||||
int take,
|
||||
Ordering ordering,
|
||||
out long totalItems)
|
||||
{
|
||||
totalItems = 0;
|
||||
return [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the applicable child entities from a list of candidate child entities for users without root access.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using Umbraco.Cms.Core;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Api.Management.Models.Entities;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Persistence.Querying;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Services.Entities;
|
||||
@@ -10,8 +14,24 @@ namespace Umbraco.Cms.Api.Management.Services.Entities;
|
||||
public class UserStartNodeEntitiesService : IUserStartNodeEntitiesService
|
||||
{
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly ICoreScopeProvider _scopeProvider;
|
||||
private readonly IIdKeyMap _idKeyMap;
|
||||
|
||||
public UserStartNodeEntitiesService(IEntityService entityService) => _entityService = entityService;
|
||||
[Obsolete("Please use the non-obsolete constructor. Scheduled for removal in V17.")]
|
||||
public UserStartNodeEntitiesService(IEntityService entityService)
|
||||
: this(
|
||||
entityService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<ICoreScopeProvider>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IIdKeyMap>())
|
||||
{
|
||||
}
|
||||
|
||||
public UserStartNodeEntitiesService(IEntityService entityService, ICoreScopeProvider scopeProvider, IIdKeyMap idKeyMap)
|
||||
{
|
||||
_entityService = entityService;
|
||||
_scopeProvider = scopeProvider;
|
||||
_idKeyMap = idKeyMap;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<UserAccessEntity> RootUserAccessEntities(UmbracoObjectTypes umbracoObjectType, int[] userStartNodeIds)
|
||||
@@ -43,6 +63,54 @@ public class UserStartNodeEntitiesService : IUserStartNodeEntitiesService
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public IEnumerable<UserAccessEntity> ChildUserAccessEntities(UmbracoObjectTypes umbracoObjectType, string[] userStartNodePaths, Guid parentKey, int skip, int take, Ordering ordering, out long totalItems)
|
||||
{
|
||||
Attempt<int> parentIdAttempt = _idKeyMap.GetIdForKey(parentKey, umbracoObjectType);
|
||||
if (parentIdAttempt.Success is false)
|
||||
{
|
||||
totalItems = 0;
|
||||
return [];
|
||||
}
|
||||
|
||||
var parentId = parentIdAttempt.Result;
|
||||
IEntitySlim? parent = _entityService.Get(parentId);
|
||||
if (parent is null)
|
||||
{
|
||||
totalItems = 0;
|
||||
return [];
|
||||
}
|
||||
|
||||
IEntitySlim[] children;
|
||||
if (userStartNodePaths.Any(path => $"{parent.Path},".StartsWith($"{path},")))
|
||||
{
|
||||
// the requested parent is one of the user start nodes (or a descendant of one), all children are by definition allowed
|
||||
children = _entityService.GetPagedChildren(parentKey, umbracoObjectType, skip, take, out totalItems, ordering: ordering).ToArray();
|
||||
return ChildUserAccessEntities(children, userStartNodePaths);
|
||||
}
|
||||
|
||||
// if one or more of the user start nodes are descendants of the requested parent, find the "next child IDs" in those user start node paths
|
||||
// - e.g. given the user start node path "-1,2,3,4,5", if the requested parent ID is 3, the "next child ID" is 4.
|
||||
var userStartNodePathIds = userStartNodePaths.Select(path => path.Split(Constants.CharArrays.Comma).Select(int.Parse).ToArray()).ToArray();
|
||||
var allowedChildIds = userStartNodePathIds
|
||||
.Where(ids => ids.Contains(parentId))
|
||||
// given the previous checks, the parent ID can never be the last in the user start node path, so this is safe
|
||||
.Select(ids => ids[ids.IndexOf(parentId) + 1])
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
totalItems = allowedChildIds.Length;
|
||||
if (allowedChildIds.Length == 0)
|
||||
{
|
||||
// the requested parent is outside the scope of any user start nodes
|
||||
return [];
|
||||
}
|
||||
|
||||
// even though we know the IDs of the allowed child entities to fetch, we still use a Query to yield correctly sorted children
|
||||
IQuery<IUmbracoEntity> query = _scopeProvider.CreateQuery<IUmbracoEntity>().Where(x => allowedChildIds.Contains(x.Id));
|
||||
children = _entityService.GetPagedChildren(parentKey, umbracoObjectType, skip, take, out totalItems, query, ordering).ToArray();
|
||||
return ChildUserAccessEntities(children, userStartNodePaths);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<UserAccessEntity> ChildUserAccessEntities(IEnumerable<IEntitySlim> candidateChildren, string[] userStartNodePaths)
|
||||
// child entities for users without root access should include:
|
||||
|
||||
+1
-2
@@ -1,4 +1,4 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Dictionary;
|
||||
|
||||
@@ -7,6 +7,5 @@ public class DictionaryItemTranslationModel
|
||||
[Required]
|
||||
public string IsoCode { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public string Translation { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
+2
@@ -11,5 +11,7 @@ public class DocumentCollectionResponseModel : ContentCollectionResponseModelBas
|
||||
|
||||
public bool IsProtected { get; set; }
|
||||
|
||||
public IEnumerable<ReferenceByIdModel> Ancestors { get; set; } = [];
|
||||
|
||||
public string? Updater { get; set; }
|
||||
}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
|
||||
public class PublishWithDescendantsResultModel
|
||||
{
|
||||
public Guid TaskId { get; set; }
|
||||
|
||||
public bool IsComplete { get; set; }
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
|
||||
public abstract class ContentTypePropertyTypeReferenceResponseModel : ReferenceResponseModel
|
||||
{
|
||||
public string? Alias { get; set; }
|
||||
}
|
||||
+2
-6
@@ -1,11 +1,7 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
|
||||
public class DefaultReferenceResponseModel : IReferenceResponseModel
|
||||
public class DefaultReferenceResponseModel : ReferenceResponseModel
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Type { get; set; }
|
||||
|
||||
public string? Icon { get; set; }
|
||||
|
||||
+6
-6
@@ -1,12 +1,12 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
|
||||
public class DocumentReferenceResponseModel : IReferenceResponseModel
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
|
||||
public class DocumentReferenceResponseModel : ReferenceResponseModel
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
public bool? Published { get; set; }
|
||||
|
||||
public TrackedReferenceDocumentType DocumentType { get; set; } = new();
|
||||
|
||||
public IEnumerable<DocumentVariantItemResponseModel> Variants { get; set; } = Enumerable.Empty<DocumentVariantItemResponseModel>();
|
||||
}
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
|
||||
public class DocumentTypePropertyTypeReferenceResponseModel : ContentTypePropertyTypeReferenceResponseModel
|
||||
{
|
||||
public TrackedReferenceDocumentType DocumentType { get; set; } = new();
|
||||
}
|
||||
+2
-6
@@ -1,10 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
|
||||
public class MediaReferenceResponseModel : IReferenceResponseModel
|
||||
public class MediaReferenceResponseModel : ReferenceResponseModel
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
public TrackedReferenceMediaType MediaType { get; set; } = new();
|
||||
}
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
|
||||
public class MediaTypePropertyTypeReferenceResponseModel : ContentTypePropertyTypeReferenceResponseModel
|
||||
{
|
||||
public TrackedReferenceMediaType MediaType { get; set; } = new();
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
|
||||
public class MemberReferenceResponseModel : ReferenceResponseModel
|
||||
{
|
||||
public TrackedReferenceMemberType MemberType { get; set; } = new();
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
|
||||
public class MemberTypePropertyTypeReferenceResponseModel : ContentTypePropertyTypeReferenceResponseModel
|
||||
{
|
||||
public TrackedReferenceMemberType MemberType { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
|
||||
public abstract class ReferenceResponseModel : IReferenceResponseModel
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
+3
-1
@@ -1,7 +1,9 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
|
||||
public abstract class TrackedReferenceContentType
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string? Icon { get; set; }
|
||||
|
||||
public string? Alias { get; set; }
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences;
|
||||
|
||||
public class TrackedReferenceMemberType : TrackedReferenceContentType
|
||||
{
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DocumentType;
|
||||
|
||||
@@ -8,6 +7,8 @@ public class DocumentTreeItemResponseModel : ContentTreeItemResponseModel
|
||||
{
|
||||
public bool IsProtected { get; set; }
|
||||
|
||||
public IEnumerable<ReferenceByIdModel> Ancestors { get; set; } = [];
|
||||
|
||||
public DocumentTypeReferenceResponseModel DocumentType { get; set; } = new();
|
||||
|
||||
public IEnumerable<DocumentVariantItemResponseModel> Variants { get; set; } = Enumerable.Empty<DocumentVariantItemResponseModel>();
|
||||
|
||||
@@ -26,23 +26,36 @@
|
||||
<ProjectReference Include="..\Umbraco.Web.Website\Umbraco.Web.Website.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Restore and build backoffice project -->
|
||||
<!-- General ignored files -->
|
||||
<ItemGroup>
|
||||
<Content Remove="wwwroot\umbraco\assets\README.md" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
<!-- BEGIN: Restore and build backoffice project -->
|
||||
<PropertyGroup>
|
||||
<BackofficeProjectDirectory Condition="'$(BackofficeProjectDirectory)' == ''">..\Umbraco.Web.UI.Client\</BackofficeProjectDirectory>
|
||||
<BackofficeAssetsPath>wwwroot\umbraco\backoffice</BackofficeAssetsPath>
|
||||
<BackofficeAssetsPath>$(ProjectDir)wwwroot\umbraco\backoffice</BackofficeAssetsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<BackofficeAssetsInputs Include="$(BackofficeProjectDirectory)package.json;$(BackofficeProjectDirectory)package-lock.json;$(BackofficeProjectDirectory)src\**" Exclude="$(DefaultItemExcludes)" />
|
||||
<Content Remove="$(BackofficeAssetsPath)\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="RestoreBackoffice" Inputs="$(BackofficeProjectDirectory)package-lock.json" Outputs="$(BackofficeProjectDirectory)node_modules\.package-lock.json">
|
||||
<Message Importance="high" Text="Restoring Backoffice NPM packages..." />
|
||||
<Exec Command="npm ci --no-fund --no-audit --prefer-offline" WorkingDirectory="$(BackofficeProjectDirectory)" />
|
||||
<Target Name="BuildStaticAssetsPreconditions" BeforeTargets="AssignTargetPaths">
|
||||
<Message Text="Skip BuildBackoffice target because UmbracoBuild is '$(UmbracoBuild)' (this is not Visual Studio)" Importance="high" Condition="'$(UmbracoBuild)' != ''" />
|
||||
<Message Text="Skip BuildBackoffice target because '$(BackofficeAssetsPath)' already exists" Importance="high" Condition="Exists('$(BackofficeAssetsPath)')" />
|
||||
<Message Text="Call BuildBackoffice target because UmbracoBuild is empty (this is Visual Studio) and '$(BackofficeAssetsPath)' doesn't exist" Importance="high" Condition="'$(UmbracoBuild)' == '' and !Exists('$(BackofficeAssetsPath)')" />
|
||||
<CallTarget Targets="BuildBackoffice" Condition="'$(UmbracoBuild)' == '' and !Exists('$(BackofficeAssetsPath)')" />
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildBackoffice" DependsOnTargets="RestoreBackoffice" BeforeTargets="AssignTargetPaths" Inputs="@(BackofficeAssetsInputs)" Outputs="$(IntermediateOutputPath)backoffice.complete.txt">
|
||||
<Target Name="RestoreBackoffice" Inputs="$(BackofficeProjectDirectory)package-lock.json" Outputs="$(BackofficeProjectDirectory)node_modules\.package-lock.json">
|
||||
<Message Importance="high" Text="Restoring Backoffice NPM packages..." />
|
||||
<Exec Command="npm i --no-fund --no-audit" WorkingDirectory="$(BackofficeProjectDirectory)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildBackoffice" DependsOnTargets="RestoreBackoffice">
|
||||
<Message Importance="high" Text="Executing Backoffice NPM build script..." />
|
||||
<Exec Command="npm run build:for:cms" WorkingDirectory="$(BackofficeProjectDirectory)" />
|
||||
<ItemGroup>
|
||||
@@ -61,27 +74,46 @@
|
||||
</DefineStaticWebAssets>
|
||||
</Target>
|
||||
|
||||
<!-- Restore and build login project -->
|
||||
<Target Name="CleanStaticAssetsPreconditions" AfterTargets="Clean" Condition="'$(UmbracoBuild)' == ''">
|
||||
<Message Text="Skip CleanBackoffice target because '$(BackofficeAssetsPath)' doesn't exist" Importance="high" Condition="!Exists('$(BackofficeAssetsPath)')" />
|
||||
<Message Text="Skip CleanBackoffice target because preserve.backoffice marker file exists" Importance="high" Condition="Exists('$(BackofficeAssetsPath)') and Exists('$(SolutionDir)preserve.backoffice')" />
|
||||
<Message Text="Call CleanBackoffice target because '$(BackofficeAssetsPath)' exists and preserve.backoffice marker file doesn't exist" Importance="high" Condition="Exists('$(BackofficeAssetsPath)') and !Exists('$(SolutionDir)preserve.backoffice')" />
|
||||
<CallTarget Targets="CleanBackoffice" Condition="Exists('$(BackofficeAssetsPath)') and !Exists('$(SolutionDir)preserve.backoffice')" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanBackoffice">
|
||||
<ItemGroup>
|
||||
<BackofficeDirectories Include="$(BackofficeAssetsPath)" />
|
||||
</ItemGroup>
|
||||
<RemoveDir Directories="@(BackofficeDirectories)" />
|
||||
</Target>
|
||||
<!-- END: Restore and build backoffice project -->
|
||||
|
||||
|
||||
|
||||
<!-- BEGIN: Restore and build login project -->
|
||||
<PropertyGroup>
|
||||
<LoginProjectDirectory Condition="'$(LoginProjectDirectory)' == ''">..\Umbraco.Web.UI.Login\</LoginProjectDirectory>
|
||||
<LoginAssetsPath>wwwroot\umbraco\login</LoginAssetsPath>
|
||||
<LoginAssetsPath>$(ProjectDir)wwwroot\umbraco\login</LoginAssetsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<LoginAssetsInputs Include="$(LoginProjectDirectory)**" Exclude="$(DefaultItemExcludes)" />
|
||||
<Content Remove="$(LoginAssetsPath)\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="wwwroot\umbraco\assets\README.md" />
|
||||
</ItemGroup>
|
||||
<Target Name="BuildLoginStaticAssetsPreconditions" BeforeTargets="AssignTargetPaths">
|
||||
<Message Text="Skip BuildLogin target because UmbracoBuild is '$(UmbracoBuild)' (this is not Visual Studio)" Importance="high" Condition="'$(UmbracoBuild)' != ''" />
|
||||
<Message Text="Skip BuildLogin target because '$(LoginAssetsPath)' already exists" Importance="high" Condition="Exists('$(LoginAssetsPath)')" />
|
||||
<Message Text="Call BuildLogin target because UmbracoBuild is empty (this is Visual Studio) and '$(LoginAssetsPath)' doesn't exist" Importance="high" Condition="'$(UmbracoBuild)' == '' and !Exists('$(LoginAssetsPath)')" />
|
||||
<CallTarget Targets="BuildLogin" Condition="'$(UmbracoBuild)' == '' and !Exists('$(LoginAssetsPath)')" />
|
||||
</Target>
|
||||
|
||||
<Target Name="RestoreLogin" Inputs="$(LoginProjectDirectory)package-lock.json" Outputs="$(LoginProjectDirectory)node_modules/.package-lock.json">
|
||||
<Message Importance="high" Text="Restoring Login NPM packages..." />
|
||||
<Exec Command="npm ci --no-fund --no-audit --prefer-offline" WorkingDirectory="$(LoginProjectDirectory)" />
|
||||
<Exec Command="npm i --no-fund --no-audit" WorkingDirectory="$(LoginProjectDirectory)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildLogin" DependsOnTargets="RestoreLogin" BeforeTargets="AssignTargetPaths" Inputs="@(LoginAssetsInputs)" Outputs="$(IntermediateOutputPath)login.complete.txt">
|
||||
<Target Name="BuildLogin" DependsOnTargets="RestoreLogin">
|
||||
<Message Importance="high" Text="Executing Login NPM build script..." />
|
||||
<Exec Command="npm run build" WorkingDirectory="$(LoginProjectDirectory)" />
|
||||
<ItemGroup>
|
||||
@@ -99,4 +131,19 @@
|
||||
<Output TaskParameter="Assets" ItemName="StaticWebAsset" />
|
||||
</DefineStaticWebAssets>
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanLoginStaticAssetsPreconditions" AfterTargets="Clean" Condition="'$(UmbracoBuild)' == ''">
|
||||
<Message Text="Skip CleanLogin target because '$(LoginAssetsPath)' doesn't exist" Importance="high" Condition="!Exists('$(LoginAssetsPath)')" />
|
||||
<Message Text="Skip CleanLogin target because preserve.login marker file exists" Importance="high" Condition="Exists('$(LoginAssetsPath)') and Exists('$(SolutionDir)preserve.login')" />
|
||||
<Message Text="Call CleanLogin target because '$(LoginAssetsPath)' exists and preserve.login marker file doesn't exist" Importance="high" Condition="Exists('$(LoginAssetsPath)') and !Exists('$(SolutionDir)preserve.login')" />
|
||||
<CallTarget Targets="CleanLogin" Condition="Exists('$(LoginAssetsPath)') and !Exists('$(SolutionDir)preserve.login')" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanLogin">
|
||||
<ItemGroup>
|
||||
<LoginDirectories Include="$(LoginAssetsPath)" />
|
||||
</ItemGroup>
|
||||
<RemoveDir Directories="@(LoginDirectories)" />
|
||||
</Target>
|
||||
<!-- END: Restore and build login project -->
|
||||
</Project>
|
||||
|
||||
@@ -37,6 +37,7 @@ public interface IAction : IDiscoverable
|
||||
/// <summary>
|
||||
/// Gets the icon to display for this action
|
||||
/// </summary>
|
||||
[Obsolete("No longer used. Scheduled for removal in V17.")]
|
||||
string Icon { get; }
|
||||
|
||||
/// <summary>
|
||||
@@ -51,5 +52,6 @@ public interface IAction : IDiscoverable
|
||||
/// <remarks>
|
||||
/// Used in the UI when assigning permissions
|
||||
/// </remarks>
|
||||
[Obsolete("No longer used. Scheduled for removal in V17.")]
|
||||
string? Category { get; }
|
||||
}
|
||||
|
||||
@@ -22,4 +22,6 @@ public static class CacheKeys
|
||||
|
||||
public const string PreviewPropertyCacheKeyPrefix = "Cache.Property.CacheValues[D:";
|
||||
public const string PropertyCacheKeyPrefix = "Cache.Property.CacheValues[P:";
|
||||
|
||||
public const string MemberUserNameCachePrefix = "uRepo_userNameKey+";
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
@@ -21,9 +23,11 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase<ContentCac
|
||||
private readonly IDocumentNavigationManagementService _documentNavigationManagementService;
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IDocumentCacheService _documentCacheService;
|
||||
private readonly ICacheManager _cacheManager;
|
||||
private readonly IPublishStatusManagementService _publishStatusManagementService;
|
||||
private readonly IIdKeyMap _idKeyMap;
|
||||
|
||||
[Obsolete("Use the constructor with ICacheManager instead, scheduled for removal in V17.")]
|
||||
public ContentCacheRefresher(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer serializer,
|
||||
@@ -38,6 +42,39 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase<ContentCac
|
||||
IContentService contentService,
|
||||
IPublishStatusManagementService publishStatusManagementService,
|
||||
IDocumentCacheService documentCacheService)
|
||||
: this(
|
||||
appCaches,
|
||||
serializer,
|
||||
idKeyMap,
|
||||
domainService,
|
||||
eventAggregator,
|
||||
factory,
|
||||
documentUrlService,
|
||||
domainCacheService,
|
||||
documentNavigationQueryService,
|
||||
documentNavigationManagementService,
|
||||
contentService,
|
||||
publishStatusManagementService,
|
||||
documentCacheService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<ICacheManager>())
|
||||
{
|
||||
}
|
||||
|
||||
public ContentCacheRefresher(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer serializer,
|
||||
IIdKeyMap idKeyMap,
|
||||
IDomainService domainService,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheRefresherNotificationFactory factory,
|
||||
IDocumentUrlService documentUrlService,
|
||||
IDomainCacheService domainCacheService,
|
||||
IDocumentNavigationQueryService documentNavigationQueryService,
|
||||
IDocumentNavigationManagementService documentNavigationManagementService,
|
||||
IContentService contentService,
|
||||
IPublishStatusManagementService publishStatusManagementService,
|
||||
IDocumentCacheService documentCacheService,
|
||||
ICacheManager cacheManager)
|
||||
: base(appCaches, serializer, eventAggregator, factory)
|
||||
{
|
||||
_idKeyMap = idKeyMap;
|
||||
@@ -49,6 +86,11 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase<ContentCac
|
||||
_contentService = contentService;
|
||||
_documentCacheService = documentCacheService;
|
||||
_publishStatusManagementService = publishStatusManagementService;
|
||||
|
||||
// TODO: Ideally we should inject IElementsCache
|
||||
// this interface is in infrastructure, and changing this is very breaking
|
||||
// so as long as we have the cache manager, which casts the IElementsCache to a simple AppCache we might as well use that.
|
||||
_cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
#region Indirect
|
||||
@@ -83,6 +125,13 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase<ContentCac
|
||||
AppCaches.RuntimeCache.ClearOfType<PublicAccessEntry>();
|
||||
AppCaches.RuntimeCache.ClearByKey(CacheKeys.ContentRecycleBinCacheKey);
|
||||
|
||||
// Ideally, we'd like to not have to clear the entire cache here. However, this was the existing behavior in NuCache.
|
||||
// The reason for this is that we have no way to know which elements are affected by the changes or what their keys are.
|
||||
// This is because currently published elements live exclusively in a JSON blob in the umbracoPropertyData table.
|
||||
// This means that the only way to resolve these keys is to actually parse this data with a specific value converter, and for all cultures, which is not possible.
|
||||
// If published elements become their own entities with relations, instead of just property data, we can revisit this.
|
||||
_cacheManager.ElementsCache.Clear();
|
||||
|
||||
var idsRemoved = new HashSet<int>();
|
||||
IAppPolicyCache isolatedCache = AppCaches.IsolatedCaches.GetOrCreate<IContent>();
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
@@ -18,7 +20,9 @@ public sealed class MediaCacheRefresher : PayloadCacheRefresherBase<MediaCacheRe
|
||||
private readonly IMediaNavigationManagementService _mediaNavigationManagementService;
|
||||
private readonly IMediaService _mediaService;
|
||||
private readonly IMediaCacheService _mediaCacheService;
|
||||
private readonly ICacheManager _cacheManager;
|
||||
|
||||
[Obsolete("Use the constructor with ICacheManager instead, scheduled for removal in V17.")]
|
||||
public MediaCacheRefresher(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer serializer,
|
||||
@@ -29,6 +33,31 @@ public sealed class MediaCacheRefresher : PayloadCacheRefresherBase<MediaCacheRe
|
||||
IMediaNavigationManagementService mediaNavigationManagementService,
|
||||
IMediaService mediaService,
|
||||
IMediaCacheService mediaCacheService)
|
||||
: this(
|
||||
appCaches,
|
||||
serializer,
|
||||
idKeyMap,
|
||||
eventAggregator,
|
||||
factory,
|
||||
mediaNavigationQueryService,
|
||||
mediaNavigationManagementService,
|
||||
mediaService,
|
||||
mediaCacheService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<ICacheManager>())
|
||||
{
|
||||
}
|
||||
|
||||
public MediaCacheRefresher(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer serializer,
|
||||
IIdKeyMap idKeyMap,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheRefresherNotificationFactory factory,
|
||||
IMediaNavigationQueryService mediaNavigationQueryService,
|
||||
IMediaNavigationManagementService mediaNavigationManagementService,
|
||||
IMediaService mediaService,
|
||||
IMediaCacheService mediaCacheService,
|
||||
ICacheManager cacheManager)
|
||||
: base(appCaches, serializer, eventAggregator, factory)
|
||||
{
|
||||
_idKeyMap = idKeyMap;
|
||||
@@ -36,6 +65,9 @@ public sealed class MediaCacheRefresher : PayloadCacheRefresherBase<MediaCacheRe
|
||||
_mediaNavigationManagementService = mediaNavigationManagementService;
|
||||
_mediaService = mediaService;
|
||||
_mediaCacheService = mediaCacheService;
|
||||
|
||||
// TODO: Use IElementsCache instead of ICacheManager, see ContentCacheRefresher for more information.
|
||||
_cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
#region Indirect
|
||||
@@ -87,6 +119,13 @@ public sealed class MediaCacheRefresher : PayloadCacheRefresherBase<MediaCacheRe
|
||||
AppCaches.RuntimeCache.ClearByKey(CacheKeys.MediaRecycleBinCacheKey);
|
||||
Attempt<IAppPolicyCache?> mediaCache = AppCaches.IsolatedCaches.Get<IMedia>();
|
||||
|
||||
// Ideally, we'd like to not have to clear the entire cache here. However, this was the existing behavior in NuCache.
|
||||
// The reason for this is that we have no way to know which elements are affected by the changes or what their keys are.
|
||||
// This is because currently published elements live exclusively in a JSON blob in the umbracoPropertyData table.
|
||||
// This means that the only way to resolve these keys is to actually parse this data with a specific value converter, and for all cultures, which is not possible.
|
||||
// If published elements become their own entities with relations, instead of just property data, we can revisit this.
|
||||
_cacheManager.ElementsCache.Clear();
|
||||
|
||||
foreach (JsonPayload payload in payloads)
|
||||
{
|
||||
if (payload.ChangeTypes == TreeChangeTypes.Remove)
|
||||
|
||||
@@ -71,11 +71,22 @@ public sealed class MemberCacheRefresher : PayloadCacheRefresherBase<MemberCache
|
||||
foreach (JsonPayload p in payloads)
|
||||
{
|
||||
_idKeyMap.ClearCache(p.Id);
|
||||
if (memberCache.Success)
|
||||
if (memberCache.Success is false)
|
||||
{
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, int>(p.Id));
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(p.Username));
|
||||
continue;
|
||||
}
|
||||
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, int>(p.Id));
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(p.Username));
|
||||
|
||||
// This specific cache key was introduced to fix an issue where the member username could not be the same as the member id, because the cache keys collided.
|
||||
// This is done in a bit of a hacky way, because the cache key is created internally in the repository, but we need to clear it here.
|
||||
// Ideally, we want to use a shared way of generating the key between this and the repository.
|
||||
// Additionally, the RepositoryCacheKeys actually caches the string to avoid re-allocating memory; we would like to also use this in the repository
|
||||
// See:
|
||||
// https://github.com/umbraco/Umbraco-CMS/pull/17350
|
||||
// https://github.com/umbraco/Umbraco-CMS/pull/17815
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(CacheKeys.MemberUserNameCachePrefix + p.Username));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,9 +160,9 @@ public class ObservableDictionary<TKey, TValue> : ObservableCollection<TValue>,
|
||||
|
||||
if (index != Count)
|
||||
{
|
||||
foreach (TKey k in Indecies.Keys.Where(k => Indecies[k] >= index).ToList())
|
||||
foreach (KeyValuePair<TKey, int> largerOrEqualToIndex in Indecies.Where(kvp => kvp.Value >= index))
|
||||
{
|
||||
Indecies[k]++;
|
||||
Indecies[largerOrEqualToIndex.Key] = largerOrEqualToIndex.Value + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,9 +185,9 @@ public class ObservableDictionary<TKey, TValue> : ObservableCollection<TValue>,
|
||||
|
||||
Indecies.Remove(key);
|
||||
|
||||
foreach (TKey k in Indecies.Keys.Where(k => Indecies[k] > index).ToList())
|
||||
foreach (KeyValuePair<TKey, int> largerThanIndex in Indecies.Where(kvp => kvp.Value > index))
|
||||
{
|
||||
Indecies[k]--;
|
||||
Indecies[largerThanIndex.Key] = largerThanIndex.Value - 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -233,10 +233,7 @@ public class TypeFinder : ITypeFinder
|
||||
excludeFromResults = new HashSet<Assembly>();
|
||||
}
|
||||
|
||||
if (exclusionFilter == null)
|
||||
{
|
||||
exclusionFilter = new string[] { };
|
||||
}
|
||||
exclusionFilter ??= [];
|
||||
|
||||
return GetAllAssemblies()
|
||||
.Where(x => excludeFromResults.Contains(x) == false
|
||||
|
||||
@@ -5,14 +5,17 @@ namespace Umbraco.Extensions;
|
||||
public static class ContentSettingsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines if file extension is allowed for upload based on (optional) white list and black list
|
||||
/// held in settings.
|
||||
/// Allow upload if extension is whitelisted OR if there is no whitelist and extension is NOT blacklisted.
|
||||
/// Determines if file extension is allowed for upload based on (optional) allow list and deny list held in settings.
|
||||
/// Disallowed file extensions are only considered if there are no allowed file extensions.
|
||||
/// </summary>
|
||||
public static bool IsFileAllowedForUpload(this ContentSettings contentSettings, string extension) =>
|
||||
contentSettings.AllowedUploadedFileExtensions.Any(x => x.InvariantEquals(extension)) ||
|
||||
(contentSettings.AllowedUploadedFileExtensions.Any() == false &&
|
||||
contentSettings.DisallowedUploadedFileExtensions.Any(x => x.InvariantEquals(extension)) == false);
|
||||
/// <param name="contentSettings">The content settings.</param>
|
||||
/// <param name="extension">The file extension.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the file extension is allowed for upload; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool IsFileAllowedForUpload(this ContentSettings contentSettings, string extension)
|
||||
=> contentSettings.AllowedUploadedFileExtensions.Any(x => x.InvariantEquals(extension.Trim())) ||
|
||||
(contentSettings.AllowedUploadedFileExtensions.Any() == false && contentSettings.DisallowedUploadedFileExtensions.Any(x => x.InvariantEquals(extension.Trim())) == false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the auto-fill configuration for a specified property alias.
|
||||
|
||||
@@ -233,17 +233,27 @@ public static partial class Constants
|
||||
public const string RelatedMediaName = "Related Media";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for default relation type "Related Media"
|
||||
/// Alias for default relation type "Related Media".
|
||||
/// </summary>
|
||||
public const string RelatedMediaAlias = "umbMedia";
|
||||
|
||||
/// <summary>
|
||||
/// Name for default relation type "Related Member".
|
||||
/// </summary>
|
||||
public const string RelatedMemberName = "Related Member";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for default relation type "Related Member".
|
||||
/// </summary>
|
||||
public const string RelatedMemberAlias = "umbMember";
|
||||
|
||||
/// <summary>
|
||||
/// Name for default relation type "Related Document".
|
||||
/// </summary>
|
||||
public const string RelatedDocumentName = "Related Document";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for default relation type "Related Document"
|
||||
/// Alias for default relation type "Related Document".
|
||||
/// </summary>
|
||||
public const string RelatedDocumentAlias = "umbDocument";
|
||||
|
||||
@@ -284,7 +294,7 @@ public static partial class Constants
|
||||
/// Developers should not manually use these relation types since they will all be cleared whenever an entity
|
||||
/// (content, media or member) is saved since they are auto-populated based on property values.
|
||||
/// </remarks>
|
||||
public static string[] AutomaticRelationTypes { get; } = { RelatedMediaAlias, RelatedDocumentAlias };
|
||||
public static string[] AutomaticRelationTypes { get; } = { RelatedMediaAlias, RelatedMemberAlias, RelatedDocumentAlias };
|
||||
|
||||
// TODO: return a list of built in types so we can use that to prevent deletion in the UI
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace Umbraco.Cms.Core;
|
||||
|
||||
public static partial class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines reference types.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Reference types are used to identify the type of entity that is being referenced when exposing references
|
||||
/// between Umbraco entities.
|
||||
/// These are used in the management API and backoffice to indicate and warn editors when working with an entity,
|
||||
/// as to what other entities depend on it.
|
||||
/// These consist of references managed by Umbraco relations (e.g. document, media and member).
|
||||
/// But also references that come from schema (e.g. data type usage on content types).
|
||||
/// </remarks>
|
||||
public static class ReferenceType
|
||||
{
|
||||
public const string Document = UdiEntityType.Document;
|
||||
public const string Media = UdiEntityType.Media;
|
||||
public const string Member = UdiEntityType.Member;
|
||||
public const string DocumentTypePropertyType = "document-type-property-type";
|
||||
public const string MediaTypePropertyType = "media-type-property-type";
|
||||
public const string MemberTypePropertyType = "member-type-property-type";
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Navigation;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -19,6 +20,7 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder
|
||||
private readonly IPublishedContentCache _contentCache;
|
||||
private readonly IDocumentNavigationQueryService _navigationQueryService;
|
||||
private readonly IPublishStatusQueryService _publishStatusQueryService;
|
||||
private readonly IDocumentUrlService _documentUrlService;
|
||||
private RequestHandlerSettings _requestSettings;
|
||||
|
||||
public ApiContentRouteBuilder(
|
||||
@@ -29,7 +31,8 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder
|
||||
IOptionsMonitor<RequestHandlerSettings> requestSettings,
|
||||
IPublishedContentCache contentCache,
|
||||
IDocumentNavigationQueryService navigationQueryService,
|
||||
IPublishStatusQueryService publishStatusQueryService)
|
||||
IPublishStatusQueryService publishStatusQueryService,
|
||||
IDocumentUrlService documentUrlService)
|
||||
{
|
||||
_apiContentPathProvider = apiContentPathProvider;
|
||||
_variationContextAccessor = variationContextAccessor;
|
||||
@@ -37,11 +40,35 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder
|
||||
_contentCache = contentCache;
|
||||
_navigationQueryService = navigationQueryService;
|
||||
_publishStatusQueryService = publishStatusQueryService;
|
||||
_documentUrlService = documentUrlService;
|
||||
_globalSettings = globalSettings.Value;
|
||||
_requestSettings = requestSettings.CurrentValue;
|
||||
requestSettings.OnChange(settings => _requestSettings = settings);
|
||||
}
|
||||
|
||||
[Obsolete("Use the non-obsolete constructor, scheduled for removal in v17")]
|
||||
public ApiContentRouteBuilder(
|
||||
IApiContentPathProvider apiContentPathProvider,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
IOptionsMonitor<RequestHandlerSettings> requestSettings,
|
||||
IPublishedContentCache contentCache,
|
||||
IDocumentNavigationQueryService navigationQueryService,
|
||||
IPublishStatusQueryService publishStatusQueryService)
|
||||
: this(
|
||||
apiContentPathProvider,
|
||||
globalSettings,
|
||||
variationContextAccessor,
|
||||
requestPreviewService,
|
||||
requestSettings,
|
||||
contentCache,
|
||||
navigationQueryService,
|
||||
publishStatusQueryService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentUrlService>())
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Use the non-obsolete constructor, scheduled for removal in v17")]
|
||||
public ApiContentRouteBuilder(
|
||||
IApiContentPathProvider apiContentPathProvider,
|
||||
@@ -59,7 +86,8 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder
|
||||
requestSettings,
|
||||
contentCache,
|
||||
navigationQueryService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishStatusQueryService>())
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishStatusQueryService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentUrlService>())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -113,7 +141,7 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder
|
||||
// we can perform fallback to the content route.
|
||||
if (IsInvalidContentPath(contentPath))
|
||||
{
|
||||
contentPath = _contentCache.GetRouteById(content.Id, culture) ?? contentPath;
|
||||
contentPath = _documentUrlService.GetLegacyRouteFormat(content.Key, culture ?? _variationContextAccessor.VariationContext?.Culture, isPreview);
|
||||
}
|
||||
|
||||
// if the content path has still not been resolved as a valid path, the content is un-routable in this culture
|
||||
@@ -125,7 +153,9 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder
|
||||
: null;
|
||||
}
|
||||
|
||||
return contentPath;
|
||||
return _requestSettings.AddTrailingSlash
|
||||
? contentPath?.EnsureEndsWith('/')
|
||||
: contentPath?.TrimEnd('/');
|
||||
}
|
||||
|
||||
private string ContentPreviewPath(IPublishedContent content) => $"{Constants.DeliveryApi.Routing.PreviewContentPathPrefix}{content.Key:D}{(_requestSettings.AddTrailingSlash ? "/" : string.Empty)}";
|
||||
|
||||
@@ -86,10 +86,35 @@ public sealed class ApiPublishedContentCache : IApiPublishedContentCache
|
||||
_variationContextAccessor.VariationContext?.Culture,
|
||||
_requestPreviewService.IsPreview());
|
||||
|
||||
// in multi-root settings, we've historically resolved all but the first root by their ID + URL segment,
|
||||
// e.g. "1234/second-root-url-segment". in V15+, IDocumentUrlService won't resolve this anymore; it will
|
||||
// however resolve "1234/" correctly, so to remain backwards compatible, we need to perform this extra step.
|
||||
var verifyUrlSegment = false;
|
||||
if (documentKey is null && route.TrimEnd('/').CountOccurrences("/") is 1)
|
||||
{
|
||||
documentKey = _apiDocumentUrlService.GetDocumentKeyByRoute(
|
||||
route[..(route.IndexOf('/') + 1)],
|
||||
_variationContextAccessor.VariationContext?.Culture,
|
||||
_requestPreviewService.IsPreview());
|
||||
verifyUrlSegment = true;
|
||||
}
|
||||
|
||||
IPublishedContent? content = documentKey.HasValue
|
||||
? _publishedContentCache.GetById(isPreviewMode, documentKey.Value)
|
||||
: null;
|
||||
|
||||
// the additional look-up above can result in false positives; if attempting to request a non-existing child to
|
||||
// the currently contextualized request root (either by start item or by domain), the root content key might
|
||||
// get resolved. to counter for this, we compare the requested URL segment with the resolved content URL segment.
|
||||
if (content is not null && verifyUrlSegment)
|
||||
{
|
||||
var expectedUrlSegment = route[(route.IndexOf('/') + 1)..];
|
||||
if (content.UrlSegment != expectedUrlSegment)
|
||||
{
|
||||
content = null;
|
||||
}
|
||||
}
|
||||
|
||||
return ContentOrNullIfDisallowed(content);
|
||||
}
|
||||
|
||||
|
||||
@@ -422,6 +422,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
// Two factor providers
|
||||
Services.AddUnique<ITwoFactorLoginService, TwoFactorLoginService>();
|
||||
Services.AddUnique<IUserTwoFactorLoginService, UserTwoFactorLoginService>();
|
||||
Services.AddUnique<IMemberTwoFactorLoginService, MemberTwoFactorLoginService>();
|
||||
|
||||
// Add Query services
|
||||
Services.AddUnique<IDocumentRecycleBinQueryService, DocumentRecycleBinQueryService>();
|
||||
|
||||
@@ -377,7 +377,7 @@
|
||||
<key alias="invalidEmpty">Value cannot be empty</key>
|
||||
<key alias="invalidPattern">Value is invalid, it does not match the correct pattern</key>
|
||||
<key alias="entriesShort"><![CDATA[Minimum %0% entries, requires <strong>%1%</strong> more.]]></key>
|
||||
<key alias="entriesExceed"><![CDATA[Maximum %0% entries, <strong>%1%</strong> too many.]]></key>
|
||||
<key alias="entriesExceed"><![CDATA[Maximum %0% entries, you have entered <strong>%1%</strong> too many.]]></key>
|
||||
<key alias="stringLengthExceeded">The string length exceeds the maximum length of %0% characters, %1% too many.</key>
|
||||
<key alias="entriesAreasMismatch">The content amount requirements are not met for one or more areas.</key>
|
||||
<key alias="invalidMemberGroupName">Invalid member group name</key>
|
||||
|
||||
@@ -394,7 +394,7 @@
|
||||
<key alias="unexpectedRange">The value %0% is not expected to contain a range</key>
|
||||
<key alias="invalidRange">The value %0% is not expected to have a to value less than the from value</key>
|
||||
<key alias="entriesShort"><![CDATA[Minimum %0% entries, requires <strong>%1%</strong> more.]]></key>
|
||||
<key alias="entriesExceed"><![CDATA[Maximum %0% entries, <strong>%1%</strong> too many.]]></key>
|
||||
<key alias="entriesExceed"><![CDATA[Maximum %0% entries, you have entered <strong>%1%</strong> too many.]]></key>
|
||||
<key alias="stringLengthExceeded">The string length exceeds the maximum length of %0% characters, %1% too many.</key>
|
||||
<key alias="entriesAreasMismatch">The content amount requirements are not met for one or more areas.</key>
|
||||
<key alias="invalidMediaType">The chosen media type is invalid.</key>
|
||||
|
||||
@@ -35,7 +35,7 @@ public class InvalidCompositionException : Exception
|
||||
/// <param name="addedCompositionAlias">The added composition alias.</param>
|
||||
/// <param name="propertyTypeAliases">The property type aliases.</param>
|
||||
public InvalidCompositionException(string contentTypeAlias, string? addedCompositionAlias, string[] propertyTypeAliases)
|
||||
: this(contentTypeAlias, addedCompositionAlias, propertyTypeAliases, new string[0])
|
||||
: this(contentTypeAlias, addedCompositionAlias, propertyTypeAliases, [])
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -245,13 +245,13 @@ public static class ContentExtensions
|
||||
}
|
||||
|
||||
IEnumerable<ContentSchedule> expires = contentSchedule.GetSchedule(culture, ContentScheduleAction.Expire);
|
||||
if (expires != null && expires.Any(x => x.Date > DateTime.MinValue && DateTime.Now > x.Date))
|
||||
if (expires != null && expires.Any(x => x.Date > DateTime.MinValue && DateTime.UtcNow > x.Date))
|
||||
{
|
||||
return ContentStatus.Expired;
|
||||
}
|
||||
|
||||
IEnumerable<ContentSchedule> release = contentSchedule.GetSchedule(culture, ContentScheduleAction.Release);
|
||||
if (release != null && release.Any(x => x.Date > DateTime.MinValue && x.Date > DateTime.Now))
|
||||
if (release != null && release.Any(x => x.Date > DateTime.MinValue && x.Date > DateTime.UtcNow))
|
||||
{
|
||||
return ContentStatus.AwaitingRelease;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ namespace Umbraco.Extensions;
|
||||
|
||||
public static class DateTimeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the levels to truncate a date to.
|
||||
/// </summary>
|
||||
public enum DateTruncate
|
||||
{
|
||||
Year,
|
||||
@@ -25,33 +28,39 @@ public static class DateTimeExtensions
|
||||
public static string ToIsoString(this DateTime dt) =>
|
||||
dt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
|
||||
|
||||
/// <summary>
|
||||
/// Truncates the date to the specified level, i.e. if you pass in DateTruncate.Hour it will truncate the date to the hour.
|
||||
/// </summary>
|
||||
/// <param name="dt">The date.</param>
|
||||
/// <param name="truncateTo">The level to truncate the date to.</param>
|
||||
/// <returns>The truncated date.</returns>
|
||||
public static DateTime TruncateTo(this DateTime dt, DateTruncate truncateTo)
|
||||
{
|
||||
if (truncateTo == DateTruncate.Year)
|
||||
{
|
||||
return new DateTime(dt.Year, 1, 1);
|
||||
return new DateTime(dt.Year, 1, 1, 0, 0, 0, dt.Kind);
|
||||
}
|
||||
|
||||
if (truncateTo == DateTruncate.Month)
|
||||
{
|
||||
return new DateTime(dt.Year, dt.Month, 1);
|
||||
return new DateTime(dt.Year, dt.Month, 1, 0, 0, 0, dt.Kind);
|
||||
}
|
||||
|
||||
if (truncateTo == DateTruncate.Day)
|
||||
{
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day);
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day, 0, 0, 0, dt.Kind);
|
||||
}
|
||||
|
||||
if (truncateTo == DateTruncate.Hour)
|
||||
{
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0);
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0, dt.Kind);
|
||||
}
|
||||
|
||||
if (truncateTo == DateTruncate.Minute)
|
||||
{
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0);
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, dt.Kind);
|
||||
}
|
||||
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second);
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, dt.Kind);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ public static class IntExtensions
|
||||
/// </returns>
|
||||
public static Guid ToGuid(this int value)
|
||||
{
|
||||
var bytes = new byte[16];
|
||||
BitConverter.GetBytes(value).CopyTo(bytes, 0);
|
||||
Span<byte> bytes = stackalloc byte[16];
|
||||
BitConverter.GetBytes(value).CopyTo(bytes);
|
||||
return new Guid(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3850,9 +3850,8 @@ public static class PublishedContentExtensions
|
||||
|
||||
if (parentKey.HasValue)
|
||||
{
|
||||
IEnumerable<Guid> childrenKeys;
|
||||
var foundChildrenKeys = contentTypeAlias is null
|
||||
? navigationQueryService.TryGetChildrenKeys(parentKey.Value, out childrenKeys)
|
||||
? navigationQueryService.TryGetChildrenKeys(parentKey.Value, out IEnumerable<Guid> childrenKeys)
|
||||
: navigationQueryService.TryGetChildrenKeysOfType(parentKey.Value, contentTypeAlias, out childrenKeys);
|
||||
|
||||
return foundChildrenKeys
|
||||
@@ -3860,19 +3859,12 @@ public static class PublishedContentExtensions
|
||||
: [];
|
||||
}
|
||||
|
||||
IEnumerable<Guid> rootKeys;
|
||||
var foundRootKeys = contentTypeAlias is null
|
||||
? navigationQueryService.TryGetRootKeys(out rootKeys)
|
||||
? navigationQueryService.TryGetRootKeys(out IEnumerable<Guid> rootKeys)
|
||||
: navigationQueryService.TryGetRootKeysOfType(contentTypeAlias, out rootKeys);
|
||||
|
||||
if (foundRootKeys)
|
||||
{
|
||||
IEnumerable<Guid> rootKeysArray = rootKeys as Guid[] ?? rootKeys.ToArray();
|
||||
return rootKeysArray.Contains(content.Key)
|
||||
? publishedStatusFilteringService.FilterAvailable(rootKeysArray, culture)
|
||||
: [];
|
||||
}
|
||||
|
||||
return [];
|
||||
return foundRootKeys
|
||||
? publishedStatusFilteringService.FilterAvailable(rootKeys, culture)
|
||||
: [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -57,17 +58,24 @@ public static class StringExtensions
|
||||
/// <returns></returns>
|
||||
public static int[] GetIdsFromPathReversed(this string path)
|
||||
{
|
||||
string[] pathSegments = path.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries);
|
||||
List<int> nodeIds = new(pathSegments.Length);
|
||||
for (int i = pathSegments.Length - 1; i >= 0; i--)
|
||||
ReadOnlySpan<char> pathSpan = path.AsSpan();
|
||||
List<int> nodeIds = [];
|
||||
foreach (Range rangeOfPathSegment in pathSpan.Split(Constants.CharArrays.Comma))
|
||||
{
|
||||
if (int.TryParse(pathSegments[i], NumberStyles.Integer, CultureInfo.InvariantCulture, out int pathSegment))
|
||||
if (int.TryParse(pathSpan[rangeOfPathSegment], NumberStyles.Integer, CultureInfo.InvariantCulture, out int pathSegment))
|
||||
{
|
||||
nodeIds.Add(pathSegment);
|
||||
}
|
||||
}
|
||||
|
||||
return nodeIds.ToArray();
|
||||
var result = new int[nodeIds.Count];
|
||||
var resultIndex = 0;
|
||||
for (int i = nodeIds.Count - 1; i >= 0; i--)
|
||||
{
|
||||
result[resultIndex++] = nodeIds[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -152,14 +160,16 @@ public static class StringExtensions
|
||||
|
||||
public static string ReplaceNonAlphanumericChars(this string input, char replacement)
|
||||
{
|
||||
var inputArray = input.ToCharArray();
|
||||
var outputArray = new char[input.Length];
|
||||
for (var i = 0; i < inputArray.Length; i++)
|
||||
var chars = input.ToCharArray();
|
||||
for (var i = 0; i < chars.Length; i++)
|
||||
{
|
||||
outputArray[i] = char.IsLetterOrDigit(inputArray[i]) ? inputArray[i] : replacement;
|
||||
if (!char.IsLetterOrDigit(chars[i]))
|
||||
{
|
||||
chars[i] = replacement;
|
||||
}
|
||||
}
|
||||
|
||||
return new string(outputArray);
|
||||
return new string(chars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -209,7 +219,7 @@ public static class StringExtensions
|
||||
|
||||
var nonEmpty = queryStrings.Where(x => !x.IsNullOrWhiteSpace()).ToArray();
|
||||
|
||||
if (url.Contains("?"))
|
||||
if (url.Contains('?'))
|
||||
{
|
||||
return url + string.Join("&", nonEmpty).EnsureStartsWith('&');
|
||||
}
|
||||
@@ -692,7 +702,7 @@ public static class StringExtensions
|
||||
|
||||
if (input.Length == 0)
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
return [];
|
||||
}
|
||||
|
||||
// calc array size - must be groups of 4
|
||||
@@ -807,7 +817,7 @@ public static class StringExtensions
|
||||
}
|
||||
|
||||
// replace chars that would cause problems in URLs
|
||||
var chArray = new char[pos];
|
||||
Span<char> chArray = pos <= 1024 ? stackalloc char[pos] : new char[pos];
|
||||
for (var i = 0; i < pos; i++)
|
||||
{
|
||||
var ch = str[i];
|
||||
@@ -1293,8 +1303,7 @@ public static class StringExtensions
|
||||
}
|
||||
|
||||
// most bytes from the hash are copied straight to the bytes of the new GUID (steps 5-7, 9, 11-12)
|
||||
var newGuid = new byte[16];
|
||||
Array.Copy(hash, 0, newGuid, 0, 16);
|
||||
Span<byte> newGuid = hash.AsSpan()[..16];
|
||||
|
||||
// set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the appropriate 4-bit version number from Section 4.1.3 (step 8)
|
||||
newGuid[6] = (byte)((newGuid[6] & 0x0F) | (version << 4));
|
||||
@@ -1308,7 +1317,7 @@ public static class StringExtensions
|
||||
}
|
||||
|
||||
// Converts a GUID (expressed as a byte array) to/from network order (MSB-first).
|
||||
internal static void SwapByteOrder(byte[] guid)
|
||||
internal static void SwapByteOrder(Span<byte> guid)
|
||||
{
|
||||
SwapBytes(guid, 0, 3);
|
||||
SwapBytes(guid, 1, 2);
|
||||
@@ -1316,12 +1325,7 @@ public static class StringExtensions
|
||||
SwapBytes(guid, 6, 7);
|
||||
}
|
||||
|
||||
private static void SwapBytes(byte[] guid, int left, int right)
|
||||
{
|
||||
var temp = guid[left];
|
||||
guid[left] = guid[right];
|
||||
guid[right] = temp;
|
||||
}
|
||||
private static void SwapBytes(Span<byte> guid, int left, int right) => (guid[left], guid[right]) = (guid[right], guid[left]);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given path is a full path including drive letter
|
||||
|
||||
@@ -22,7 +22,7 @@ public static class UriExtensions
|
||||
/// <remarks>Everything else remains unchanged, except for the fragment which is removed.</remarks>
|
||||
public static Uri Rewrite(this Uri uri, string path)
|
||||
{
|
||||
if (path.StartsWith("/") == false)
|
||||
if (path.StartsWith("/", StringComparison.Ordinal) == false)
|
||||
{
|
||||
throw new ArgumentException("Path must start with a slash.", "path");
|
||||
}
|
||||
@@ -42,12 +42,12 @@ public static class UriExtensions
|
||||
/// <remarks>Everything else remains unchanged, except for the fragment which is removed.</remarks>
|
||||
public static Uri Rewrite(this Uri uri, string path, string query)
|
||||
{
|
||||
if (path.StartsWith("/") == false)
|
||||
if (path.StartsWith("/", StringComparison.Ordinal) == false)
|
||||
{
|
||||
throw new ArgumentException("Path must start with a slash.", "path");
|
||||
}
|
||||
|
||||
if (query.Length > 0 && query.StartsWith("?") == false)
|
||||
if (query.Length > 0 && query.StartsWith("?", StringComparison.Ordinal) == false)
|
||||
{
|
||||
throw new ArgumentException("Query must start with a question mark.", "query");
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public static class GuidUtils
|
||||
// a Guid is 3 blocks + 8 bits
|
||||
|
||||
// so it turns into a 3*8+2 = 26 chars string
|
||||
var chars = new char[length];
|
||||
Span<char> chars = stackalloc char[length];
|
||||
|
||||
var i = 0;
|
||||
var j = 0;
|
||||
|
||||
@@ -28,7 +28,8 @@ public static class HexEncoder
|
||||
public static string Encode(byte[] bytes)
|
||||
{
|
||||
var length = bytes.Length;
|
||||
var chars = new char[length * 2];
|
||||
int charsLength = length * 2;
|
||||
Span<char> chars = charsLength <= 1024 ? stackalloc char[charsLength] : new char[charsLength];
|
||||
|
||||
var index = 0;
|
||||
for (var i = 0; i < length; i++)
|
||||
@@ -38,7 +39,7 @@ public static class HexEncoder
|
||||
chars[index++] = HexLutLo[byteIndex];
|
||||
}
|
||||
|
||||
return new string(chars, 0, chars.Length);
|
||||
return new string(chars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,7 +55,8 @@ public static class HexEncoder
|
||||
public static string Encode(byte[] bytes, char separator, int blockSize, int blockCount)
|
||||
{
|
||||
var length = bytes.Length;
|
||||
var chars = new char[(length * 2) + blockCount];
|
||||
int charsLength = (length * 2) + blockCount;
|
||||
Span<char> chars = charsLength <= 1024 ? stackalloc char[charsLength] : new char[charsLength];
|
||||
var count = 0;
|
||||
var size = 0;
|
||||
var index = 0;
|
||||
@@ -80,6 +82,6 @@ public static class HexEncoder
|
||||
count++;
|
||||
}
|
||||
|
||||
return new string(chars, 0, chars.Length);
|
||||
return new string(chars);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Umbraco.Cms.Core.HostedServices;
|
||||
|
||||
/// <summary>
|
||||
/// A Background Task Queue, to enqueue tasks for executing in the background.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Borrowed from https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-5.0
|
||||
/// </remarks>
|
||||
public interface IBackgroundTaskQueue
|
||||
{
|
||||
/// <summary>
|
||||
/// Enqueue a work item to be executed in the background.
|
||||
/// </summary>
|
||||
void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem);
|
||||
|
||||
/// <summary>
|
||||
/// Dequeue the first item on the queue.
|
||||
/// </summary>
|
||||
Task<Func<CancellationToken, Task>?> DequeueAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -358,7 +358,7 @@ namespace Umbraco.Cms.Core.IO
|
||||
|
||||
// nothing prevents us to reach the file, security-wise, yet it is outside
|
||||
// this filesystem's root - throw
|
||||
throw new UnauthorizedAccessException($"File original: [{originalPath}] full: [{path}] is outside this filesystem's root.");
|
||||
throw new UnauthorizedAccessException($"Requested path {originalPath} is outside this filesystem's root.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,7 +4,6 @@ public abstract class RasterizedTypeDetector
|
||||
{
|
||||
public static byte[]? GetFileHeader(Stream fileStream)
|
||||
{
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
var header = new byte[8];
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ public class Content : ContentBase, IContent
|
||||
|
||||
/// <inheritdoc />
|
||||
[IgnoreDataMember]
|
||||
public IEnumerable<string> PublishedCultures => _publishInfos?.Keys ?? Enumerable.Empty<string>();
|
||||
public IEnumerable<string> PublishedCultures => _publishInfos?.Keys ?? [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsCulturePublished(string culture)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Cms.Core.Models.ContentPublishing;
|
||||
namespace Umbraco.Cms.Core.Models.ContentPublishing;
|
||||
|
||||
public sealed class ContentPublishingBranchResult
|
||||
{
|
||||
@@ -7,4 +7,6 @@ public sealed class ContentPublishingBranchResult
|
||||
public IEnumerable<ContentPublishingBranchItemResult> SucceededItems { get; set; } = [];
|
||||
|
||||
public IEnumerable<ContentPublishingBranchItemResult> FailedItems { get; set; } = [];
|
||||
|
||||
public Guid? AcceptedTaskId { get; init; }
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ public struct UmbracoEntityReference : IEquatable<UmbracoEntityReference>
|
||||
case Constants.UdiEntityType.Media:
|
||||
RelationTypeAlias = Constants.Conventions.RelationTypes.RelatedMediaAlias;
|
||||
break;
|
||||
case Constants.UdiEntityType.Member:
|
||||
RelationTypeAlias = Constants.Conventions.RelationTypes.RelatedMemberAlias;
|
||||
break;
|
||||
default:
|
||||
// No relation type alias convention for this entity type, so leave it empty
|
||||
RelationTypeAlias = string.Empty;
|
||||
|
||||
@@ -53,8 +53,8 @@ public class User : EntityBase, IUser, IProfile
|
||||
_language = globalSettings.DefaultUILanguage;
|
||||
_isApproved = true;
|
||||
_isLockedOut = false;
|
||||
_startContentIds = new int[] { };
|
||||
_startMediaIds = new int[] { };
|
||||
_startContentIds = [];
|
||||
_startMediaIds = [];
|
||||
|
||||
// cannot be null
|
||||
_rawPasswordValue = string.Empty;
|
||||
@@ -101,8 +101,8 @@ public class User : EntityBase, IUser, IProfile
|
||||
_userGroups = new HashSet<IReadOnlyUserGroup>();
|
||||
_isApproved = true;
|
||||
_isLockedOut = false;
|
||||
_startContentIds = new int[] { };
|
||||
_startMediaIds = new int[] { };
|
||||
_startContentIds = [];
|
||||
_startMediaIds = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -39,9 +39,6 @@ public sealed class NavigationNode
|
||||
child.SortOrder = _children.Count;
|
||||
|
||||
_children.Add(childKey);
|
||||
|
||||
// Update the navigation structure
|
||||
navigationStructure[childKey] = child;
|
||||
}
|
||||
|
||||
public void RemoveChild(ConcurrentDictionary<Guid, NavigationNode> navigationStructure, Guid childKey)
|
||||
@@ -53,8 +50,5 @@ public sealed class NavigationNode
|
||||
|
||||
_children.Remove(childKey);
|
||||
child.Parent = null;
|
||||
|
||||
// Update the navigation structure
|
||||
navigationStructure[childKey] = child;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Cms.Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Definition of relation directions used as a filter when requesting if a given item has relations.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum RelationDirectionFilter
|
||||
{
|
||||
Parent = 1,
|
||||
Child = 2,
|
||||
Any = Parent | Child
|
||||
}
|
||||
@@ -23,6 +23,9 @@ public class RelationItem
|
||||
[DataMember(Name = "published")]
|
||||
public bool? NodePublished { get; set; }
|
||||
|
||||
[DataMember(Name = "contentTypeKey")]
|
||||
public Guid ContentTypeKey { get; set; }
|
||||
|
||||
[DataMember(Name = "icon")]
|
||||
public string? ContentTypeIcon { get; set; }
|
||||
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
namespace Umbraco.Cms.Core.Models;
|
||||
namespace Umbraco.Cms.Core.Models;
|
||||
|
||||
public class RelationItemModel
|
||||
{
|
||||
public Guid NodeKey { get; set; }
|
||||
|
||||
public string? NodeAlias { get; set; }
|
||||
|
||||
public string? NodeName { get; set; }
|
||||
|
||||
public string? NodeType { get; set; }
|
||||
|
||||
public bool? NodePublished { get; set; }
|
||||
|
||||
public Guid ContentTypeKey { get; set; }
|
||||
|
||||
public string? ContentTypeIcon { get; set; }
|
||||
|
||||
public string? ContentTypeAlias { get; set; }
|
||||
|
||||
@@ -11,6 +11,7 @@ public static class RelationTypeExtensions
|
||||
public static bool IsSystemRelationType(this IRelationType relationType) =>
|
||||
relationType.Alias == Constants.Conventions.RelationTypes.RelatedDocumentAlias
|
||||
|| relationType.Alias == Constants.Conventions.RelationTypes.RelatedMediaAlias
|
||||
|| relationType.Alias == Constants.Conventions.RelationTypes.RelatedMemberAlias
|
||||
|| relationType.Alias == Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias
|
||||
|| relationType.Alias == Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias
|
||||
|| relationType.Alias == Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace Umbraco.Cms.Core.Models.TemplateQuery;
|
||||
|
||||
[Obsolete("No longer used. Scheduled for removal in V17.")]
|
||||
public class QueryResultModel
|
||||
{
|
||||
public string? QueryExpression { get; set; }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace Umbraco.Cms.Core.Models.TemplateQuery;
|
||||
|
||||
[Obsolete("No longer used. Scheduled for removal in V17.")]
|
||||
public class TemplateQueryResult
|
||||
{
|
||||
public string? Icon { get; set; }
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Core.Notifications;
|
||||
|
||||
public class UserPasswordResettingNotification : CancelableObjectNotification<IUser>
|
||||
{
|
||||
public UserPasswordResettingNotification(IUser target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IUser User => Target;
|
||||
}
|
||||
@@ -24,6 +24,5 @@ public interface IDataTypeRepository : IReadWriteQueryRepository<int, IDataType>
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
IReadOnlyDictionary<Udi, IEnumerable<string>> FindListViewUsages(int id) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -70,6 +70,29 @@ public interface ITrackedReferencesRepository
|
||||
bool filterMustBeIsDependency,
|
||||
out long totalRecords);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a paged result of items which are in relation with an item in the recycle bin.
|
||||
/// </summary>
|
||||
/// <param name="objectTypeKey">The Umbraco object type that has recycle bin support (currently Document or Media).</param>
|
||||
/// <param name="skip">The amount of items to skip.</param>
|
||||
/// <param name="take">The amount of items to take.</param>
|
||||
/// <param name="filterMustBeIsDependency">
|
||||
/// A boolean indicating whether to filter only the RelationTypes which are
|
||||
/// dependencies (isDependency field is set to true).
|
||||
/// </param>
|
||||
/// <param name="totalRecords">The total count of the items with reference to the current item.</param>
|
||||
/// <returns>An enumerable list of <see cref="RelationItem" /> objects.</returns>
|
||||
IEnumerable<RelationItemModel> GetPagedRelationsForRecycleBin(
|
||||
Guid objectTypeKey,
|
||||
long skip,
|
||||
long take,
|
||||
bool filterMustBeIsDependency,
|
||||
out long totalRecords)
|
||||
{
|
||||
totalRecords = 0;
|
||||
return [];
|
||||
}
|
||||
|
||||
[Obsolete("Use overload that takes key instead of id. This will be removed in Umbraco 15.")]
|
||||
IEnumerable<RelationItemModel> GetPagedRelationsForItem(
|
||||
int id,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user