Compare commits
259
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
497c31ef2c | ||
|
|
78dfcf97b7 | ||
|
|
fd4ed2cfc1 | ||
|
|
9340842ee2 | ||
|
|
86411e4ae3 | ||
|
|
b0e5cd768d | ||
|
|
857f2900bb | ||
|
|
657ccbd104 | ||
|
|
7d0101170e | ||
|
|
706ac2d8f6 | ||
|
|
f408d2a1b3 | ||
|
|
1694e3bad2 | ||
|
|
35f73a8a31 | ||
|
|
822a2fcf70 | ||
|
|
1d59e20daa | ||
|
|
75b40e79a2 | ||
|
|
1c4b4c90c9 | ||
|
|
e6b99938db | ||
|
|
5ab93454d9 | ||
|
|
da502e06ae | ||
|
|
2b7efbe861 | ||
|
|
9beed532a9 | ||
|
|
215c4dc540 | ||
|
|
f19aaaee52 | ||
|
|
dc50a6e8c3 | ||
|
|
1e64c8d5af | ||
|
|
745d74104e | ||
|
|
cc8d90be91 | ||
|
|
95c9d48b6e | ||
|
|
57c66faf40 | ||
|
|
08990b9147 | ||
|
|
23fc355e61 | ||
|
|
1fedaa3d8f | ||
|
|
ba7d550a74 | ||
|
|
0600df4211 | ||
|
|
06c566c074 | ||
|
|
2a609e1eca | ||
|
|
342cb171eb | ||
|
|
e850c131e5 | ||
|
|
c48103cd30 | ||
|
|
20180c3180 | ||
|
|
89d487e449 | ||
|
|
640f9f2615 | ||
|
|
14f4c54312 | ||
|
|
cbdb1d567f | ||
|
|
31072bab51 | ||
|
|
d687c4365a | ||
|
|
43688148ae | ||
|
|
e39b05f44d | ||
|
|
2067db1c3c | ||
|
|
6f2b7ab1a0 | ||
|
|
ea142d51b7 | ||
|
|
e3c394b109 | ||
|
|
2e85f1bbf8 | ||
|
|
b22d3ddc00 | ||
|
|
b68a6a9502 | ||
|
|
a4d893a7b4 | ||
|
|
313b60aca5 | ||
|
|
4ee1d7b13e | ||
|
|
95cc6cc67b | ||
|
|
417335a5c6 | ||
|
|
bd94522237 | ||
|
|
aadf5e7643 | ||
|
|
e315ae3ae1 | ||
|
|
393179d8a1 | ||
|
|
b7105c88d8 | ||
|
|
f1b7a33e86 | ||
|
|
a61bb8dbef | ||
|
|
da0bdbf8c7 | ||
|
|
f726a7d7c5 | ||
|
|
f6b566c65f | ||
|
|
a3d6b4f844 | ||
|
|
1615740bdf | ||
|
|
d3b9256b4e | ||
|
|
399431b02d | ||
|
|
33c2838d20 | ||
|
|
981c9a7db2 | ||
|
|
de9cbf9ef5 | ||
|
|
bd6ab83712 | ||
|
|
4252cbc64b | ||
|
|
b3145a01eb | ||
|
|
aeb1437f0e | ||
|
|
04df2652f5 | ||
|
|
1bf53f3554 | ||
|
|
a5adcbdeef | ||
|
|
286e727de8 | ||
|
|
201906f662 | ||
|
|
a4c0da3465 | ||
|
|
28c01d2819 | ||
|
|
2248eeb03e | ||
|
|
f6df0c1828 | ||
|
|
63ae29a1aa | ||
|
|
6effe32136 | ||
|
|
f0ac7b4a50 | ||
|
|
5f0122e18b | ||
|
|
de5a9ca5af | ||
|
|
58068d1aa7 | ||
|
|
5f1c65e7ea | ||
|
|
20eedda4c7 | ||
|
|
1099332edb | ||
|
|
3a83e4a494 | ||
|
|
af3b17243b | ||
|
|
d05add4e1f | ||
|
|
68d1b9481a | ||
|
|
e6f48799a1 | ||
|
|
1efdde2473 | ||
|
|
c666c00ac5 | ||
|
|
94692cccb7 | ||
|
|
ab4be79da0 | ||
|
|
a808e3ab46 | ||
|
|
92dbaf7162 | ||
|
|
72d44decd4 | ||
|
|
7e7d9da144 | ||
|
|
f82c0a90e7 | ||
|
|
65798b5722 | ||
|
|
7d0adc4755 | ||
|
|
11bf60a67c | ||
|
|
8beb7f2acc | ||
|
|
498754e170 | ||
|
|
3830d75413 | ||
|
|
f1f2f702c0 | ||
|
|
2503cabbce | ||
|
|
7e8a7f0b40 | ||
|
|
126c8a56d5 | ||
|
|
99ea8b9087 | ||
|
|
86dda9f9b3 | ||
|
|
9c082c613e | ||
|
|
5af9df1871 | ||
|
|
12297ea617 | ||
|
|
3892670d92 | ||
|
|
d9de69c40f | ||
|
|
49f3fc9993 | ||
|
|
7f1cdf8ef5 | ||
|
|
37b239b8ca | ||
|
|
ea44850804 | ||
|
|
8be2d77006 | ||
|
|
a6eb2a1527 | ||
|
|
11fcfba091 | ||
|
|
d6b9eb61b2 | ||
|
|
0367e56db6 | ||
|
|
2ce6c8f8c8 | ||
|
|
fd1c9f1673 | ||
|
|
c68a26cb5f | ||
|
|
c037323910 | ||
|
|
f60436270f | ||
|
|
9b0b60e6cb | ||
|
|
f539a27a19 | ||
|
|
832bd7eb61 | ||
|
|
fb91ade164 | ||
|
|
38a9a79c75 | ||
|
|
924b1446e3 | ||
|
|
2e0d006a6c | ||
|
|
d9f8faf509 | ||
|
|
5eaf7fdb90 | ||
|
|
0dcb10ad41 | ||
|
|
15089b28bf | ||
|
|
ebe056df05 | ||
|
|
54407a0687 | ||
|
|
787a5ccc2d | ||
|
|
17a5477242 | ||
|
|
74328e9496 | ||
|
|
cd9dda2aad | ||
|
|
62efc2631e | ||
|
|
20de48a496 | ||
|
|
f0cf4703fa | ||
|
|
0cf8279e65 | ||
|
|
2f712cab08 | ||
|
|
034cf2894c | ||
|
|
1498f3bdc6 | ||
|
|
324e5bcb0a | ||
|
|
ba4a874fb4 | ||
|
|
1f351244d4 | ||
|
|
e3d2001477 | ||
|
|
7c0b5b7dd4 | ||
|
|
7bcef5fde5 | ||
|
|
173887d0a1 | ||
|
|
c67478874e | ||
|
|
0daf5ea506 | ||
|
|
29bdd3cac0 | ||
|
|
c5b4571cd5 | ||
|
|
d45a8f2049 | ||
|
|
0ded9a74fb | ||
|
|
99c51a54f1 | ||
|
|
3da8f41292 | ||
|
|
f14c4d5968 | ||
|
|
5d986e44f6 | ||
|
|
51575e5e36 | ||
|
|
07203b7270 | ||
|
|
cdb2be64d4 | ||
|
|
ac6b205e7f | ||
|
|
81f29be53b | ||
|
|
ac23ff5f38 | ||
|
|
099e7d7d3a | ||
|
|
b1956dea3e | ||
|
|
8d5117fec1 | ||
|
|
d8e8e32f75 | ||
|
|
765e5b0ba9 | ||
|
|
099fd2aa7c | ||
|
|
cea2184d47 | ||
|
|
f5cfbb110e | ||
|
|
aa2a85328e | ||
|
|
7db7346d4a | ||
|
|
859505e751 | ||
|
|
5d17920a73 | ||
|
|
815f28e3b9 | ||
|
|
a7bb1fc20d | ||
|
|
dca70a0bd0 | ||
|
|
07641981a4 | ||
|
|
b593d9dadf | ||
|
|
dd01a56d2a | ||
|
|
8a342be4f4 | ||
|
|
eea970a648 | ||
|
|
20a2f90441 | ||
|
|
3d7ac5776c | ||
|
|
c3eabda10e | ||
|
|
d3e6508952 | ||
|
|
6dc9191ad3 | ||
|
|
f4c0264e61 | ||
|
|
60a03c75e4 | ||
|
|
f050488b92 | ||
|
|
5a0a2bf77e | ||
|
|
da8c036719 | ||
|
|
20c29f8091 | ||
|
|
b8ca11edd6 | ||
|
|
2a6bb64c78 | ||
|
|
156bcdc98a | ||
|
|
07abec239d | ||
|
|
50ae48b0a2 | ||
|
|
0b2a247614 | ||
|
|
f2b5061414 | ||
|
|
d623476902 | ||
|
|
b427a8cbdd | ||
|
|
bd8ad5f6f9 | ||
|
|
596389d961 | ||
|
|
cd00dfaa34 | ||
|
|
6d5271fb80 | ||
|
|
f8ec2dadfe | ||
|
|
e57f66537c | ||
|
|
ab0af2a291 | ||
|
|
5fc12ec36e | ||
|
|
73d2f207d0 | ||
|
|
c3e93f143c | ||
|
|
c6bc4ef49a | ||
|
|
c1ac80653b | ||
|
|
a50ad893a8 | ||
|
|
a6c311977c | ||
|
|
04ac8d3ea9 | ||
|
|
3fcc09169a | ||
|
|
2ce647738f | ||
|
|
4a438ad6e9 | ||
|
|
0b1bd16b9d | ||
|
|
447cb881bd | ||
|
|
ceb745a7bd | ||
|
|
7fc2bc84de | ||
|
|
321087c96e | ||
|
|
f0e37e04fa | ||
|
|
135f0f63de | ||
|
|
2dced37117 | ||
|
|
44d9f5ac2e |
+10
-4
@@ -9,7 +9,7 @@ In order to use Umbraco as a CMS and build your website with it, you should not
|
||||
- Are you about to [create a pull request for Umbraco][contribution guidelines]?
|
||||
- Are you trying to get to the bottom of a problem in your existing Umbraco installation?
|
||||
|
||||
If the answer is yes, please read on. Otherwise, make sure to head on over [to the download page](https://our.umbraco.com/download) and start using Umbraco CMS as intended.
|
||||
If the answer is yes, please read on. Otherwise, make sure to head on over [to the releases page](https://releases.umbraco.com) and start using Umbraco CMS as intended.
|
||||
|
||||
## Table of contents
|
||||
|
||||
@@ -37,7 +37,7 @@ In order to work with the Umbraco source code locally, first make sure you have
|
||||
|
||||
### Familiarizing yourself with the code
|
||||
|
||||
Umbraco is a .NET application using C#. The solution is broken down into multiple projects. There are several class libraries. The `Umbraco.Web.UI` project is the main project that hosts the back office and login screen. This is the project you will want to run to see your changes.
|
||||
Umbraco is a .NET application using C#. The solution is broken down into multiple projects. There are several class libraries. The `Umbraco.Web.UI` project is the main project that hosts the back office and login screen. This is the project you will want to run to see your changes.
|
||||
|
||||
There are two web projects in the solution with client-side assets based on TypeScript, `Umbraco.Web.UI.Client` and `Umbraco.Web.UI.Login`.
|
||||
|
||||
@@ -73,13 +73,19 @@ Just be careful not to include this change in your PR.
|
||||
|
||||
Conversely, if you are working on front-end only, you want to build the back-end once and then run it. Before you do so, update the configuration in `appSettings.json` to add the following under `Umbraco:Cms:Security`:
|
||||
|
||||
```
|
||||
```json
|
||||
"BackOfficeHost": "http://localhost:5173",
|
||||
"AuthorizeCallbackPathName": "/oauth_complete",
|
||||
"AuthorizeCallbackLogoutPathName": "/logout",
|
||||
"AuthorizeCallbackErrorPathName": "/error"
|
||||
"AuthorizeCallbackErrorPathName": "/error",
|
||||
"BackOfficeTokenCookie": {
|
||||
"SameSite": "None"
|
||||
}
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> If you get stuck in a login loop, try clearing your browser cookies for localhost, and make sure that the `Umbraco:Cms:Security:BackOfficeTokenCookie:SameSite` setting is set to `None`.
|
||||
|
||||
Then run Umbraco from the command line.
|
||||
|
||||
```
|
||||
|
||||
@@ -79,7 +79,7 @@ You can get in touch with [the core contributors team][core collabs] in multiple
|
||||
|
||||
- If there's an existing issue on the issue tracker then that's a good place to leave questions and discuss how to start or move forward.
|
||||
- If you want to ask questions on some code you've already written you can create a draft pull request, [detailed in a GitHub blog post][draft prs].
|
||||
- Unsure where to start? Did something not work as expected? Try leaving a note in the ["Contributing to Umbraco"][contrib forum] forum. The team monitors that one closely, so one of us will be on hand and ready to point you in the right direction.
|
||||
- Unsure where to start? Did something not work as expected? Try leaving a note in the [forum][forum]. The team monitors that one closely, so one of us will be on hand and ready to point you in the right direction.
|
||||
|
||||
|
||||
<!-- Local -->
|
||||
@@ -90,7 +90,7 @@ You can get in touch with [the core contributors team][core collabs] in multiple
|
||||
|
||||
[sync fork ext]: http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated "Details on keeping a git fork updated"
|
||||
[draft prs]: https://github.blog/2019-02-14-introducing-draft-pull-requests/ "Github's blog post providing details on draft pull requests"
|
||||
[contrib forum]: https://our.umbraco.com/forum/contributing-to-umbraco-cms/
|
||||
[forum]: https://forum.umbraco.com/
|
||||
[Umbraco CMS repo]: https://github.com/umbraco/Umbraco-CMS
|
||||
[up for grabs issues]: https://github.com/umbraco/Umbraco-CMS/issues?q=is%3Aissue+is%3Aopen+label%3Acommunity%2Fup-for-grabs
|
||||
[issue tracker]: https://github.com/umbraco/Umbraco-CMS/issues
|
||||
@@ -31,6 +31,8 @@ Bootstrap, build, and test the repository:
|
||||
- `cd src/Umbraco.Web.UI`
|
||||
- `dotnet run --no-build` -- Application runs on https://localhost:44339 and http://localhost:11000
|
||||
|
||||
Check out [BUILD.md](./BUILD.md) for more detailed instructions.
|
||||
|
||||
## Validation
|
||||
|
||||
- ALWAYS run through at least one complete end-to-end scenario after making changes.
|
||||
@@ -103,7 +105,10 @@ For frontend-only changes:
|
||||
"BackOfficeHost": "http://localhost:5173",
|
||||
"AuthorizeCallbackPathName": "/oauth_complete",
|
||||
"AuthorizeCallbackLogoutPathName": "/logout",
|
||||
"AuthorizeCallbackErrorPathName": "/error"
|
||||
"AuthorizeCallbackErrorPathName": "/error",
|
||||
"BackOfficeTokenCookie": {
|
||||
"SameSite": "None"
|
||||
}
|
||||
```
|
||||
2. Run backend: `cd src/Umbraco.Web.UI && dotnet run --no-build`
|
||||
3. Run frontend dev server: `cd src/Umbraco.Web.UI.Client && npm run dev:server`
|
||||
|
||||
Vendored
+3
-1
@@ -104,7 +104,9 @@
|
||||
"UMBRACO__CMS__SECURITY__BACKOFFICEHOST": "http://localhost:5173",
|
||||
"UMBRACO__CMS__SECURITY__AUTHORIZECALLBACKPATHNAME": "/oauth_complete",
|
||||
"UMBRACO__CMS__SECURITY__AUTHORIZECALLBACKLOGOUTPATHNAME": "/logout",
|
||||
"UMBRACO__CMS__SECURITY__AUTHORIZECALLBACKERRORPATHNAME": "/error"
|
||||
"UMBRACO__CMS__SECURITY__AUTHORIZECALLBACKERRORPATHNAME": "/error",
|
||||
"UMBRACO__CMS__SECURITY__KEEPUSERLOGGEDIN": "true",
|
||||
"UMBRACO__CMS__SECURITY__BACKOFFICETOKENCOOKIE__SAMESITE": "None"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/Views": "${workspaceFolder}/Umbraco.Web.UI/Views"
|
||||
|
||||
Vendored
+3
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"backoffice",
|
||||
"pickable",
|
||||
"Pickable",
|
||||
"unprovide",
|
||||
"Unproviding"
|
||||
],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Company>Umbraco HQ</Company>
|
||||
<Authors>Umbraco</Authors>
|
||||
<Copyright>Copyright © Umbraco $([System.DateTime]::Today.ToString('yyyy'))</Copyright>
|
||||
@@ -40,7 +40,7 @@
|
||||
<!-- Package Validation -->
|
||||
<PropertyGroup>
|
||||
<GenerateCompatibilitySuppressionFile>false</GenerateCompatibilitySuppressionFile>
|
||||
<EnablePackageValidation>true</EnablePackageValidation>
|
||||
<EnablePackageValidation>false</EnablePackageValidation> <!-- TODO (V17): Set to true with version 17.0.0 once this version is released. -->
|
||||
<PackageValidationBaselineVersion>16.0.0</PackageValidationBaselineVersion>
|
||||
<EnableStrictModeForCompatibleFrameworksInPackage>true</EnableStrictModeForCompatibleFrameworksInPackage>
|
||||
<EnableStrictModeForCompatibleTfms>true</EnableStrictModeForCompatibleTfms>
|
||||
|
||||
+37
-61
@@ -5,39 +5,35 @@
|
||||
</PropertyGroup>
|
||||
<!-- Global packages (private, build-time packages for all projects) -->
|
||||
<ItemGroup>
|
||||
<GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.7.115" />
|
||||
<GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.8.118" />
|
||||
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
|
||||
<GlobalPackageReference Include="Umbraco.Code" Version="2.3.0" />
|
||||
<GlobalPackageReference Include="Umbraco.Code" Version="2.4.0" />
|
||||
<GlobalPackageReference Include="Umbraco.GitVersioning.Extensions" Version="0.2.0" />
|
||||
</ItemGroup>
|
||||
<!-- Microsoft packages -->
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.13.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.13.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.13.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.13.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.4.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="10.0.0" />
|
||||
<PackageVersion Include="System.Linq.Async" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
<!-- Umbraco packages -->
|
||||
<ItemGroup>
|
||||
@@ -50,21 +46,21 @@
|
||||
<PackageVersion Include="Dazinator.Extensions.FileProviders" Version="2.0.0" />
|
||||
<PackageVersion Include="Examine" Version="3.7.1" />
|
||||
<PackageVersion Include="Examine.Core" Version="3.7.1" />
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.12.1" />
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.12.4" />
|
||||
<PackageVersion Include="JsonPatch.Net" Version="3.3.0" />
|
||||
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.8" />
|
||||
<PackageVersion Include="MailKit" Version="4.11.0" />
|
||||
<PackageVersion Include="MailKit" Version="4.14.0" />
|
||||
<PackageVersion Include="Markdown" Version="2.2.1" />
|
||||
<PackageVersion Include="MessagePack" Version="3.1.3" />
|
||||
<PackageVersion Include="MessagePack" Version="3.1.4" />
|
||||
<PackageVersion Include="MiniProfiler.AspNetCore.Mvc" Version="4.5.4" />
|
||||
<PackageVersion Include="MiniProfiler.Shared" Version="4.5.4" />
|
||||
<PackageVersion Include="ncrontab" Version="3.3.3" />
|
||||
<PackageVersion Include="NPoco" Version="5.7.1" />
|
||||
<PackageVersion Include="NPoco.SqlServer" Version="5.7.1" />
|
||||
<PackageVersion Include="OpenIddict.Abstractions" Version="6.2.1" />
|
||||
<PackageVersion Include="OpenIddict.AspNetCore" Version="6.2.1" />
|
||||
<PackageVersion Include="OpenIddict.EntityFrameworkCore" Version="6.2.1" />
|
||||
<PackageVersion Include="Serilog" Version="4.2.0" />
|
||||
<PackageVersion Include="ncrontab" Version="3.4.0" />
|
||||
<PackageVersion Include="NPoco" Version="6.1.0" />
|
||||
<PackageVersion Include="NPoco.SqlServer" Version="6.1.0" />
|
||||
<PackageVersion Include="OpenIddict.Abstractions" Version="7.1.0" />
|
||||
<PackageVersion Include="OpenIddict.AspNetCore" Version="7.1.0" />
|
||||
<PackageVersion Include="OpenIddict.EntityFrameworkCore" Version="7.1.0" />
|
||||
<PackageVersion Include="Serilog" Version="4.3.0" />
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||
<PackageVersion Include="Serilog.Enrichers.Process" Version="3.0.0" />
|
||||
<PackageVersion Include="Serilog.Enrichers.Thread" Version="4.0.0" />
|
||||
@@ -74,33 +70,13 @@
|
||||
<PackageVersion Include="Serilog.Formatting.Compact.Reader" Version="4.0.0" />
|
||||
<PackageVersion Include="Serilog.Settings.Configuration" Version="9.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Map" Version="2.0.0" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.1.5" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.2.0" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="10.0.1" />
|
||||
</ItemGroup>
|
||||
<!-- Transitive pinned versions (only required because our direct dependencies have vulnerable versions of transitive dependencies) -->
|
||||
<ItemGroup>
|
||||
<!-- Microsoft.EntityFrameworkCore.SqlServer and NPoco.SqlServer brings in a vulnerable version of Azure.Identity -->
|
||||
<!-- Take top-level depedendency on Azure.Identity, because Microsoft.EntityFrameworkCore.SqlServer depends on a vulnerable version -->
|
||||
<PackageVersion Include="Azure.Identity" Version="1.13.2" />
|
||||
<!-- Microsoft.EntityFrameworkCore.SqlServer brings in a vulnerable version of System.Runtime.Caching -->
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="9.0.4" />
|
||||
<!-- Dazinator.Extensions.FileProviders brings in a vulnerable version of System.Net.Http -->
|
||||
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
||||
<!-- Examine brings in a vulnerable version of System.Security.Cryptography.Xml -->
|
||||
<PackageVersion Include="System.Security.Cryptography.Xml" Version="9.0.4" />
|
||||
<!-- Dazinator.Extensions.FileProviders and MiniProfiler.AspNetCore.Mvc brings in a vulnerable version of System.Text.RegularExpressions -->
|
||||
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
<!-- OpenIddict.AspNetCore, Npoco.SqlServer and Microsoft.EntityFrameworkCore.SqlServer brings in a vulnerable version of Microsoft.IdentityModel.JsonWebTokens -->
|
||||
<!-- Take top-level depedendency on Microsoft.IdentityModel.JsonWebTokens, because OpenIddict.AspNetCore, Npoco.SqlServer and Microsoft.EntityFrameworkCore.SqlServer depends on a vulnerable version -->
|
||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.8.0" />
|
||||
<!-- Azure.Identity, Microsoft.EntityFrameworkCore.SqlServer and Dazinator.Extensions.FileProviders brings in a legacy version of System.Text.Encodings.Web -->
|
||||
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.4" />
|
||||
<!-- NPoco.SqlServer brings in a vulnerable version of Microsoft.Data.SqlClient -->
|
||||
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.0.1" />
|
||||
<!-- Examine.Lucene brings in a vulnerable version of Lucene.Net.Replicator -->
|
||||
<PackageVersion Include="Lucene.Net.Replicator" Version="4.8.0-beta00017" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -34,6 +34,10 @@ parameters:
|
||||
displayName: Upload API docs
|
||||
type: boolean
|
||||
default: false
|
||||
- name: uploadDependencyTrack
|
||||
displayName: Upload BOMs to Dependency Track
|
||||
type: boolean
|
||||
default: false
|
||||
- name: forceReleaseTestFilter
|
||||
displayName: Force to use the release test filters
|
||||
type: boolean
|
||||
@@ -103,6 +107,15 @@ stages:
|
||||
command: build
|
||||
projects: $(solution)
|
||||
arguments: "--configuration $(buildConfiguration) --no-restore --property:ContinuousIntegrationBuild=true --property:GeneratePackageOnBuild=true --property:PackageOutputPath=$(Build.ArtifactStagingDirectory)/nupkg"
|
||||
- powershell: |
|
||||
dotnet tool install --global CycloneDX
|
||||
dotnet-CycloneDX $(solution) --output $(Build.ArtifactStagingDirectory)/bom --filename bom-dotnet.xml
|
||||
displayName: 'Generate Backend BOM'
|
||||
- powershell: |
|
||||
npm install --global @cyclonedx/cyclonedx-npm
|
||||
cyclonedx-npm -o $(Build.ArtifactStagingDirectory)\bom\bom-login.xml --ignore-npm-errors --verbose
|
||||
displayName: Generate Login UI BOM
|
||||
workingDirectory: src/Umbraco.Web.UI.Login
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: Publish nupkg
|
||||
inputs:
|
||||
@@ -113,6 +126,11 @@ stages:
|
||||
inputs:
|
||||
targetPath: $(Build.SourcesDirectory)
|
||||
artifactName: build_output
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: Publish Backend BOM
|
||||
inputs:
|
||||
targetPath: $(Build.ArtifactStagingDirectory)/bom
|
||||
artifactName: bom-backend
|
||||
|
||||
- job: B
|
||||
displayName: Build Bellissima Package
|
||||
@@ -124,6 +142,11 @@ stages:
|
||||
lfs: false,
|
||||
fetchDepth: 500
|
||||
- template: templates/backoffice-install.yml
|
||||
- powershell: |
|
||||
npm install --global @cyclonedx/cyclonedx-npm
|
||||
cyclonedx-npm -o $(Build.ArtifactStagingDirectory)/bom/bom-backoffice.xml --ignore-npm-errors --verbose
|
||||
displayName: Generate Backoffice UI BOM
|
||||
workingDirectory: src/Umbraco.Web.UI.Client
|
||||
- script: npm run build:for:npm
|
||||
displayName: Run build:for:npm
|
||||
workingDirectory: src/Umbraco.Web.UI.Client
|
||||
@@ -140,6 +163,35 @@ stages:
|
||||
inputs:
|
||||
targetPath: $(Build.ArtifactStagingDirectory)/npm
|
||||
artifactName: npm
|
||||
- publish: $(Build.ArtifactStagingDirectory)/bom
|
||||
artifact: bom-frontend
|
||||
displayName: 'Publish Frontend BOM'
|
||||
|
||||
- stage: E2E_BOM
|
||||
displayName: E2E Tests BOM Generation
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- job:
|
||||
displayName: E2E Generate BOM
|
||||
pool:
|
||||
vmImage: "ubuntu-latest"
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: false
|
||||
lfs: false,
|
||||
fetchDepth: 500
|
||||
- template: templates/e2e-install.yml
|
||||
parameters:
|
||||
nodeVersion: ${{ variables.nodeVersion }}
|
||||
npm_config_cache: ${{ variables.npm_config_cache }}
|
||||
- powershell: |
|
||||
npm install --global @cyclonedx/cyclonedx-npm
|
||||
cyclonedx-npm -o $(Build.ArtifactStagingDirectory)/bom/bom-e2e.xml --ignore-npm-errors --verbose
|
||||
displayName: Generate E2E Tests BOM
|
||||
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
|
||||
- publish: $(Build.ArtifactStagingDirectory)/bom
|
||||
artifact: bom-e2e
|
||||
displayName: 'Publish E2E BOM'
|
||||
|
||||
- stage: Build_Docs
|
||||
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.buildApiDocs}}))
|
||||
@@ -313,8 +365,8 @@ stages:
|
||||
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
|
||||
LinuxPart3Of3:
|
||||
vmImage: "ubuntu-latest"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
macOSPart1Of3:
|
||||
vmImage: "macOS-latest"
|
||||
# Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace
|
||||
@@ -325,8 +377,8 @@ stages:
|
||||
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
|
||||
macOSPart3Of3:
|
||||
vmImage: "macOS-latest"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure namespace.
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure and the ManagementApi namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
variables:
|
||||
@@ -389,8 +441,8 @@ stages:
|
||||
vmImage: "windows-latest"
|
||||
Tests__Database__DatabaseType: LocalDb
|
||||
Tests__Database__SQLServerMasterConnectionString: N/A
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
LinuxPart1Of3:
|
||||
vmImage: "ubuntu-latest"
|
||||
SA_PASSWORD: UmbracoIntegration123!
|
||||
@@ -410,8 +462,8 @@ stages:
|
||||
SA_PASSWORD: UmbracoIntegration123!
|
||||
Tests__Database__DatabaseType: SqlServer
|
||||
Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
steps:
|
||||
@@ -668,6 +720,34 @@ stages:
|
||||
ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }}
|
||||
DatabaseType: ${{ variables.DatabaseType }}
|
||||
|
||||
- stage: Dependency_Track
|
||||
displayName: Dependency Track
|
||||
dependsOn:
|
||||
- Build
|
||||
- E2E_BOM
|
||||
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.uploadDependencyTrack}}))
|
||||
variables:
|
||||
# Determine Umbraco version based on whether it's a public release or not. If public release, use major version, else use full NuGet package version.
|
||||
umbracoVersion: $[ iif(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), stageDependencies.Build.A.outputs['build.NBGV_VersionMajor'], stageDependencies.Build.A.outputs['build.NBGV_NuGetPackageVersion']) ]
|
||||
jobs:
|
||||
- template: templates/dependency-track.yml
|
||||
parameters:
|
||||
projectName: "Umbraco-CMS"
|
||||
umbracoVersion: $(umbracoVersion)
|
||||
projects:
|
||||
- name: "Backend"
|
||||
artifact: "bom-backend"
|
||||
bomFilePath: "bom-dotnet.xml"
|
||||
- name: "Login"
|
||||
artifact: "bom-backend"
|
||||
bomFilePath: "bom-login.xml"
|
||||
- name: "Backoffice"
|
||||
artifact: "bom-frontend"
|
||||
bomFilePath: "bom-backoffice.xml"
|
||||
- name: "E2E"
|
||||
artifact: "bom-e2e"
|
||||
bomFilePath: "bom-e2e.xml"
|
||||
|
||||
###############################################
|
||||
## Release
|
||||
###############################################
|
||||
@@ -874,3 +954,4 @@ stages:
|
||||
ContainerName: "$web"
|
||||
BlobPrefix: v$(umbracoMajorVersion)/ui-api
|
||||
CleanTargetBeforeCopy: true
|
||||
|
||||
|
||||
@@ -26,38 +26,18 @@ steps:
|
||||
artifact: nupkg
|
||||
path: $(Agent.BuildDirectory)/app/nupkg
|
||||
|
||||
- task: NodeTool@0
|
||||
displayName: Use Node.js $(nodeVersion)
|
||||
inputs:
|
||||
versionSpec: $(nodeVersion)
|
||||
|
||||
- task: UseDotNet@2
|
||||
displayName: Use .NET SDK from global.json
|
||||
inputs:
|
||||
useGlobalJson: true
|
||||
|
||||
- pwsh: |
|
||||
"UMBRACO_USER_LOGIN=${{ parameters.PlaywrightUserEmail }}
|
||||
UMBRACO_USER_PASSWORD=${{ parameters.PlaywrightPassword }}
|
||||
URL=${{ parameters.ASPNETCORE_URLS }}
|
||||
STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json
|
||||
CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env
|
||||
displayName: Generate .env
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
|
||||
|
||||
# Cache and restore NPM packages
|
||||
- task: Cache@2
|
||||
displayName: Cache NPM packages
|
||||
inputs:
|
||||
key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json'
|
||||
restoreKeys: |
|
||||
npm_e2e | "$(Agent.OS)"
|
||||
npm_e2e
|
||||
path: ${{ parameters.npm_config_cache }}
|
||||
|
||||
- script: npm ci --no-fund --no-audit --prefer-offline
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
|
||||
displayName: Restore NPM packages
|
||||
- template: templates/e2e-install.yml
|
||||
parameters:
|
||||
nodeVersion: ${{ parameters.nodeVersion }}
|
||||
npm_config_cache: ${{ parameters.npm_config_cache }}
|
||||
PlaywrightUserEmail: ${{ parameters.PlaywrightUserEmail }}
|
||||
PlaywrightPassword: ${{ parameters.PlaywrightPassword }}
|
||||
ASPNETCORE_URLS: ${{ parameters.ASPNETCORE_URLS }}
|
||||
|
||||
# Install Template
|
||||
- pwsh: |
|
||||
|
||||
@@ -10,6 +10,7 @@ schedules:
|
||||
include:
|
||||
- v15/dev
|
||||
- main
|
||||
- v17/dev
|
||||
|
||||
parameters:
|
||||
- name: skipIntegrationTests
|
||||
@@ -20,7 +21,7 @@ parameters:
|
||||
- name: differentAppSettingsAcceptanceTests
|
||||
displayName: Run acceptance tests with different app settings
|
||||
type: boolean
|
||||
default: false
|
||||
default: true
|
||||
|
||||
- name: skipDefaultConfigAcceptanceTests
|
||||
displayName: Skip tests with DefaultConfig
|
||||
@@ -122,31 +123,39 @@ stages:
|
||||
matrix:
|
||||
# Windows:
|
||||
# vmImage: 'windows-latest'
|
||||
# We split the tests into 3 parts for each OS to reduce the time it takes to run them on the pipeline
|
||||
LinuxPart1Of3:
|
||||
# We split the tests into 4 parts for each OS to reduce the time it takes to run them on the pipeline
|
||||
LinuxPart1Of4:
|
||||
vmImage: "ubuntu-latest"
|
||||
# Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace
|
||||
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)"
|
||||
LinuxPart2Of3:
|
||||
LinuxPart2Of4:
|
||||
vmImage: "ubuntu-latest"
|
||||
# Filter tests that are part of the Umbraco.Infrastructure.Service namespace
|
||||
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
|
||||
LinuxPart3Of3:
|
||||
LinuxPart3Of4:
|
||||
vmImage: "ubuntu-latest"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
|
||||
macOSPart1Of3:
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
LinuxPart4Of4:
|
||||
vmImage: "ubuntu-latest"
|
||||
# Filter tests that are part of the ManagementApi namespace
|
||||
testFilter: "(FullyQualifiedName~ManagementApi)"
|
||||
macOSPart1Of4:
|
||||
vmImage: "macOS-latest"
|
||||
# Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace
|
||||
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)"
|
||||
macOSPart2Of3:
|
||||
macOSPart2Of4:
|
||||
vmImage: "macOS-latest"
|
||||
# Filter tests that are part of the Umbraco.Infrastructure.Service namespace
|
||||
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
|
||||
macOSPart3Of3:
|
||||
macOSPart3Of4:
|
||||
vmImage: "macOS-latest"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure namespace.
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace.
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
macOSPart4Of4:
|
||||
vmImage: "macOS-latest"
|
||||
# Filter tests that are part of the ManagementApi namespace.
|
||||
testFilter: "(FullyQualifiedName~ManagementApi)"
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
variables:
|
||||
@@ -186,43 +195,55 @@ stages:
|
||||
SA_PASSWORD: UmbracoAcceptance123!
|
||||
strategy:
|
||||
matrix:
|
||||
# We split the tests into 3 parts for each OS to reduce the time it takes to run them on the pipeline
|
||||
WindowsPart1Of3:
|
||||
# We split the tests into 4 parts for each OS to reduce the time it takes to run them on the pipeline
|
||||
WindowsPart1Of4:
|
||||
vmImage: "windows-latest"
|
||||
Tests__Database__DatabaseType: LocalDb
|
||||
Tests__Database__SQLServerMasterConnectionString: N/A
|
||||
# Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace
|
||||
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)"
|
||||
WindowsPart2Of3:
|
||||
WindowsPart2Of4:
|
||||
vmImage: "windows-latest"
|
||||
Tests__Database__DatabaseType: LocalDb
|
||||
Tests__Database__SQLServerMasterConnectionString: N/A
|
||||
# Filter tests that are part of the Umbraco.Infrastructure.Service namespace
|
||||
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
|
||||
WindowsPart3Of3:
|
||||
WindowsPart3Of4:
|
||||
vmImage: "windows-latest"
|
||||
Tests__Database__DatabaseType: LocalDb
|
||||
Tests__Database__SQLServerMasterConnectionString: N/A
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
|
||||
LinuxPart1Of3:
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace.
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
WindowsPart4Of4:
|
||||
vmImage: "windows-latest"
|
||||
Tests__Database__DatabaseType: LocalDb
|
||||
Tests__Database__SQLServerMasterConnectionString: N/A
|
||||
# Filter tests that are part of the ManagementApi namespace.
|
||||
testFilter: "(FullyQualifiedName~ManagementApi)"
|
||||
LinuxPart1Of4:
|
||||
vmImage: "ubuntu-latest"
|
||||
Tests__Database__DatabaseType: SqlServer
|
||||
Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
|
||||
# Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace
|
||||
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)"
|
||||
LinuxPart2Of3:
|
||||
LinuxPart2Of4:
|
||||
vmImage: "ubuntu-latest"
|
||||
Tests__Database__DatabaseType: SqlServer
|
||||
Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
|
||||
# Filter tests that are part of the Umbraco.Infrastructure.Service namespace
|
||||
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
|
||||
LinuxPart3Of3:
|
||||
LinuxPart3Of4:
|
||||
vmImage: "ubuntu-latest"
|
||||
Tests__Database__DatabaseType: SqlServer
|
||||
Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace.
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
LinuxPart4Of4:
|
||||
vmImage: "ubuntu-latest"
|
||||
Tests__Database__DatabaseType: SqlServer
|
||||
Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
|
||||
# Filter tests that are part of the ManagementApi namespace.
|
||||
testFilter: "(FullyQualifiedName~ManagementApi)"
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
steps:
|
||||
@@ -484,7 +505,7 @@ stages:
|
||||
jobs:
|
||||
- job:
|
||||
displayName: E2E Tests with Different App settings (SQL Server)
|
||||
condition: ${{ or(eq(parameters.differentAppSettingsAcceptanceTests, true), eq(parameters.skipDefaultConfigAcceptanceTests, true)) }}
|
||||
condition: ${{ eq(parameters.differentAppSettingsAcceptanceTests, true) }}
|
||||
timeoutInMinutes: 180
|
||||
variables:
|
||||
SA_PASSWORD: UmbracoAcceptance123!
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
parameters:
|
||||
- name: projectName
|
||||
type: string
|
||||
- name: umbracoVersion
|
||||
type: string
|
||||
- name: projects
|
||||
type: object
|
||||
|
||||
jobs:
|
||||
- job: Create_DT_Project
|
||||
displayName: Create Dependency Track Project
|
||||
steps:
|
||||
- checkout: none
|
||||
|
||||
- bash: |
|
||||
project_id=$(curl --no-progress-meter -H "X-Api-Key: $(DT_API_KEY)" "$(DT_API_URL)/v1/project/lookup?name=${{ parameters.projectName }}&version=${{ parameters.umbracoVersion }}" | jq -r '.uuid')
|
||||
if [ "$project_id" != "null" ] && [ -n "$project_id" ]; then
|
||||
echo "Project '${{ parameters.projectName }}' with version '${{ parameters.umbracoVersion }}' already exists (ID: $project_id)."
|
||||
else
|
||||
project_id=$(curl --no-progress-meter \
|
||||
-X PUT "$(DT_API_URL)/v1/project" \
|
||||
-H "X-Api-Key: $(DT_API_KEY)" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "${{ parameters.projectName }}", "version": "${{ parameters.umbracoVersion }}", "collectionLogic": "AGGREGATE_DIRECT_CHILDREN"}' \
|
||||
| jq -r '.uuid')
|
||||
if [ -z "$project_id" ] || [ "$project_id" == "null" ]; then
|
||||
echo "Failed to create project '${{ parameters.projectName }}' version '${{ parameters.umbracoVersion }}'."
|
||||
exit 1
|
||||
fi
|
||||
echo "Created project '${{ parameters.projectName }}' with version '${{ parameters.umbracoVersion }}' (ID: $project_id)."
|
||||
fi
|
||||
displayName: Ensure main project exists in Dependency Track
|
||||
|
||||
- ${{ each project in parameters.projects }}:
|
||||
- job:
|
||||
displayName: Upload ${{ project.name }} BOM
|
||||
dependsOn: Create_DT_Project
|
||||
steps:
|
||||
- checkout: none
|
||||
|
||||
- download: current
|
||||
artifact: ${{ project.artifact }}
|
||||
displayName: Download ${{ project.artifact }} artifact
|
||||
|
||||
- script: |
|
||||
curl --no-progress-meter --fail-with-body \
|
||||
-X POST "$(DT_API_URL)/v1/bom" \
|
||||
-H "X-Api-Key: $(DT_API_KEY)" \
|
||||
-H "Content-Type: multipart/form-data" \
|
||||
-F "autoCreate=true" \
|
||||
-F "projectName=${{ parameters.projectName }}-${{ project.name }}" \
|
||||
-F "projectVersion=${{ parameters.umbracoVersion }}" \
|
||||
-F "parentName=${{ parameters.projectName }}" \
|
||||
-F "parentVersion=${{ parameters.umbracoVersion }}" \
|
||||
-F "bom=@$(Pipeline.Workspace)/${{ project.artifact }}/${{ project.bomFilePath }}"
|
||||
displayName: Upload ${{ project.name }} BOM to Dependency Track
|
||||
@@ -0,0 +1,49 @@
|
||||
parameters:
|
||||
- name: nodeVersion
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
- name: npm_config_cache
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
- name: PlaywrightUserEmail
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
- name: PlaywrightPassword
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
- name: ASPNETCORE_URLS
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
displayName: Use Node.js $(nodeVersion)
|
||||
inputs:
|
||||
versionSpec: $(nodeVersion)
|
||||
|
||||
- pwsh: |
|
||||
"UMBRACO_USER_LOGIN=${{ parameters.PlaywrightUserEmail }}
|
||||
UMBRACO_USER_PASSWORD=${{ parameters.PlaywrightPassword }}
|
||||
URL=${{ parameters.ASPNETCORE_URLS }}
|
||||
STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json
|
||||
CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env
|
||||
displayName: Generate .env
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
|
||||
|
||||
# Cache and restore NPM packages
|
||||
- task: Cache@2
|
||||
displayName: Cache NPM packages
|
||||
inputs:
|
||||
key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json'
|
||||
restoreKeys: |
|
||||
npm_e2e | "$(Agent.OS)"
|
||||
npm_e2e
|
||||
path: ${{ parameters.npm_config_cache }}
|
||||
|
||||
- script: npm ci --no-fund --no-audit --prefer-offline
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
|
||||
displayName: Restore NPM packages
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "9.0.100",
|
||||
"version": "10.0.100",
|
||||
"rollForward": "latestFeature",
|
||||
"allowPrerelease": false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
namespace Umbraco.Cms.Api.Common.Accessors;
|
||||
|
||||
public sealed class RequestContextOutputExpansionStrategyAccessor : RequestContextServiceAccessorBase<IOutputExpansionStrategy>, IOutputExpansionStrategyAccessor
|
||||
{
|
||||
public RequestContextOutputExpansionStrategyAccessor(IHttpContextAccessor httpContextAccessor)
|
||||
: base(httpContextAccessor)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Cms.Api.Common.Accessors;
|
||||
|
||||
public abstract class RequestContextServiceAccessorBase<T>
|
||||
where T : class
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
protected RequestContextServiceAccessorBase(IHttpContextAccessor httpContextAccessor)
|
||||
=> _httpContextAccessor = httpContextAccessor;
|
||||
|
||||
public bool TryGetValue([NotNullWhen(true)] out T? requestStartNodeService)
|
||||
{
|
||||
requestStartNodeService = _httpContextAccessor.HttpContext?.RequestServices.GetService<T>();
|
||||
return requestStartNodeService is not null;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Common.OpenApi;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
using OpenIddict.Server;
|
||||
using OpenIddict.Validation;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Web.Common.Security;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Common.DependencyInjection;
|
||||
|
||||
internal sealed class HideBackOfficeTokensHandler
|
||||
: IOpenIddictServerHandler<OpenIddictServerEvents.ApplyTokenResponseContext>,
|
||||
IOpenIddictServerHandler<OpenIddictServerEvents.ApplyAuthorizationResponseContext>,
|
||||
IOpenIddictServerHandler<OpenIddictServerEvents.ExtractTokenRequestContext>,
|
||||
IOpenIddictValidationHandler<OpenIddictValidationEvents.ProcessAuthenticationContext>,
|
||||
INotificationHandler<UserLogoutSuccessNotification>
|
||||
{
|
||||
private const string RedactedTokenValue = "[redacted]";
|
||||
|
||||
// The __Host- prefix enforces secure cookies at browser level (requires Secure, Path=/, no Domain).
|
||||
// For local development over HTTP, we use a simpler prefix to avoid browser rejection.
|
||||
private const string SecureCookiePrefix = "__Host-";
|
||||
private const string AccessTokenCookieName = "umbAccessToken";
|
||||
private const string RefreshTokenCookieName = "umbRefreshToken";
|
||||
private const string PkceCodeCookieName = "umbPkceCode";
|
||||
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IDataProtectionProvider _dataProtectionProvider;
|
||||
private readonly BackOfficeTokenCookieSettings _backOfficeTokenCookieSettings;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public HideBackOfficeTokensHandler(
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
IOptions<BackOfficeTokenCookieSettings> backOfficeTokenCookieSettings,
|
||||
IOptions<GlobalSettings> globalSettings)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_dataProtectionProvider = dataProtectionProvider;
|
||||
_backOfficeTokenCookieSettings = backOfficeTokenCookieSettings.Value;
|
||||
_globalSettings = globalSettings.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is invoked when tokens (access and refresh tokens) are issued to a client. For the back-office client,
|
||||
/// we will intercept the response, write the tokens from the response into HTTP-only cookies, and redact the
|
||||
/// tokens from the response, so they are not exposed to the client.
|
||||
/// </summary>
|
||||
public ValueTask HandleAsync(OpenIddictServerEvents.ApplyTokenResponseContext context)
|
||||
{
|
||||
if (context.Request?.ClientId is not Constants.OAuthClientIds.BackOffice)
|
||||
{
|
||||
// Only ever handle the back-office client.
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
HttpContext httpContext = GetHttpContext();
|
||||
|
||||
if (context.Response.AccessToken is not null)
|
||||
{
|
||||
SetCookie(httpContext, AccessTokenCookieName, context.Response.AccessToken);
|
||||
context.Response.AccessToken = RedactedTokenValue;
|
||||
}
|
||||
|
||||
if (context.Response.RefreshToken is not null)
|
||||
{
|
||||
SetCookie(httpContext, RefreshTokenCookieName, context.Response.RefreshToken);
|
||||
context.Response.RefreshToken = RedactedTokenValue;
|
||||
}
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is invoked when a PKCE code is issued to the client. For the back-office client, we will intercept the
|
||||
/// response, write the PKCE code from the response into a HTTP-only cookie, and redact the code from the response,
|
||||
/// so it's not exposed to the client.
|
||||
/// </summary>
|
||||
public ValueTask HandleAsync(OpenIddictServerEvents.ApplyAuthorizationResponseContext context)
|
||||
{
|
||||
if (context.Request?.ClientId is not Constants.OAuthClientIds.BackOffice)
|
||||
{
|
||||
// Only ever handle the back-office client.
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
if (context.Response.Code is not null)
|
||||
{
|
||||
SetCookie(GetHttpContext(), PkceCodeCookieName, context.Response.Code);
|
||||
context.Response.Code = RedactedTokenValue;
|
||||
}
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is invoked when requesting new tokens.
|
||||
/// </summary>
|
||||
public ValueTask HandleAsync(OpenIddictServerEvents.ExtractTokenRequestContext context)
|
||||
{
|
||||
if (context.Request?.ClientId != Constants.OAuthClientIds.BackOffice)
|
||||
{
|
||||
// Only ever handle the back-office client.
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
HttpContext httpContext = GetHttpContext();
|
||||
|
||||
// Handle when the PKCE code is being exchanged for an access token.
|
||||
if (context.Request.Code == RedactedTokenValue
|
||||
&& TryGetCookie(httpContext, PkceCodeCookieName, out var code))
|
||||
{
|
||||
context.Request.Code = code;
|
||||
|
||||
// We won't need the PKCE cookie after this, let's remove it.
|
||||
RemoveCookie(httpContext, PkceCodeCookieName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// PCKE codes should always be redacted. If we got here, someone might be trying to pass another PKCE
|
||||
// code. For security reasons, explicitly discard the code (if any) to be on the safe side.
|
||||
context.Request.Code = null;
|
||||
}
|
||||
|
||||
// Handle when a refresh token is being exchanged for a new access token.
|
||||
if (context.Request.RefreshToken == RedactedTokenValue
|
||||
&& TryGetCookie(httpContext, RefreshTokenCookieName, out var refreshToken))
|
||||
{
|
||||
context.Request.RefreshToken = refreshToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we got here, either the refresh token was not redacted, or nothing was found in the refresh token cookie.
|
||||
// If OpenIddict found a refresh token, it could be an old token that is potentially still valid. For security
|
||||
// reasons, we cannot accept that; at this point, we expect the refresh tokens to be explicitly redacted.
|
||||
context.Request.RefreshToken = null;
|
||||
}
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is invoked when extracting the auth context for a client request.
|
||||
/// </summary>
|
||||
public ValueTask HandleAsync(OpenIddictValidationEvents.ProcessAuthenticationContext context)
|
||||
{
|
||||
// For the back-office client, this only happens when an access token is sent to the API.
|
||||
if (context.AccessToken != RedactedTokenValue)
|
||||
{
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
if (TryGetCookie(GetHttpContext(), AccessTokenCookieName, out var accessToken))
|
||||
{
|
||||
context.AccessToken = accessToken;
|
||||
}
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
public void Handle(UserLogoutSuccessNotification notification)
|
||||
{
|
||||
HttpContext? httpContext = _httpContextAccessor.HttpContext;
|
||||
if (httpContext is null)
|
||||
{
|
||||
// For some reason there is no ambient HTTP context, so we can't clean up the cookies.
|
||||
// This is OK, because the tokens in the cookies have already been revoked at user sign-out,
|
||||
// so the cookie clean-up is mostly cosmetic.
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveCookie(httpContext, AccessTokenCookieName);
|
||||
RemoveCookie(httpContext, RefreshTokenCookieName);
|
||||
}
|
||||
|
||||
private HttpContext GetHttpContext()
|
||||
=> _httpContextAccessor.GetRequiredHttpContext();
|
||||
|
||||
private string GetCookieKey(HttpContext httpContext, string cookieName)
|
||||
=> _globalSettings.UseHttps || httpContext.Request.IsHttps
|
||||
? $"{SecureCookiePrefix}{cookieName}"
|
||||
: cookieName;
|
||||
|
||||
private void SetCookie(HttpContext httpContext, string cookieName, string value)
|
||||
{
|
||||
var key = GetCookieKey(httpContext, cookieName);
|
||||
var cookieValue = EncryptionHelper.Encrypt(value, _dataProtectionProvider);
|
||||
|
||||
RemoveCookie(httpContext, cookieName);
|
||||
httpContext.Response.Cookies.Append(key, cookieValue, GetCookieOptions(httpContext));
|
||||
}
|
||||
|
||||
private void RemoveCookie(HttpContext httpContext, string cookieName)
|
||||
{
|
||||
var key = GetCookieKey(httpContext, cookieName);
|
||||
httpContext.Response.Cookies.Delete(key, GetCookieOptions(httpContext));
|
||||
}
|
||||
|
||||
private CookieOptions GetCookieOptions(HttpContext httpContext) =>
|
||||
new()
|
||||
{
|
||||
// Prevent the client-side scripts from accessing the cookie.
|
||||
HttpOnly = true,
|
||||
|
||||
// Mark the cookie as essential to the application, to enforce it despite any
|
||||
// data collection consent options. This aligns with how ASP.NET Core Identity
|
||||
// does when writing cookies for cookie authentication.
|
||||
IsEssential = true,
|
||||
|
||||
// Cookie path must be root for optimal security.
|
||||
Path = "/",
|
||||
|
||||
// For optimal security, the cooke must be secure. However, Umbraco allows for running development
|
||||
// environments over HTTP, so we need to take that into account here.
|
||||
// Thus, we will make the cookie secure if:
|
||||
// - HTTPS is explicitly enabled by config (default for production environments), or
|
||||
// - The current request is over HTTPS (meaning the environment supports it regardless of config).
|
||||
Secure = _globalSettings.UseHttps || httpContext.Request.IsHttps,
|
||||
|
||||
// SameSite is configurable (see BackOfficeTokenCookieSettings for defaults):
|
||||
SameSite = ParseSameSiteMode(_backOfficeTokenCookieSettings.SameSite),
|
||||
};
|
||||
|
||||
private bool TryGetCookie(HttpContext httpContext, string cookieName, [NotNullWhen(true)] out string? value)
|
||||
{
|
||||
var key = GetCookieKey(httpContext, cookieName);
|
||||
if (httpContext.Request.Cookies.TryGetValue(key, out var cookieValue))
|
||||
{
|
||||
value = EncryptionHelper.Decrypt(cookieValue, _dataProtectionProvider);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static SameSiteMode ParseSameSiteMode(string sameSiteMode) =>
|
||||
Enum.TryParse(sameSiteMode, ignoreCase: true, out SameSiteMode result)
|
||||
? result
|
||||
: throw new ArgumentException($"The provided {nameof(sameSiteMode)} value could not be parsed into as SameSiteMode value.", nameof(sameSiteMode));
|
||||
}
|
||||
@@ -9,8 +9,9 @@ using Umbraco.Cms.Api.Common.Security;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Cms.Infrastructure.BackgroundJobs;
|
||||
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
|
||||
namespace Umbraco.Cms.Api.Common.DependencyInjection;
|
||||
|
||||
@@ -113,6 +114,25 @@ public static class UmbracoBuilderAuthExtensions
|
||||
{
|
||||
configuration.UseSingletonHandler<ProcessRequestContextHandler>().SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.ResolveRequestUri.Descriptor.Order - 1);
|
||||
});
|
||||
|
||||
options.AddEventHandler<OpenIddictServerEvents.ApplyTokenResponseContext>(configuration =>
|
||||
{
|
||||
configuration
|
||||
.UseSingletonHandler<HideBackOfficeTokensHandler>()
|
||||
.SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.ProcessJsonResponse<OpenIddictServerEvents.ApplyTokenResponseContext>.Descriptor.Order - 1);
|
||||
});
|
||||
options.AddEventHandler<OpenIddictServerEvents.ApplyAuthorizationResponseContext>(configuration =>
|
||||
{
|
||||
configuration
|
||||
.UseSingletonHandler<HideBackOfficeTokensHandler>()
|
||||
.SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.Authentication.ProcessQueryResponse.Descriptor.Order - 1);
|
||||
});
|
||||
options.AddEventHandler<OpenIddictServerEvents.ExtractTokenRequestContext>(configuration =>
|
||||
{
|
||||
configuration
|
||||
.UseSingletonHandler<HideBackOfficeTokensHandler>()
|
||||
.SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.ExtractPostRequest<OpenIddictServerEvents.ExtractTokenRequestContext>.Descriptor.Order + 1);
|
||||
});
|
||||
})
|
||||
|
||||
// Register the OpenIddict validation components.
|
||||
@@ -137,9 +157,19 @@ public static class UmbracoBuilderAuthExtensions
|
||||
{
|
||||
configuration.UseSingletonHandler<ProcessRequestContextHandler>().SetOrder(OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers.ResolveRequestUri.Descriptor.Order - 1);
|
||||
});
|
||||
|
||||
options.AddEventHandler<OpenIddictValidationEvents.ProcessAuthenticationContext>(configuration =>
|
||||
{
|
||||
configuration
|
||||
.UseSingletonHandler<HideBackOfficeTokensHandler>()
|
||||
// IMPORTANT: the handler must be AFTER the built-in query string handler, because the client-side SignalR library sometimes appends access tokens to the query string.
|
||||
.SetOrder(OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers.ExtractAccessTokenFromQueryString.Descriptor.Order + 1);
|
||||
});
|
||||
});
|
||||
|
||||
builder.Services.AddRecurringBackgroundJob<OpenIddictCleanupJob>();
|
||||
builder.Services.AddSingleton<IDistributedBackgroundJob, OpenIddictCleanupJob>();
|
||||
builder.Services.ConfigureOptions<ConfigureOpenIddict>();
|
||||
|
||||
builder.AddNotificationHandler<UserLogoutSuccessNotification, HideBackOfficeTokensHandler>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ namespace Umbraco.Extensions;
|
||||
public static class MethodInfoApiCommonExtensions
|
||||
{
|
||||
|
||||
public static string? GetMapToApiVersionAttributeValue(this MethodInfo methodInfo)
|
||||
public static string GetMapToApiVersionAttributeValue(this MethodInfo methodInfo)
|
||||
{
|
||||
MapToApiVersionAttribute[] mapToApis = methodInfo.GetCustomAttributes(typeof(MapToApiVersionAttribute), inherit: true).Cast<MapToApiVersionAttribute>().ToArray();
|
||||
|
||||
return string.Join("|", mapToApis.SelectMany(x=>x.Versions));
|
||||
return string.Join("|", mapToApis.SelectMany(x => x.Versions));
|
||||
}
|
||||
|
||||
public static string? GetMapToApiAttributeValue(this MethodInfo methodInfo)
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace Umbraco.Cms.Api.Common.OpenApi;
|
||||
|
||||
public class EnumSchemaFilter : ISchemaFilter
|
||||
{
|
||||
public void Apply(OpenApiSchema model, SchemaFilterContext context)
|
||||
public void Apply(IOpenApiSchema model, SchemaFilterContext context)
|
||||
{
|
||||
if (context.Type.IsEnum)
|
||||
if (model is not OpenApiSchema schema || context.Type.IsEnum is false)
|
||||
{
|
||||
model.Type = "string";
|
||||
model.Format = null;
|
||||
model.Enum.Clear();
|
||||
foreach (var name in Enum.GetNames(context.Type))
|
||||
{
|
||||
var actualName = context.Type.GetField(name)?.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? name;
|
||||
model.Enum.Add(new OpenApiString(actualName));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
schema.Type = JsonSchemaType.String;
|
||||
schema.Format = null;
|
||||
schema.Enum = new List<JsonNode>();
|
||||
foreach (var name in Enum.GetNames(context.Type))
|
||||
{
|
||||
var actualName = context.Type.GetField(name)?.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? name;
|
||||
schema.Enum.Add(actualName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -21,25 +21,32 @@ public class MimeTypeDocumentFilter : IDocumentFilter
|
||||
}
|
||||
|
||||
OpenApiOperation[] operations = swaggerDoc.Paths
|
||||
.SelectMany(path => path.Value.Operations.Values)
|
||||
.SelectMany(path => path.Value.Operations?.Values ?? Enumerable.Empty<OpenApiOperation>())
|
||||
.ToArray();
|
||||
|
||||
void RemoveUnwantedMimeTypes(IDictionary<string, OpenApiMediaType> content)
|
||||
void RemoveUnwantedMimeTypes(IDictionary<string, OpenApiMediaType>? content)
|
||||
{
|
||||
if (content.ContainsKey("application/json"))
|
||||
if (content is null || content.ContainsKey("application/json") is false)
|
||||
{
|
||||
content.RemoveAll(r => r.Key != "application/json");
|
||||
return;
|
||||
}
|
||||
|
||||
content.RemoveAll(r => r.Key != "application/json");
|
||||
}
|
||||
|
||||
OpenApiRequestBody[] requestBodies = operations.Select(operation => operation.RequestBody).WhereNotNull().ToArray();
|
||||
OpenApiRequestBody[] requestBodies = operations
|
||||
.Select(operation => operation.RequestBody)
|
||||
.OfType<OpenApiRequestBody>()
|
||||
.ToArray();
|
||||
foreach (OpenApiRequestBody requestBody in requestBodies)
|
||||
{
|
||||
RemoveUnwantedMimeTypes(requestBody.Content);
|
||||
}
|
||||
|
||||
OpenApiResponse[] responses = operations.SelectMany(operation => operation.Responses.Values).WhereNotNull().ToArray();
|
||||
OpenApiResponse[] responses = operations
|
||||
.SelectMany(operation => operation.Responses?.Values ?? Enumerable.Empty<IOpenApiResponse>())
|
||||
.OfType<OpenApiResponse>()
|
||||
.ToArray();
|
||||
foreach (OpenApiResponse response in responses)
|
||||
{
|
||||
RemoveUnwantedMimeTypes(response.Content);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace Umbraco.Cms.Api.Common.OpenApi;
|
||||
@@ -20,6 +20,6 @@ public class RemoveSecuritySchemesDocumentFilter : IDocumentFilter
|
||||
return;
|
||||
}
|
||||
|
||||
swaggerDoc.Components.SecuritySchemes.Clear();
|
||||
swaggerDoc.Components?.SecuritySchemes?.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,20 +15,6 @@ public class SubTypesSelector : ISubTypesSelector
|
||||
private readonly IEnumerable<ISubTypesHandler> _subTypeHandlers;
|
||||
private readonly IUmbracoJsonTypeInfoResolver _umbracoJsonTypeInfoResolver;
|
||||
|
||||
[Obsolete("The settings parameter is not required anymore, use the other constructor instead. Scheduled for removal in Umbraco 17.")]
|
||||
public SubTypesSelector(
|
||||
IOptions<GlobalSettings> settings,
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IEnumerable<ISubTypesHandler> subTypeHandlers,
|
||||
IUmbracoJsonTypeInfoResolver umbracoJsonTypeInfoResolver)
|
||||
{
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_subTypeHandlers = subTypeHandlers;
|
||||
_umbracoJsonTypeInfoResolver = umbracoJsonTypeInfoResolver;
|
||||
}
|
||||
|
||||
public SubTypesSelector(
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
|
||||
@@ -3,13 +3,12 @@ using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Web.Common.ApplicationBuilder;
|
||||
using Umbraco.Extensions;
|
||||
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;
|
||||
|
||||
namespace Umbraco.Cms.Api.Common.OpenApi;
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Common.Rendering;
|
||||
|
||||
public class ElementOnlyOutputExpansionStrategy : IOutputExpansionStrategy
|
||||
{
|
||||
protected const string All = "$all";
|
||||
protected const string None = "";
|
||||
protected const string ExpandParameterName = "expand";
|
||||
protected const string FieldsParameterName = "fields";
|
||||
|
||||
private readonly IApiPropertyRenderer _propertyRenderer;
|
||||
|
||||
protected Stack<Node?> ExpandProperties { get; } = new();
|
||||
|
||||
protected Stack<Node?> IncludeProperties { get; } = new();
|
||||
|
||||
public ElementOnlyOutputExpansionStrategy(
|
||||
IApiPropertyRenderer propertyRenderer)
|
||||
{
|
||||
_propertyRenderer = propertyRenderer;
|
||||
}
|
||||
|
||||
public virtual IDictionary<string, object?> MapContentProperties(IPublishedContent content)
|
||||
=> content.ItemType == PublishedItemType.Content
|
||||
? MapProperties(content.Properties)
|
||||
: throw new ArgumentException($"Invalid item type. This method can only be used with item type {nameof(PublishedItemType.Content)}, got: {content.ItemType}");
|
||||
|
||||
public virtual IDictionary<string, object?> MapMediaProperties(IPublishedContent media, bool skipUmbracoProperties = true)
|
||||
{
|
||||
if (media.ItemType != PublishedItemType.Media)
|
||||
{
|
||||
throw new ArgumentException($"Invalid item type. This method can only be used with item type {PublishedItemType.Media}, got: {media.ItemType}");
|
||||
}
|
||||
|
||||
IPublishedProperty[] properties = media
|
||||
.Properties
|
||||
.Where(p => skipUmbracoProperties is false || p.Alias.StartsWith("umbraco") is false)
|
||||
.ToArray();
|
||||
|
||||
return properties.Any()
|
||||
? MapProperties(properties)
|
||||
: new Dictionary<string, object?>();
|
||||
}
|
||||
|
||||
public virtual IDictionary<string, object?> MapElementProperties(IPublishedElement element)
|
||||
=> MapProperties(element.Properties, true);
|
||||
|
||||
private IDictionary<string, object?> MapProperties(IEnumerable<IPublishedProperty> properties, bool forceExpandProperties = false)
|
||||
{
|
||||
Node? currentExpandProperties = ExpandProperties.Count > 0 ? ExpandProperties.Peek() : null;
|
||||
if (ExpandProperties.Count > 1 && currentExpandProperties is null && forceExpandProperties is false)
|
||||
{
|
||||
return new Dictionary<string, object?>();
|
||||
}
|
||||
|
||||
Node? currentIncludeProperties = IncludeProperties.Count > 0 ? IncludeProperties.Peek() : null;
|
||||
var result = new Dictionary<string, object?>();
|
||||
foreach (IPublishedProperty property in properties)
|
||||
{
|
||||
Node? nextIncludeProperties = GetNextProperties(currentIncludeProperties, property.Alias);
|
||||
if (currentIncludeProperties is not null && currentIncludeProperties.Items.Any() && nextIncludeProperties is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Node? nextExpandProperties = GetNextProperties(currentExpandProperties, property.Alias);
|
||||
|
||||
IncludeProperties.Push(nextIncludeProperties);
|
||||
ExpandProperties.Push(nextExpandProperties);
|
||||
|
||||
result[property.Alias] = GetPropertyValue(property);
|
||||
|
||||
ExpandProperties.Pop();
|
||||
IncludeProperties.Pop();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Node? GetNextProperties(Node? currentProperties, string propertyAlias)
|
||||
=> currentProperties?.Items.FirstOrDefault(i => i.Key == All)
|
||||
?? currentProperties?.Items.FirstOrDefault(i => i.Key == "properties")?.Items.FirstOrDefault(i => i.Key == All || i.Key == propertyAlias);
|
||||
|
||||
private object? GetPropertyValue(IPublishedProperty property)
|
||||
=> _propertyRenderer.GetPropertyValue(property, ExpandProperties.Peek() is not null);
|
||||
|
||||
protected sealed class Node
|
||||
{
|
||||
public string Key { get; private set; } = string.Empty;
|
||||
|
||||
public List<Node> Items { get; } = new();
|
||||
|
||||
public static Node Parse(string value)
|
||||
{
|
||||
// verify that there are as many start brackets as there are end brackets
|
||||
if (value.CountOccurrences("[") != value.CountOccurrences("]"))
|
||||
{
|
||||
throw new ArgumentException("Value did not contain an equal number of start and end brackets");
|
||||
}
|
||||
|
||||
// verify that the value does not start with a start bracket
|
||||
if (value.StartsWith("["))
|
||||
{
|
||||
throw new ArgumentException("Value cannot start with a bracket");
|
||||
}
|
||||
|
||||
// verify that there are no empty brackets
|
||||
if (value.Contains("[]"))
|
||||
{
|
||||
throw new ArgumentException("Value cannot contain empty brackets");
|
||||
}
|
||||
|
||||
var stack = new Stack<Node>();
|
||||
var root = new Node { Key = "root" };
|
||||
stack.Push(root);
|
||||
|
||||
var currentNode = new Node();
|
||||
root.Items.Add(currentNode);
|
||||
|
||||
foreach (char c in value)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '[': // Start a new node, child of the current node
|
||||
stack.Push(currentNode);
|
||||
currentNode = new Node();
|
||||
stack.Peek().Items.Add(currentNode);
|
||||
break;
|
||||
case ',': // Start a new node, but at the same level of the current node
|
||||
currentNode = new Node();
|
||||
stack.Peek().Items.Add(currentNode);
|
||||
break;
|
||||
case ']': // Back to parent of the current node
|
||||
currentNode = stack.Pop();
|
||||
break;
|
||||
default: // Add char to current node key
|
||||
currentNode.Key += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,6 @@
|
||||
<PackageReference Include="OpenIddict.AspNetCore" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Take top-level depedendency on OpenIddict.AspNetCore depends on a vulnerable version -->
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj" />
|
||||
<ProjectReference Include="..\Umbraco.Web.Common\Umbraco.Web.Common.csproj" />
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Common.OpenApi;
|
||||
using Umbraco.Cms.Api.Delivery.Filters;
|
||||
|
||||
+8
-22
@@ -1,6 +1,6 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Common.Security;
|
||||
using Umbraco.Cms.Api.Delivery.Controllers.Content;
|
||||
@@ -35,23 +35,9 @@ public class ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions :
|
||||
return;
|
||||
}
|
||||
|
||||
operation.Security = new List<OpenApiSecurityRequirement>
|
||||
{
|
||||
new OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = AuthSchemeName,
|
||||
}
|
||||
},
|
||||
[]
|
||||
}
|
||||
}
|
||||
};
|
||||
var schemaRef = new OpenApiSecuritySchemeReference(AuthSchemeName, context.Document);
|
||||
operation.Security ??= new List<OpenApiSecurityRequirement>();
|
||||
operation.Security.Add(new OpenApiSecurityRequirement { [schemaRef] = [] });
|
||||
}
|
||||
|
||||
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
||||
@@ -61,7 +47,7 @@ public class ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions :
|
||||
return;
|
||||
}
|
||||
|
||||
swaggerDoc.Components.SecuritySchemes.Add(
|
||||
swaggerDoc.AddComponent(
|
||||
AuthSchemeName,
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
@@ -74,9 +60,9 @@ public class ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions :
|
||||
AuthorizationCode = new OpenApiOAuthFlow
|
||||
{
|
||||
AuthorizationUrl = new Uri(Paths.MemberApi.AuthorizationEndpoint, UriKind.Relative),
|
||||
TokenUrl = new Uri(Paths.MemberApi.TokenEndpoint, UriKind.Relative)
|
||||
}
|
||||
}
|
||||
TokenUrl = new Uri(Paths.MemberApi.TokenEndpoint, UriKind.Relative),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,28 +35,35 @@ public static class UmbracoBuilderExtensions
|
||||
builder.Services.AddScoped<IRequestStartItemProvider, RequestStartItemProvider>();
|
||||
builder.Services.AddScoped<RequestContextOutputExpansionStrategy>();
|
||||
builder.Services.AddScoped<RequestContextOutputExpansionStrategyV2>();
|
||||
builder.Services.AddScoped<IOutputExpansionStrategy>(provider =>
|
||||
{
|
||||
HttpContext? httpContext = provider.GetRequiredService<IHttpContextAccessor>().HttpContext;
|
||||
ApiVersion? apiVersion = httpContext?.GetRequestedApiVersion();
|
||||
if (apiVersion is null)
|
||||
{
|
||||
return provider.GetRequiredService<RequestContextOutputExpansionStrategyV2>();
|
||||
}
|
||||
|
||||
// V1 of the Delivery API uses a different expansion strategy than V2+
|
||||
return apiVersion.MajorVersion == 1
|
||||
? provider.GetRequiredService<RequestContextOutputExpansionStrategy>()
|
||||
: provider.GetRequiredService<RequestContextOutputExpansionStrategyV2>();
|
||||
});
|
||||
builder.Services.AddUnique<IOutputExpansionStrategy>(
|
||||
provider =>
|
||||
{
|
||||
HttpContext? httpContext = provider.GetRequiredService<IHttpContextAccessor>().HttpContext;
|
||||
ApiVersion? apiVersion = httpContext?.GetRequestedApiVersion();
|
||||
if (apiVersion is null)
|
||||
{
|
||||
return provider.GetRequiredService<RequestContextOutputExpansionStrategyV2>();
|
||||
}
|
||||
|
||||
// V1 of the Delivery API uses a different expansion strategy than V2+
|
||||
return apiVersion.MajorVersion == 1
|
||||
? provider.GetRequiredService<RequestContextOutputExpansionStrategy>()
|
||||
: provider.GetRequiredService<RequestContextOutputExpansionStrategyV2>();
|
||||
},
|
||||
ServiceLifetime.Scoped);
|
||||
|
||||
builder.Services.AddSingleton<IRequestCultureService, RequestCultureService>();
|
||||
builder.Services.AddSingleton<IRequestSegmmentService, RequestSegmentService>();
|
||||
builder.Services.AddSingleton<IRequestSegmentService, RequestSegmentService>();
|
||||
builder.Services.AddSingleton<IRequestRoutingService, RequestRoutingService>();
|
||||
builder.Services.AddSingleton<IRequestRedirectService, RequestRedirectService>();
|
||||
builder.Services.AddSingleton<IRequestPreviewService, RequestPreviewService>();
|
||||
builder.Services.AddSingleton<IOutputExpansionStrategyAccessor, RequestContextOutputExpansionStrategyAccessor>();
|
||||
|
||||
// Webooks register a more basic implementation, remove it.
|
||||
builder.Services.AddUnique<IOutputExpansionStrategyAccessor, RequestContextOutputExpansionStrategyAccessor>(ServiceLifetime.Singleton);
|
||||
builder.Services.AddSingleton<IRequestStartItemProviderAccessor, RequestContextRequestStartItemProviderAccessor>();
|
||||
|
||||
builder.Services.AddSingleton<IApiAccessService, ApiAccessService>();
|
||||
builder.Services.AddSingleton<IApiContentQueryService, ApiContentQueryService>();
|
||||
builder.Services.AddSingleton<IApiContentQueryProvider, ApiContentQueryProvider>();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Delivery.Configuration;
|
||||
using Umbraco.Cms.Api.Delivery.Controllers;
|
||||
using Umbraco.Cms.Api.Delivery.Controllers.Content;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Filters;
|
||||
|
||||
@@ -13,7 +13,7 @@ internal sealed class SwaggerContentDocumentationFilter : SwaggerDocumentationFi
|
||||
|
||||
protected override void ApplyOperation(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
operation.Parameters ??= new List<OpenApiParameter>();
|
||||
operation.Parameters ??= new List<IOpenApiParameter>();
|
||||
|
||||
AddExpand(operation, context);
|
||||
|
||||
@@ -21,50 +21,50 @@ internal sealed class SwaggerContentDocumentationFilter : SwaggerDocumentationFi
|
||||
|
||||
operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
Name = Core.Constants.DeliveryApi.HeaderNames.AcceptLanguage,
|
||||
Name = Constants.DeliveryApi.HeaderNames.AcceptLanguage,
|
||||
In = ParameterLocation.Header,
|
||||
Required = false,
|
||||
Description = "Defines the language to return. Use this when querying language variant content items.",
|
||||
Schema = new OpenApiSchema { Type = "string" },
|
||||
Examples = new Dictionary<string, OpenApiExample>
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
Examples = new Dictionary<string, IOpenApiExample>
|
||||
{
|
||||
{ "Default", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{ "English culture", new OpenApiExample { Value = new OpenApiString("en-us") } }
|
||||
}
|
||||
{ "Default", new OpenApiExample { Value = string.Empty } },
|
||||
{ "English culture", new OpenApiExample { Value = "en-us" } },
|
||||
},
|
||||
});
|
||||
|
||||
operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
Name = Core.Constants.DeliveryApi.HeaderNames.AcceptSegment,
|
||||
Name = Constants.DeliveryApi.HeaderNames.AcceptSegment,
|
||||
In = ParameterLocation.Header,
|
||||
Required = false,
|
||||
Description = "Defines the segment to return. Use this when querying segment variant content items.",
|
||||
Schema = new OpenApiSchema { Type = "string" },
|
||||
Examples = new Dictionary<string, OpenApiExample>
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
Examples = new Dictionary<string, IOpenApiExample>
|
||||
{
|
||||
{ "Default", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{ "Segment One", new OpenApiExample { Value = new OpenApiString("segment-one") } }
|
||||
}
|
||||
{ "Default", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Segment One", new OpenApiExample { Value = "segment-one" } },
|
||||
},
|
||||
});
|
||||
|
||||
AddApiKey(operation);
|
||||
|
||||
operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
Name = Core.Constants.DeliveryApi.HeaderNames.Preview,
|
||||
Name = Constants.DeliveryApi.HeaderNames.Preview,
|
||||
In = ParameterLocation.Header,
|
||||
Required = false,
|
||||
Description = "Whether to request draft content.",
|
||||
Schema = new OpenApiSchema { Type = "boolean" }
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.Boolean },
|
||||
});
|
||||
|
||||
operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
Name = Core.Constants.DeliveryApi.HeaderNames.StartItem,
|
||||
Name = Constants.DeliveryApi.HeaderNames.StartItem,
|
||||
In = ParameterLocation.Header,
|
||||
Required = false,
|
||||
Description = "URL segment or GUID of a root content item.",
|
||||
Schema = new OpenApiSchema { Type = "string" }
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -92,105 +92,36 @@ internal sealed class SwaggerContentDocumentationFilter : SwaggerDocumentationFi
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, OpenApiExample> FetchQueryParameterExamples() =>
|
||||
private Dictionary<string, IOpenApiExample> FetchQueryParameterExamples() =>
|
||||
new()
|
||||
{
|
||||
{ "Select all", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{
|
||||
"Select all ancestors of a node by id",
|
||||
new OpenApiExample { Value = new OpenApiString("ancestors:id") }
|
||||
},
|
||||
{
|
||||
"Select all ancestors of a node by path",
|
||||
new OpenApiExample { Value = new OpenApiString("ancestors:path") }
|
||||
},
|
||||
{
|
||||
"Select all children of a node by id",
|
||||
new OpenApiExample { Value = new OpenApiString("children:id") }
|
||||
},
|
||||
{
|
||||
"Select all children of a node by path",
|
||||
new OpenApiExample { Value = new OpenApiString("children:path") }
|
||||
},
|
||||
{
|
||||
"Select all descendants of a node by id",
|
||||
new OpenApiExample { Value = new OpenApiString("descendants:id") }
|
||||
},
|
||||
{
|
||||
"Select all descendants of a node by path",
|
||||
new OpenApiExample { Value = new OpenApiString("descendants:path") }
|
||||
}
|
||||
{ "Select all", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Select all ancestors of a node by id", new OpenApiExample { Value = "ancestors:id" } },
|
||||
{ "Select all ancestors of a node by path", new OpenApiExample { Value = "ancestors:path" } },
|
||||
{ "Select all children of a node by id", new OpenApiExample { Value = "children:id" } },
|
||||
{ "Select all children of a node by path", new OpenApiExample { Value = "children:path" } },
|
||||
{ "Select all descendants of a node by id", new OpenApiExample { Value = "descendants:id" } },
|
||||
{ "Select all descendants of a node by path", new OpenApiExample { Value = "descendants:path" } },
|
||||
};
|
||||
|
||||
private Dictionary<string, OpenApiExample> FilterQueryParameterExamples() =>
|
||||
private Dictionary<string, IOpenApiExample> FilterQueryParameterExamples() =>
|
||||
new()
|
||||
{
|
||||
{ "Default filter", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{
|
||||
"Filter by content type (equals)",
|
||||
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("contentType:alias1") } }
|
||||
},
|
||||
{
|
||||
"Filter by name (contains)",
|
||||
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("name:nodeName") } }
|
||||
},
|
||||
{
|
||||
"Filter by creation date (less than)",
|
||||
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("createDate<2024-01-01") } }
|
||||
},
|
||||
{
|
||||
"Filter by update date (greater than or equal)",
|
||||
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("updateDate>:2023-01-01") } }
|
||||
}
|
||||
{ "Default filter", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Filter by content type (equals)", new OpenApiExample { Value = new JsonArray { "contentType:alias1" } } },
|
||||
{ "Filter by name (contains)", new OpenApiExample { Value = new JsonArray { "name:nodeName" } } },
|
||||
{ "Filter by creation date (less than)", new OpenApiExample { Value = new JsonArray { "createDate<2024-01-01" } } },
|
||||
{ "Filter by update date (greater than or equal)", new OpenApiExample { Value = new JsonArray { "updateDate>:2023-01-01" } } },
|
||||
};
|
||||
|
||||
private Dictionary<string, OpenApiExample> SortQueryParameterExamples() =>
|
||||
private Dictionary<string, IOpenApiExample> SortQueryParameterExamples() =>
|
||||
new()
|
||||
{
|
||||
{ "Default sort", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{
|
||||
"Sort by create date",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray
|
||||
{
|
||||
new OpenApiString("createDate:asc"), new OpenApiString("createDate:desc")
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by level",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray { new OpenApiString("level:asc"), new OpenApiString("level:desc") }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by name",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray { new OpenApiString("name:asc"), new OpenApiString("name:desc") }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by sort order",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray
|
||||
{
|
||||
new OpenApiString("sortOrder:asc"), new OpenApiString("sortOrder:desc")
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by update date",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray
|
||||
{
|
||||
new OpenApiString("updateDate:asc"), new OpenApiString("updateDate:desc")
|
||||
}
|
||||
}
|
||||
}
|
||||
{ "Default sort", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Sort by create date", new OpenApiExample { Value = new JsonArray { "createDate:asc", "createDate:desc" } } },
|
||||
{ "Sort by level", new OpenApiExample { Value = new JsonArray { "level:asc", "level:desc" } } },
|
||||
{ "Sort by name", new OpenApiExample { Value = new JsonArray { "name:asc", "name:desc" } } },
|
||||
{ "Sort by sort order", new OpenApiExample { Value = new JsonArray { "sortOrder:asc", "sortOrder:desc" } } },
|
||||
{ "Sort by update date", new OpenApiExample { Value = new JsonArray { "updateDate:asc", "updateDate:desc" } } },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Filters;
|
||||
|
||||
@@ -9,6 +9,8 @@ internal abstract class SwaggerDocumentationFilterBase<TBaseController>
|
||||
: SwaggerFilterBase<TBaseController>, IOperationFilter, IParameterFilter
|
||||
where TBaseController : Controller
|
||||
{
|
||||
protected abstract string DocumentationLink { get; }
|
||||
|
||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
if (CanApply(context))
|
||||
@@ -17,21 +19,19 @@ internal abstract class SwaggerDocumentationFilterBase<TBaseController>
|
||||
}
|
||||
}
|
||||
|
||||
public void Apply(OpenApiParameter parameter, ParameterFilterContext context)
|
||||
public void Apply(IOpenApiParameter parameter, ParameterFilterContext context)
|
||||
{
|
||||
if (CanApply(context))
|
||||
if (CanApply(context) && parameter is OpenApiParameter openApiParameter)
|
||||
{
|
||||
ApplyParameter(parameter, context);
|
||||
ApplyParameter(openApiParameter, context);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract string DocumentationLink { get; }
|
||||
|
||||
protected abstract void ApplyOperation(OpenApiOperation operation, OperationFilterContext context);
|
||||
|
||||
protected abstract void ApplyParameter(OpenApiParameter parameter, ParameterFilterContext context);
|
||||
|
||||
protected void AddQueryParameterDocumentation(OpenApiParameter parameter, Dictionary<string, OpenApiExample> examples, string description)
|
||||
protected void AddQueryParameterDocumentation(OpenApiParameter parameter, Dictionary<string, IOpenApiExample> examples, string description)
|
||||
{
|
||||
parameter.Description = QueryParameterDescription(description);
|
||||
parameter.Examples = examples;
|
||||
@@ -60,15 +60,19 @@ internal abstract class SwaggerDocumentationFilterBase<TBaseController>
|
||||
AddFields(operation);
|
||||
}
|
||||
|
||||
protected void AddApiKey(OpenApiOperation operation) =>
|
||||
operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
Name = Core.Constants.DeliveryApi.HeaderNames.ApiKey,
|
||||
In = ParameterLocation.Header,
|
||||
Required = false,
|
||||
Description = "API key specified through configuration to authorize access to the API.",
|
||||
Schema = new OpenApiSchema { Type = "string" }
|
||||
});
|
||||
protected void AddApiKey(OpenApiOperation operation)
|
||||
{
|
||||
operation.Parameters ??= new List<IOpenApiParameter>();
|
||||
operation.Parameters.Add(
|
||||
new OpenApiParameter
|
||||
{
|
||||
Name = Constants.DeliveryApi.HeaderNames.ApiKey,
|
||||
In = ParameterLocation.Header,
|
||||
Required = false,
|
||||
Description = "API key specified through configuration to authorize access to the API.",
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
});
|
||||
}
|
||||
|
||||
protected string PaginationDescription(bool skip, string itemType)
|
||||
=> $"Specifies the number of found {itemType} items to {(skip ? "skip" : "take")}. Use this to control pagination of the response.";
|
||||
@@ -82,78 +86,70 @@ internal abstract class SwaggerDocumentationFilterBase<TBaseController>
|
||||
|
||||
// FIXME: remove this when Delivery API V1 has been removed (expectedly in V15)
|
||||
private void AddExpandV1(OpenApiOperation operation)
|
||||
=> operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
Name = "expand",
|
||||
In = ParameterLocation.Query,
|
||||
Required = false,
|
||||
Description = QueryParameterDescription("Defines the properties that should be expanded in the response"),
|
||||
Schema = new OpenApiSchema { Type = "string" },
|
||||
Examples = new Dictionary<string, OpenApiExample>
|
||||
{
|
||||
operation.Parameters ??= new List<IOpenApiParameter>();
|
||||
operation.Parameters.Add(
|
||||
new OpenApiParameter
|
||||
{
|
||||
{ "Expand none", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{ "Expand all", new OpenApiExample { Value = new OpenApiString("all") } },
|
||||
Name = "expand",
|
||||
In = ParameterLocation.Query,
|
||||
Required = false,
|
||||
Description =
|
||||
QueryParameterDescription("Defines the properties that should be expanded in the response"),
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
Examples = new Dictionary<string, IOpenApiExample>
|
||||
{
|
||||
"Expand specific property",
|
||||
new OpenApiExample { Value = new OpenApiString("property:alias1") }
|
||||
{ "Expand none", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Expand all", new OpenApiExample { Value = "all" } },
|
||||
{ "Expand specific property", new OpenApiExample { Value = "property:alias1" } },
|
||||
{ "Expand specific properties", new OpenApiExample { Value = "property:alias1,alias2" } },
|
||||
},
|
||||
{
|
||||
"Expand specific properties",
|
||||
new OpenApiExample { Value = new OpenApiString("property:alias1,alias2") }
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void AddExpand(OpenApiOperation operation)
|
||||
=> operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
Name = "expand",
|
||||
In = ParameterLocation.Query,
|
||||
Required = false,
|
||||
Description = QueryParameterDescription("Defines the properties that should be expanded in the response"),
|
||||
Schema = new OpenApiSchema { Type = "string" },
|
||||
Examples = new Dictionary<string, OpenApiExample>
|
||||
{
|
||||
operation.Parameters ??= new List<IOpenApiParameter>();
|
||||
operation.Parameters.Add(
|
||||
new OpenApiParameter
|
||||
{
|
||||
{ "Expand none", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{ "Expand all properties", new OpenApiExample { Value = new OpenApiString("properties[$all]") } },
|
||||
Name = "expand",
|
||||
In = ParameterLocation.Query,
|
||||
Required = false,
|
||||
Description =
|
||||
QueryParameterDescription("Defines the properties that should be expanded in the response"),
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
Examples = new Dictionary<string, IOpenApiExample>
|
||||
{
|
||||
"Expand specific property",
|
||||
new OpenApiExample { Value = new OpenApiString("properties[alias1]") }
|
||||
{ "Expand none", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Expand all properties", new OpenApiExample { Value = "properties[$all]" } },
|
||||
{ "Expand specific property", new OpenApiExample { Value = "properties[alias1]" } },
|
||||
{ "Expand specific properties", new OpenApiExample { Value = "properties[alias1,alias2]" } },
|
||||
{ "Expand nested properties", new OpenApiExample { Value = "properties[alias1[properties[nestedAlias1,nestedAlias2]]]" } },
|
||||
},
|
||||
{
|
||||
"Expand specific properties",
|
||||
new OpenApiExample { Value = new OpenApiString("properties[alias1,alias2]") }
|
||||
},
|
||||
{
|
||||
"Expand nested properties",
|
||||
new OpenApiExample { Value = new OpenApiString("properties[alias1[properties[nestedAlias1,nestedAlias2]]]") }
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void AddFields(OpenApiOperation operation)
|
||||
=> operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
Name = "fields",
|
||||
In = ParameterLocation.Query,
|
||||
Required = false,
|
||||
Description = QueryParameterDescription("Explicitly defines which properties should be included in the response (by default all properties are included)"),
|
||||
Schema = new OpenApiSchema { Type = "string" },
|
||||
Examples = new Dictionary<string, OpenApiExample>
|
||||
{
|
||||
operation.Parameters ??= new List<IOpenApiParameter>();
|
||||
operation.Parameters.Add(
|
||||
new OpenApiParameter
|
||||
{
|
||||
{ "Include all properties", new OpenApiExample { Value = new OpenApiString("properties[$all]") } },
|
||||
Name = "fields",
|
||||
In = ParameterLocation.Query,
|
||||
Required = false,
|
||||
Description =
|
||||
QueryParameterDescription(
|
||||
"Explicitly defines which properties should be included in the response (by default all properties are included)"),
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
Examples = new Dictionary<string, IOpenApiExample>
|
||||
{
|
||||
"Include only specific property",
|
||||
new OpenApiExample { Value = new OpenApiString("properties[alias1]") }
|
||||
{ "Include all properties", new OpenApiExample { Value = "properties[$all]" } },
|
||||
{ "Include only specific property", new OpenApiExample { Value = "properties[alias1]" } },
|
||||
{ "Include only specific properties", new OpenApiExample { Value = "properties[alias1,alias2]" } },
|
||||
{ "Include only specific nested properties", new OpenApiExample { Value = "properties[alias1[properties[nestedAlias1,nestedAlias2]]]" } },
|
||||
},
|
||||
{
|
||||
"Include only specific properties",
|
||||
new OpenApiExample { Value = new OpenApiString("properties[alias1,alias2]") }
|
||||
},
|
||||
{
|
||||
"Include only specific nested properties",
|
||||
new OpenApiExample { Value = new OpenApiString("properties[alias1[properties[nestedAlias1,nestedAlias2]]]") }
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Delivery.Configuration;
|
||||
using Umbraco.Cms.Api.Delivery.Controllers;
|
||||
using Umbraco.Cms.Api.Delivery.Controllers.Media;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Filters;
|
||||
@@ -13,7 +12,7 @@ internal sealed class SwaggerMediaDocumentationFilter : SwaggerDocumentationFilt
|
||||
|
||||
protected override void ApplyOperation(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
operation.Parameters ??= new List<OpenApiParameter>();
|
||||
operation.Parameters ??= new List<IOpenApiParameter>();
|
||||
|
||||
AddExpand(operation, context);
|
||||
|
||||
@@ -46,77 +45,29 @@ internal sealed class SwaggerMediaDocumentationFilter : SwaggerDocumentationFilt
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, OpenApiExample> FetchQueryParameterExamples() =>
|
||||
private Dictionary<string, IOpenApiExample> FetchQueryParameterExamples() =>
|
||||
new()
|
||||
{
|
||||
{
|
||||
"Select all children at root level",
|
||||
new OpenApiExample { Value = new OpenApiString("children:/") }
|
||||
},
|
||||
{
|
||||
"Select all children of a media item by id",
|
||||
new OpenApiExample { Value = new OpenApiString("children:id") }
|
||||
},
|
||||
{
|
||||
"Select all children of a media item by path",
|
||||
new OpenApiExample { Value = new OpenApiString("children:path") }
|
||||
}
|
||||
{ "Select all children at root level", new OpenApiExample { Value = "children:/" } },
|
||||
{ "Select all children of a media item by id", new OpenApiExample { Value = "children:id" } },
|
||||
{ "Select all children of a media item by path", new OpenApiExample { Value = "children:path" } },
|
||||
};
|
||||
|
||||
private Dictionary<string, OpenApiExample> FilterQueryParameterExamples() =>
|
||||
private Dictionary<string, IOpenApiExample> FilterQueryParameterExamples() =>
|
||||
new()
|
||||
{
|
||||
{ "Default filter", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{
|
||||
"Filter by media type",
|
||||
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("mediaType:alias1") } }
|
||||
},
|
||||
{
|
||||
"Filter by name",
|
||||
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("name:nodeName") } }
|
||||
}
|
||||
{ "Default filter", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Filter by media type", new OpenApiExample { Value = new JsonArray { "mediaType:alias1" } } },
|
||||
{ "Filter by name", new OpenApiExample { Value = new JsonArray { "name:nodeName" } } },
|
||||
};
|
||||
|
||||
private Dictionary<string, OpenApiExample> SortQueryParameterExamples() =>
|
||||
private Dictionary<string, IOpenApiExample> SortQueryParameterExamples() =>
|
||||
new()
|
||||
{
|
||||
{ "Default sort", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{
|
||||
"Sort by create date",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray
|
||||
{
|
||||
new OpenApiString("createDate:asc"), new OpenApiString("createDate:desc")
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by name",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray { new OpenApiString("name:asc"), new OpenApiString("name:desc") }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by sort order",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray
|
||||
{
|
||||
new OpenApiString("sortOrder:asc"), new OpenApiString("sortOrder:desc")
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by update date",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray
|
||||
{
|
||||
new OpenApiString("updateDate:asc"), new OpenApiString("updateDate:desc")
|
||||
}
|
||||
}
|
||||
}
|
||||
{ "Default sort", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Sort by create date", new OpenApiExample { Value = new JsonArray { "createDate:asc", "createDate:desc" } } },
|
||||
{ "Sort by name", new OpenApiExample { Value = new JsonArray { "name:asc", "name:desc" } } },
|
||||
{ "Sort by sort order", new OpenApiExample { Value = new JsonArray { "sortOrder:asc", "sortOrder:desc" } } },
|
||||
{ "Sort by update date", new OpenApiExample { Value = new JsonArray { "updateDate:asc", "updateDate:desc" } } },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Querying;
|
||||
@@ -14,30 +11,6 @@ public abstract class QueryOptionBase
|
||||
private readonly IApiDocumentUrlService _apiDocumentUrlService;
|
||||
private readonly IVariationContextAccessor _variationContextAccessor;
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
|
||||
public QueryOptionBase(
|
||||
IPublishedContentCache publishedContentCache,
|
||||
IRequestRoutingService requestRoutingService)
|
||||
: this(
|
||||
requestRoutingService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IRequestPreviewService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IApiDocumentUrlService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>())
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
|
||||
public QueryOptionBase(
|
||||
IPublishedContentCache publishedContentCache,
|
||||
IRequestRoutingService requestRoutingService,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
IRequestCultureService requestCultureService,
|
||||
IApiDocumentUrlService apiDocumentUrlService,
|
||||
IVariationContextAccessor variationContextAccessor)
|
||||
: this(requestRoutingService, requestPreviewService, apiDocumentUrlService, variationContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
public QueryOptionBase(
|
||||
IRequestRoutingService requestRoutingService,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Delivery.Indexing.Selectors;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Services.Navigation;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Querying.Selectors;
|
||||
@@ -13,46 +10,6 @@ public sealed class AncestorsSelector : QueryOptionBase, ISelectorHandler
|
||||
private readonly IDocumentNavigationQueryService _navigationQueryService;
|
||||
private const string AncestorsSpecifier = "ancestors:";
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
|
||||
public AncestorsSelector(
|
||||
IPublishedContentCache publishedContentCache,
|
||||
IRequestRoutingService requestRoutingService,
|
||||
IDocumentNavigationQueryService navigationQueryService,
|
||||
IRequestPreviewService requestPreviewService)
|
||||
: this(
|
||||
requestRoutingService,
|
||||
requestPreviewService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IApiDocumentUrlService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>(),
|
||||
navigationQueryService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
|
||||
public AncestorsSelector(
|
||||
IPublishedContentCache publishedContentCache,
|
||||
IRequestRoutingService requestRoutingService,
|
||||
IDocumentNavigationQueryService navigationQueryService)
|
||||
: this(
|
||||
requestRoutingService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IRequestPreviewService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IApiDocumentUrlService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>(),
|
||||
navigationQueryService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Use the constructor that takes all parameters. Scheduled for removal in V17.")]
|
||||
public AncestorsSelector(IPublishedContentCache publishedContentCache, IRequestRoutingService requestRoutingService)
|
||||
: this(
|
||||
requestRoutingService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IRequestPreviewService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IApiDocumentUrlService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>())
|
||||
{
|
||||
}
|
||||
|
||||
public AncestorsSelector(
|
||||
IRequestRoutingService requestRoutingService,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
@@ -62,18 +19,6 @@ public sealed class AncestorsSelector : QueryOptionBase, ISelectorHandler
|
||||
: base(requestRoutingService, requestPreviewService, apiDocumentUrlService, variationContextAccessor)
|
||||
=> _navigationQueryService = navigationQueryService;
|
||||
|
||||
[Obsolete("Use the constructor that takes all parameters. Scheduled for removal in V17.")]
|
||||
public AncestorsSelector(
|
||||
IRequestRoutingService requestRoutingService,
|
||||
IPublishedContentCache publishedContentCache,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
IApiDocumentUrlService apiDocumentUrlService,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
IDocumentNavigationQueryService navigationQueryService)
|
||||
: this(requestRoutingService, requestPreviewService, apiDocumentUrlService, variationContextAccessor, navigationQueryService)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanHandle(string query)
|
||||
=> query.StartsWith(AncestorsSpecifier, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
@@ -12,27 +12,6 @@ public sealed class ChildrenSelector : QueryOptionBase, ISelectorHandler
|
||||
{
|
||||
private const string ChildrenSpecifier = "children:";
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
|
||||
public ChildrenSelector(IPublishedContentCache publishedContentCache, IRequestRoutingService requestRoutingService)
|
||||
: this(
|
||||
requestRoutingService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IRequestPreviewService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IApiDocumentUrlService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>())
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
|
||||
public ChildrenSelector(
|
||||
IPublishedContentCache publishedContentCache,
|
||||
IRequestRoutingService requestRoutingService,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
IApiDocumentUrlService apiDocumentUrlService,
|
||||
IVariationContextAccessor variationContextAccessor)
|
||||
: this(requestRoutingService, requestPreviewService, apiDocumentUrlService, variationContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
public ChildrenSelector(
|
||||
IRequestRoutingService requestRoutingService,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
|
||||
@@ -12,27 +12,6 @@ public sealed class DescendantsSelector : QueryOptionBase, ISelectorHandler
|
||||
{
|
||||
private const string DescendantsSpecifier = "descendants:";
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
|
||||
public DescendantsSelector(IPublishedContentCache publishedContentCache, IRequestRoutingService requestRoutingService)
|
||||
: this(
|
||||
requestRoutingService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IRequestPreviewService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IApiDocumentUrlService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>())
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
|
||||
public DescendantsSelector(
|
||||
IPublishedContentCache publishedContentCache,
|
||||
IRequestRoutingService requestRoutingService,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
IApiDocumentUrlService apiDocumentUrlService,
|
||||
IVariationContextAccessor variationContextAccessor)
|
||||
: this(requestRoutingService, requestPreviewService, apiDocumentUrlService, variationContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
public DescendantsSelector(
|
||||
IRequestRoutingService requestRoutingService,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
|
||||
@@ -1,62 +1,25 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Api.Common.Rendering;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Rendering;
|
||||
|
||||
internal sealed class RequestContextOutputExpansionStrategyV2 : IOutputExpansionStrategy
|
||||
internal sealed class RequestContextOutputExpansionStrategyV2 : ElementOnlyOutputExpansionStrategy, IOutputExpansionStrategy
|
||||
{
|
||||
private const string All = "$all";
|
||||
private const string None = "";
|
||||
private const string ExpandParameterName = "expand";
|
||||
private const string FieldsParameterName = "fields";
|
||||
|
||||
private readonly IApiPropertyRenderer _propertyRenderer;
|
||||
private readonly ILogger<RequestContextOutputExpansionStrategyV2> _logger;
|
||||
|
||||
private readonly Stack<Node?> _expandProperties;
|
||||
private readonly Stack<Node?> _includeProperties;
|
||||
|
||||
public RequestContextOutputExpansionStrategyV2(
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IApiPropertyRenderer propertyRenderer,
|
||||
ILogger<RequestContextOutputExpansionStrategyV2> logger)
|
||||
: base(propertyRenderer)
|
||||
{
|
||||
_propertyRenderer = propertyRenderer;
|
||||
_logger = logger;
|
||||
_expandProperties = new Stack<Node?>();
|
||||
_includeProperties = new Stack<Node?>();
|
||||
|
||||
InitializeExpandAndInclude(httpContextAccessor);
|
||||
}
|
||||
|
||||
public IDictionary<string, object?> MapContentProperties(IPublishedContent content)
|
||||
=> content.ItemType == PublishedItemType.Content
|
||||
? MapProperties(content.Properties)
|
||||
: throw new ArgumentException($"Invalid item type. This method can only be used with item type {nameof(PublishedItemType.Content)}, got: {content.ItemType}");
|
||||
|
||||
public IDictionary<string, object?> MapMediaProperties(IPublishedContent media, bool skipUmbracoProperties = true)
|
||||
{
|
||||
if (media.ItemType != PublishedItemType.Media)
|
||||
{
|
||||
throw new ArgumentException($"Invalid item type. This method can only be used with item type {PublishedItemType.Media}, got: {media.ItemType}");
|
||||
}
|
||||
|
||||
IPublishedProperty[] properties = media
|
||||
.Properties
|
||||
.Where(p => skipUmbracoProperties is false || p.Alias.StartsWith("umbraco") is false)
|
||||
.ToArray();
|
||||
|
||||
return properties.Any()
|
||||
? MapProperties(properties)
|
||||
: new Dictionary<string, object?>();
|
||||
}
|
||||
|
||||
public IDictionary<string, object?> MapElementProperties(IPublishedElement element)
|
||||
=> MapProperties(element.Properties, true);
|
||||
|
||||
private void InitializeExpandAndInclude(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
string? QueryValue(string key) => httpContextAccessor.HttpContext?.Request.Query[key];
|
||||
@@ -66,7 +29,7 @@ internal sealed class RequestContextOutputExpansionStrategyV2 : IOutputExpansion
|
||||
|
||||
try
|
||||
{
|
||||
_expandProperties.Push(Node.Parse(toExpand));
|
||||
ExpandProperties.Push(Node.Parse(toExpand));
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
@@ -76,7 +39,7 @@ internal sealed class RequestContextOutputExpansionStrategyV2 : IOutputExpansion
|
||||
|
||||
try
|
||||
{
|
||||
_includeProperties.Push(Node.Parse(toInclude));
|
||||
IncludeProperties.Push(Node.Parse(toInclude));
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
@@ -84,102 +47,4 @@ internal sealed class RequestContextOutputExpansionStrategyV2 : IOutputExpansion
|
||||
throw new ArgumentException($"Could not parse the '{FieldsParameterName}' parameter: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private IDictionary<string, object?> MapProperties(IEnumerable<IPublishedProperty> properties, bool forceExpandProperties = false)
|
||||
{
|
||||
Node? currentExpandProperties = _expandProperties.Peek();
|
||||
if (_expandProperties.Count > 1 && currentExpandProperties is null && forceExpandProperties is false)
|
||||
{
|
||||
return new Dictionary<string, object?>();
|
||||
}
|
||||
|
||||
Node? currentIncludeProperties = _includeProperties.Peek();
|
||||
var result = new Dictionary<string, object?>();
|
||||
foreach (IPublishedProperty property in properties)
|
||||
{
|
||||
Node? nextIncludeProperties = GetNextProperties(currentIncludeProperties, property.Alias);
|
||||
if (currentIncludeProperties is not null && currentIncludeProperties.Items.Any() && nextIncludeProperties is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Node? nextExpandProperties = GetNextProperties(currentExpandProperties, property.Alias);
|
||||
|
||||
_includeProperties.Push(nextIncludeProperties);
|
||||
_expandProperties.Push(nextExpandProperties);
|
||||
|
||||
result[property.Alias] = GetPropertyValue(property);
|
||||
|
||||
_expandProperties.Pop();
|
||||
_includeProperties.Pop();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Node? GetNextProperties(Node? currentProperties, string propertyAlias)
|
||||
=> currentProperties?.Items.FirstOrDefault(i => i.Key == All)
|
||||
?? currentProperties?.Items.FirstOrDefault(i => i.Key == "properties")?.Items.FirstOrDefault(i => i.Key == All || i.Key == propertyAlias);
|
||||
|
||||
private object? GetPropertyValue(IPublishedProperty property)
|
||||
=> _propertyRenderer.GetPropertyValue(property, _expandProperties.Peek() is not null);
|
||||
|
||||
private sealed class Node
|
||||
{
|
||||
public string Key { get; private set; } = string.Empty;
|
||||
|
||||
public List<Node> Items { get; } = new();
|
||||
|
||||
public static Node Parse(string value)
|
||||
{
|
||||
// verify that there are as many start brackets as there are end brackets
|
||||
if (value.CountOccurrences("[") != value.CountOccurrences("]"))
|
||||
{
|
||||
throw new ArgumentException("Value did not contain an equal number of start and end brackets");
|
||||
}
|
||||
|
||||
// verify that the value does not start with a start bracket
|
||||
if (value.StartsWith("["))
|
||||
{
|
||||
throw new ArgumentException("Value cannot start with a bracket");
|
||||
}
|
||||
|
||||
// verify that there are no empty brackets
|
||||
if (value.Contains("[]"))
|
||||
{
|
||||
throw new ArgumentException("Value cannot contain empty brackets");
|
||||
}
|
||||
|
||||
var stack = new Stack<Node>();
|
||||
var root = new Node { Key = "root" };
|
||||
stack.Push(root);
|
||||
|
||||
var currentNode = new Node();
|
||||
root.Items.Add(currentNode);
|
||||
|
||||
foreach (char c in value)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '[': // Start a new node, child of the current node
|
||||
stack.Push(currentNode);
|
||||
currentNode = new Node();
|
||||
stack.Peek().Items.Add(currentNode);
|
||||
break;
|
||||
case ',': // Start a new node, but at the same level of the current node
|
||||
currentNode = new Node();
|
||||
stack.Peek().Items.Add(currentNode);
|
||||
break;
|
||||
case ']': // Back to parent of the current node
|
||||
currentNode = stack.Pop();
|
||||
break;
|
||||
default: // Add char to current node key
|
||||
currentNode.Key += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,9 +66,14 @@ internal sealed class DeliveryApiItemsEndpointsMatcherPolicy : MatcherPolicy, IE
|
||||
{
|
||||
ApiVersion[]? supportedApiVersions = endpoint.Metadata.GetMetadata<MapToApiVersionAttribute>()?.Versions.ToArray();
|
||||
|
||||
// if the endpoint is versioned, the requested API version must be among the API versions supported by the endpoint.
|
||||
// if the endpoint is NOT versioned, it cannot be used with a requested API version
|
||||
return supportedApiVersions?.Contains(requestedApiVersion) ?? requestedApiVersion is null;
|
||||
// If the endpoint is versioned, the requested API version must be among the API versions supported by the endpoint.
|
||||
// If the endpoint is NOT versioned, it cannot be used with a requested API version.
|
||||
if (supportedApiVersions is not null && requestedApiVersion is not null)
|
||||
{
|
||||
return supportedApiVersions.Contains(requestedApiVersion);
|
||||
}
|
||||
|
||||
return requestedApiVersion is null;
|
||||
}
|
||||
|
||||
private static bool IsByIdsController(ControllerActionDescriptor? controllerActionDescriptor)
|
||||
|
||||
@@ -66,7 +66,7 @@ internal sealed class ApiMediaQueryService : IApiMediaQueryService
|
||||
private IPublishedContent? TryGetByPath(string path, IPublishedMediaCache mediaCache)
|
||||
{
|
||||
var segments = path.Split(Constants.CharArrays.ForwardSlash, StringSplitOptions.RemoveEmptyEntries);
|
||||
IEnumerable<IPublishedContent> currentChildren = mediaCache.GetAtRoot();
|
||||
IEnumerable<IPublishedContent> currentChildren = GetRootContent(mediaCache);
|
||||
IPublishedContent? resolvedMedia = null;
|
||||
|
||||
foreach (var segment in segments)
|
||||
@@ -103,7 +103,7 @@ internal sealed class ApiMediaQueryService : IApiMediaQueryService
|
||||
IPublishedMediaCache mediaCache = GetRequiredPublishedMediaCache();
|
||||
if (childrenOf.Trim(Constants.CharArrays.ForwardSlash).Length == 0)
|
||||
{
|
||||
return mediaCache.GetAtRoot();
|
||||
return GetRootContent(mediaCache);
|
||||
}
|
||||
|
||||
IPublishedContent? parent = Guid.TryParse(childrenOf, out Guid parentKey)
|
||||
@@ -196,4 +196,8 @@ internal sealed class ApiMediaQueryService : IApiMediaQueryService
|
||||
|
||||
return Attempt.SucceedWithStatus(ApiMediaQueryOperationStatus.Success, result);
|
||||
}
|
||||
|
||||
private IEnumerable<IPublishedContent> GetRootContent(IPublishedMediaCache mediaCache)
|
||||
=> _mediaNavigationQueryService.TryGetRootKeys(out IEnumerable<Guid> rootKeys) is false ? []
|
||||
: rootKeys.Select(x => mediaCache.GetById(false, x)).WhereNotNull();
|
||||
}
|
||||
|
||||
@@ -19,12 +19,6 @@ internal sealed partial class RequestCultureService : RequestHeaderHandler, IReq
|
||||
return ValidLanguageHeaderRegex().IsMatch(acceptLanguage) ? acceptLanguage : null;
|
||||
}
|
||||
|
||||
[Obsolete("Use IVariationContextAccessor to manipulate the variation context. Scheduled for removal in V17.")]
|
||||
public void SetRequestCulture(string culture)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
// at the time of writing we're introducing this to get rid of accept-language header values like "en-GB,en-US;q=0.9,en;q=0.8",
|
||||
// so we don't want to be too restrictive in this regex - keep it simple for now.
|
||||
[GeneratedRegex(@"^[\w-]*$")]
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<Title>Umbraco CMS - Delivery API</Title>
|
||||
<Description>Contains the presentation layer for the Umbraco CMS Delivery API.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!--
|
||||
TODO: Fix and remove overrides:
|
||||
@@ -11,11 +12,18 @@
|
||||
-->
|
||||
<WarningsNotAsErrors>$(WarningsNotAsErrors),ASP0019,CS0618,CS0612</WarningsNotAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Umbraco.Cms.Api.Common\Umbraco.Cms.Api.Common.csproj" />
|
||||
<ProjectReference Include="..\Umbraco.Web.Common\Umbraco.Web.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Linq.Async">
|
||||
<ExcludeAssets>compile</ExcludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Umbraco.Tests.UnitTests</_Parameter1>
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Umbraco.Cms.Api.Management.Security;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
@@ -219,10 +220,44 @@ public class ConfigureBackOfficeCookieOptions : IConfigureNamedOptions<CookieAut
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
},
|
||||
// FIXME: We want to change this over to using an attribute on the backoffice controllers
|
||||
// See this for more: https://github.com/dotnet/aspnetcore/issues/63093#issuecomment-3201530217
|
||||
OnRedirectToLogin = context =>
|
||||
{
|
||||
if (IsXhr(context.Request))
|
||||
{
|
||||
context.Response.Headers.Location = context.RedirectUri;
|
||||
context.Response.StatusCode = 401;
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.Redirect(context.RedirectUri);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
},
|
||||
OnRedirectToAccessDenied = context =>
|
||||
{
|
||||
if (IsXhr(context.Request))
|
||||
{
|
||||
context.Response.Headers.Location = context.RedirectUri;
|
||||
context.Response.StatusCode = 403;
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.Redirect(context.RedirectUri);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private bool IsXhr(HttpRequest request) =>
|
||||
string.Equals(request.Query[HeaderNames.XRequestedWith], "XMLHttpRequest", StringComparison.Ordinal) ||
|
||||
string.Equals(request.Headers.XRequestedWith, "XMLHttpRequest", StringComparison.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the ticket is renewed if the <see cref="SecuritySettings.KeepUserLoggedIn" /> is set to true
|
||||
/// and the current request is for the get user seconds endpoint
|
||||
|
||||
+7
-8
@@ -1,8 +1,8 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Common.OpenApi;
|
||||
using Umbraco.Cms.Api.Common.Security;
|
||||
using Umbraco.Cms.Api.Common.Serialization;
|
||||
using Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.OpenApi;
|
||||
@@ -11,7 +11,7 @@ namespace Umbraco.Cms.Api.Management.Configuration;
|
||||
|
||||
public class ConfigureUmbracoManagementApiSwaggerGenOptions : IConfigureOptions<SwaggerGenOptions>
|
||||
{
|
||||
private IUmbracoJsonTypeInfoResolver _umbracoJsonTypeInfoResolver;
|
||||
private readonly IUmbracoJsonTypeInfoResolver _umbracoJsonTypeInfoResolver;
|
||||
|
||||
public ConfigureUmbracoManagementApiSwaggerGenOptions(IUmbracoJsonTypeInfoResolver umbracoJsonTypeInfoResolver)
|
||||
{
|
||||
@@ -20,7 +20,6 @@ public class ConfigureUmbracoManagementApiSwaggerGenOptions : IConfigureOptions<
|
||||
|
||||
public void Configure(SwaggerGenOptions swaggerGenOptions)
|
||||
{
|
||||
|
||||
swaggerGenOptions.SwaggerDoc(
|
||||
ManagementApiConfiguration.ApiName,
|
||||
new OpenApiInfo
|
||||
@@ -51,10 +50,10 @@ public class ConfigureUmbracoManagementApiSwaggerGenOptions : IConfigureOptions<
|
||||
AuthorizationCode = new OpenApiOAuthFlow
|
||||
{
|
||||
AuthorizationUrl =
|
||||
new Uri(Common.Security.Paths.BackOfficeApi.AuthorizationEndpoint, UriKind.Relative),
|
||||
TokenUrl = new Uri(Common.Security.Paths.BackOfficeApi.TokenEndpoint, UriKind.Relative)
|
||||
}
|
||||
}
|
||||
new Uri(Paths.BackOfficeApi.AuthorizationEndpoint, UriKind.Relative),
|
||||
TokenUrl = new Uri(Paths.BackOfficeApi.TokenEndpoint, UriKind.Relative),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Sets Security requirement on backoffice apis
|
||||
|
||||
@@ -29,39 +29,6 @@ public abstract class ContentCollectionControllerBase<TContent, TCollectionRespo
|
||||
_flagProviders = flagProvider;
|
||||
}
|
||||
|
||||
[Obsolete("Use the constructer with all parameters. To be removed in Umbraco 18")]
|
||||
protected ContentCollectionControllerBase(IUmbracoMapper mapper)
|
||||
: this(mapper, StaticServiceProvider.Instance.GetRequiredService<FlagProviderCollection>())
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("This method is no longer used and will be removed in Umbraco 17.")]
|
||||
protected IActionResult CollectionResult(ListViewPagedModel<TContent> result)
|
||||
{
|
||||
PagedModel<TContent> collectionItemsResult = result.Items;
|
||||
ListViewConfiguration collectionConfiguration = result.ListViewConfiguration;
|
||||
|
||||
var collectionPropertyAliases = collectionConfiguration
|
||||
.IncludeProperties
|
||||
.Select(p => p.Alias)
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
|
||||
List<TCollectionResponseModel> collectionResponseModels =
|
||||
_mapper.MapEnumerable<TContent, TCollectionResponseModel>(collectionItemsResult.Items, context =>
|
||||
{
|
||||
context.SetIncludedProperties(collectionPropertyAliases);
|
||||
});
|
||||
|
||||
var pageViewModel = new PagedViewModel<TCollectionResponseModel>
|
||||
{
|
||||
Items = collectionResponseModels,
|
||||
Total = collectionItemsResult.Total,
|
||||
};
|
||||
|
||||
return Ok(pageViewModel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a collection result from the provided collection response models and total number of items.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing.Validation;
|
||||
using Umbraco.Cms.Core.PropertyEditors.Validation;
|
||||
@@ -12,7 +10,6 @@ namespace Umbraco.Cms.Api.Management.Controllers.Content;
|
||||
|
||||
public abstract class ContentControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
|
||||
protected IActionResult ContentEditingOperationStatusResult(ContentEditingOperationStatus status)
|
||||
=> OperationStatusResult(status, problemDetailsBuilder => status switch
|
||||
{
|
||||
@@ -98,6 +95,17 @@ public abstract class ContentControllerBase : ManagementApiControllerBase
|
||||
.Build()),
|
||||
});
|
||||
|
||||
protected IActionResult GetReferencesOperationStatusResult(GetReferencesOperationStatus status)
|
||||
=> OperationStatusResult(status, problemDetailsBuilder => status switch
|
||||
{
|
||||
GetReferencesOperationStatus.ContentNotFound => NotFound(problemDetailsBuilder
|
||||
.WithTitle("The requested content could not be found")
|
||||
.Build()),
|
||||
_ => StatusCode(StatusCodes.Status500InternalServerError, problemDetailsBuilder
|
||||
.WithTitle("Unknown get references operation status.")
|
||||
.Build()),
|
||||
});
|
||||
|
||||
protected IActionResult ContentEditingOperationStatusResult<TContentModelBase, TValueModel, TVariantModel>(
|
||||
ContentEditingOperationStatus status,
|
||||
TContentModelBase requestModel,
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DataType;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
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;
|
||||
private readonly IDataTypeReferencePresentationFactory _dataTypeReferencePresentationFactory;
|
||||
|
||||
public ReferencesDataTypeController(IDataTypeService dataTypeService, IDataTypeReferencePresentationFactory dataTypeReferencePresentationFactory)
|
||||
{
|
||||
_dataTypeService = dataTypeService;
|
||||
_dataTypeReferencePresentationFactory = dataTypeReferencePresentationFactory;
|
||||
}
|
||||
|
||||
[HttpGet("{id:guid}/references")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(DataTypeReferenceResponseModel[]), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> References(CancellationToken cancellationToken, Guid id)
|
||||
{
|
||||
Attempt<IReadOnlyDictionary<Udi, IEnumerable<string>>, DataTypeOperationStatus> result = await _dataTypeService.GetReferencesAsync(id);
|
||||
if (result.Success == false)
|
||||
{
|
||||
return DataTypeOperationStatusResult(result.Status);
|
||||
}
|
||||
|
||||
DataTypeReferenceResponseModel[] viewModels = _dataTypeReferencePresentationFactory.CreateDataTypeReferenceViewModels(result.Result).ToArray();
|
||||
return Ok(viewModels);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,10 @@ 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.Actions;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Security.Authorization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Querying;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
using Umbraco.Extensions;
|
||||
@@ -22,31 +19,6 @@ public class ByKeyDocumentController : DocumentControllerBase
|
||||
private readonly IDocumentPresentationFactory _documentPresentationFactory;
|
||||
private readonly IContentQueryService _contentQueryService;
|
||||
|
||||
[Obsolete("Scheduled for removal in v17")]
|
||||
public ByKeyDocumentController(
|
||||
IAuthorizationService authorizationService,
|
||||
IContentEditingService contentEditingService,
|
||||
IDocumentPresentationFactory documentPresentationFactory)
|
||||
{
|
||||
_authorizationService = authorizationService;
|
||||
_documentPresentationFactory = documentPresentationFactory;
|
||||
_contentQueryService = StaticServiceProvider.Instance.GetRequiredService<IContentQueryService>();
|
||||
}
|
||||
|
||||
// needed for greedy selection until other constructor remains in v17
|
||||
[Obsolete("Scheduled for removal in v17")]
|
||||
public ByKeyDocumentController(
|
||||
IAuthorizationService authorizationService,
|
||||
IContentEditingService contentEditingService,
|
||||
IDocumentPresentationFactory documentPresentationFactory,
|
||||
IContentQueryService contentQueryService)
|
||||
{
|
||||
_authorizationService = authorizationService;
|
||||
_documentPresentationFactory = documentPresentationFactory;
|
||||
_contentQueryService = contentQueryService;
|
||||
}
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public ByKeyDocumentController(
|
||||
IAuthorizationService authorizationService,
|
||||
IDocumentPresentationFactory documentPresentationFactory,
|
||||
|
||||
-6
@@ -23,12 +23,6 @@ public abstract class DocumentCollectionControllerBase : ContentCollectionContro
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor with all parameters. Scheduled to be removed in V18")]
|
||||
protected DocumentCollectionControllerBase(IUmbracoMapper mapper)
|
||||
: base(mapper)
|
||||
{
|
||||
}
|
||||
|
||||
protected IActionResult CollectionOperationStatusResult(ContentCollectionOperationStatus status)
|
||||
=> ContentCollectionOperationStatusResult(status, "document");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.Builders;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Document;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class DocumentPreviewUrlController : DocumentControllerBase
|
||||
{
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IDocumentUrlFactory _documentUrlFactory;
|
||||
|
||||
public DocumentPreviewUrlController(
|
||||
IContentService contentService,
|
||||
IDocumentUrlFactory documentUrlFactory)
|
||||
{
|
||||
_contentService = contentService;
|
||||
_documentUrlFactory = documentUrlFactory;
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[HttpGet("{id:guid}/preview-url")]
|
||||
[ProducesResponseType(typeof(DocumentUrlInfo), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> GetPreviewUrl(Guid id, string providerAlias, string? culture, string? segment)
|
||||
{
|
||||
IContent? content = _contentService.GetById(id);
|
||||
if (content is null)
|
||||
{
|
||||
return NotFound(new ProblemDetailsBuilder()
|
||||
.WithTitle("Document not found")
|
||||
.WithDetail("The requested document did not exist.")
|
||||
.Build());
|
||||
}
|
||||
|
||||
DocumentUrlInfo? previewUrlInfo = await _documentUrlFactory.GetPreviewUrlAsync(content, providerAlias, culture, segment);
|
||||
if (previewUrlInfo is null)
|
||||
{
|
||||
return BadRequest(new ProblemDetailsBuilder()
|
||||
.WithTitle("No preview URL for document")
|
||||
.WithDetail("Failed to produce a preview URL for the requested document.")
|
||||
.Build());
|
||||
}
|
||||
|
||||
return Ok(previewUrlInfo);
|
||||
}
|
||||
}
|
||||
+40
-11
@@ -4,8 +4,10 @@ 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;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Document.References;
|
||||
|
||||
@@ -15,22 +17,16 @@ public class ReferencedByDocumentController : DocumentControllerBase
|
||||
private readonly ITrackedReferencesService _trackedReferencesService;
|
||||
private readonly IRelationTypePresentationFactory _relationTypePresentationFactory;
|
||||
|
||||
public ReferencedByDocumentController(ITrackedReferencesService trackedReferencesService, IRelationTypePresentationFactory relationTypePresentationFactory)
|
||||
public ReferencedByDocumentController(
|
||||
ITrackedReferencesService trackedReferencesService,
|
||||
IRelationTypePresentationFactory relationTypePresentationFactory)
|
||||
{
|
||||
_trackedReferencesService = trackedReferencesService;
|
||||
_relationTypePresentationFactory = relationTypePresentationFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a paged 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)]
|
||||
[Obsolete("Use the ReferencedBy2 action method instead. Scheduled for removal in Umbraco 19, when ReferencedBy2 will be renamed back to ReferencedBy.")]
|
||||
[NonAction]
|
||||
public async Task<ActionResult<PagedViewModel<IReferenceResponseModel>>> ReferencedBy(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
@@ -47,4 +43,37 @@ public class ReferencedByDocumentController : DocumentControllerBase
|
||||
|
||||
return pagedViewModel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a paged 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)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ReferencedBy2(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
int skip = 0,
|
||||
int take = 20)
|
||||
{
|
||||
Attempt<PagedModel<RelationItemModel>, GetReferencesOperationStatus> relationItemsAttempt = await _trackedReferencesService.GetPagedRelationsForItemAsync(id, UmbracoObjectTypes.Document, skip, take, true);
|
||||
|
||||
if (relationItemsAttempt.Success is false)
|
||||
{
|
||||
return GetReferencesOperationStatusResult(relationItemsAttempt.Status);
|
||||
}
|
||||
|
||||
var pagedViewModel = new PagedViewModel<IReferenceResponseModel>
|
||||
{
|
||||
Total = relationItemsAttempt.Result.Total,
|
||||
Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItemsAttempt.Result.Items),
|
||||
};
|
||||
|
||||
return Ok(pagedViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
+35
-6
@@ -3,9 +3,11 @@ 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;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Document.References;
|
||||
|
||||
@@ -15,12 +17,32 @@ public class ReferencedDescendantsDocumentController : DocumentControllerBase
|
||||
private readonly ITrackedReferencesService _trackedReferencesSkipTakeService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public ReferencedDescendantsDocumentController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper)
|
||||
public ReferencedDescendantsDocumentController(
|
||||
ITrackedReferencesService trackedReferencesSkipTakeService,
|
||||
IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_trackedReferencesSkipTakeService = trackedReferencesSkipTakeService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
[Obsolete("Use the ReferencedDescendants2 action method instead. Scheduled for removal in Umbraco 19, when ReferencedDescendants2 will be renamed back to ReferencedDescendants.")]
|
||||
[NonAction]
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a paged list of the descendant nodes of the current item used in any kind of relation.
|
||||
/// </summary>
|
||||
@@ -32,19 +54,26 @@ public class ReferencedDescendantsDocumentController : DocumentControllerBase
|
||||
[HttpGet("{id:guid}/referenced-descendants")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<ReferenceByIdModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<ReferenceByIdModel>>> ReferencedDescendants(
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ReferencedDescendants2(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
int skip = 0,
|
||||
int take = 20)
|
||||
{
|
||||
PagedModel<RelationItemModel> relationItems = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(id, skip, take, true);
|
||||
Attempt<PagedModel<RelationItemModel>, GetReferencesOperationStatus> relationItemsAttempt = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(id, UmbracoObjectTypes.Document, skip, take, true);
|
||||
|
||||
if (relationItemsAttempt.Success is false)
|
||||
{
|
||||
return GetReferencesOperationStatusResult(relationItemsAttempt.Status);
|
||||
}
|
||||
|
||||
var pagedViewModel = new PagedViewModel<ReferenceByIdModel>
|
||||
{
|
||||
Total = relationItems.Total,
|
||||
Items = _umbracoMapper.MapEnumerable<RelationItemModel, ReferenceByIdModel>(relationItems.Items),
|
||||
Total = relationItemsAttempt.Result.Total,
|
||||
Items = _umbracoMapper.MapEnumerable<RelationItemModel, ReferenceByIdModel>(relationItemsAttempt.Result.Items),
|
||||
};
|
||||
|
||||
return pagedViewModel;
|
||||
return Ok(pagedViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
-14
@@ -21,20 +21,6 @@ public class ValidateCreateDocumentController : CreateDocumentControllerBase
|
||||
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,
|
||||
|
||||
-14
@@ -21,20 +21,6 @@ public class ValidateUpdateDocumentController : UpdateDocumentControllerBase
|
||||
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,
|
||||
|
||||
-23
@@ -16,35 +16,12 @@ public class SearchDocumentTypeItemController : DocumentTypeItemControllerBase
|
||||
private readonly IUmbracoMapper _mapper;
|
||||
private readonly IContentTypeSearchService _contentTypeSearchService;
|
||||
|
||||
[Obsolete("Please use ctor that only accepts IUmbracoMapper & IContentTypeSearchService, scheduled for removal in v17")]
|
||||
public SearchDocumentTypeItemController(IEntitySearchService entitySearchService, IContentTypeService contentTypeService, IUmbracoMapper mapper)
|
||||
: this(mapper, StaticServiceProvider.Instance.GetRequiredService<IContentTypeSearchService>())
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use ctor that only accepts IUmbracoMapper & IContentTypeSearchService, scheduled for removal in v17")]
|
||||
// We need to have this constructor, or else we get ambiguous constructor error
|
||||
public SearchDocumentTypeItemController(
|
||||
IEntitySearchService entitySearchService,
|
||||
IContentTypeService contentTypeService,
|
||||
IUmbracoMapper mapper,
|
||||
IContentTypeSearchService contentTypeSearchService)
|
||||
: this(mapper, contentTypeSearchService)
|
||||
{
|
||||
}
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public SearchDocumentTypeItemController(IUmbracoMapper mapper, IContentTypeSearchService contentTypeSearchService)
|
||||
{
|
||||
_mapper = mapper;
|
||||
_contentTypeSearchService = contentTypeSearchService;
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
[Obsolete("Scheduled to be removed in v16, use the non obsoleted method instead")]
|
||||
public async Task<IActionResult> Search(CancellationToken cancellationToken, string query, int skip = 0, int take = 100)
|
||||
=> await SearchDocumentType(cancellationToken, query, null, skip, take);
|
||||
|
||||
[HttpGet("search")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedModel<DocumentTypeItemResponseModel>), StatusCodes.Status200OK)]
|
||||
|
||||
@@ -11,12 +11,13 @@ using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Help;
|
||||
|
||||
[Obsolete("This is no longer used and will be removed in v19")]
|
||||
[ApiVersion("1.0")]
|
||||
public class GetHelpController : HelpControllerBase
|
||||
{
|
||||
private readonly ILogger<GetHelpController> _logger;
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private HelpPageSettings _helpPageSettings;
|
||||
private readonly HelpPageSettings _helpPageSettings;
|
||||
|
||||
public GetHelpController(
|
||||
IOptionsMonitor<HelpPageSettings> helpPageSettings,
|
||||
@@ -26,11 +27,8 @@ public class GetHelpController : HelpControllerBase
|
||||
_logger = logger;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_helpPageSettings = helpPageSettings.CurrentValue;
|
||||
helpPageSettings.OnChange(UpdateHelpPageSettings);
|
||||
}
|
||||
|
||||
private void UpdateHelpPageSettings(HelpPageSettings settings) => _helpPageSettings = settings;
|
||||
|
||||
[HttpGet]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
|
||||
@@ -3,6 +3,7 @@ using Umbraco.Cms.Api.Management.Routing;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Help;
|
||||
|
||||
[Obsolete("This is no longer used and will be removed in v19")]
|
||||
[VersionedApiBackOfficeRoute("help")]
|
||||
[ApiExplorerSettings(GroupName = "Help")]
|
||||
public abstract class HelpControllerBase : ManagementApiControllerBase
|
||||
|
||||
@@ -26,9 +26,9 @@ public class RebuildIndexerController : IndexerControllerBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds the index
|
||||
/// Rebuilds the index.
|
||||
/// </summary>
|
||||
/// <param name="indexName"></param>
|
||||
/// <param name="indexName">The name of the index to rebuild.</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{indexName}/rebuild")]
|
||||
[MapToApiVersion("1.0")]
|
||||
@@ -36,7 +36,7 @@ public class RebuildIndexerController : IndexerControllerBase
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status409Conflict)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public Task<IActionResult> Rebuild(CancellationToken cancellationToken, string indexName)
|
||||
public async Task<IActionResult> Rebuild(CancellationToken cancellationToken, string indexName)
|
||||
{
|
||||
if (!_examineManager.TryGetIndex(indexName, out IIndex? index))
|
||||
{
|
||||
@@ -48,7 +48,7 @@ public class RebuildIndexerController : IndexerControllerBase
|
||||
Type = "Error",
|
||||
};
|
||||
|
||||
return Task.FromResult<IActionResult>(NotFound(invalidModelProblem));
|
||||
return NotFound(invalidModelProblem);
|
||||
}
|
||||
|
||||
if (!_indexingRebuilderService.CanRebuild(index.Name))
|
||||
@@ -57,19 +57,19 @@ public class RebuildIndexerController : IndexerControllerBase
|
||||
{
|
||||
Title = "Could not validate the populator",
|
||||
Detail =
|
||||
$"The index {index?.Name} could not be rebuilt because we could not validate its associated {typeof(IIndexPopulator)}",
|
||||
$"The index {index.Name} could not be rebuilt because we could not validate its associated {typeof(IIndexPopulator)}",
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Type = "Error",
|
||||
};
|
||||
|
||||
return Task.FromResult<IActionResult>(BadRequest(invalidModelProblem));
|
||||
return BadRequest(invalidModelProblem);
|
||||
}
|
||||
|
||||
_logger.LogInformation("Rebuilding index '{IndexName}'", indexName);
|
||||
|
||||
if (_indexingRebuilderService.TryRebuild(index, indexName))
|
||||
if (await _indexingRebuilderService.TryRebuildAsync(index, indexName))
|
||||
{
|
||||
return Task.FromResult<IActionResult>(Ok());
|
||||
return Ok();
|
||||
}
|
||||
|
||||
var problemDetails = new ProblemDetails
|
||||
@@ -80,6 +80,6 @@ public class RebuildIndexerController : IndexerControllerBase
|
||||
Type = "Error",
|
||||
};
|
||||
|
||||
return Task.FromResult<IActionResult>(Conflict(problemDetails));
|
||||
return Conflict(problemDetails);
|
||||
}
|
||||
}
|
||||
|
||||
-6
@@ -26,12 +26,6 @@ public abstract class MediaCollectionControllerBase : ContentCollectionControlle
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor with all parameters. Scheduled to be removed in Umbraco 18")]
|
||||
protected MediaCollectionControllerBase(IUmbracoMapper mapper)
|
||||
: base(mapper)
|
||||
{
|
||||
}
|
||||
|
||||
protected IActionResult CollectionOperationStatusResult(ContentCollectionOperationStatus status)
|
||||
=> ContentCollectionOperationStatusResult(status, "media");
|
||||
}
|
||||
|
||||
+40
-11
@@ -4,8 +4,10 @@ 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;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Media.References;
|
||||
|
||||
@@ -15,22 +17,16 @@ public class ReferencedByMediaController : MediaControllerBase
|
||||
private readonly ITrackedReferencesService _trackedReferencesService;
|
||||
private readonly IRelationTypePresentationFactory _relationTypePresentationFactory;
|
||||
|
||||
public ReferencedByMediaController(ITrackedReferencesService trackedReferencesService, IRelationTypePresentationFactory relationTypePresentationFactory)
|
||||
public ReferencedByMediaController(
|
||||
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)]
|
||||
[Obsolete("Use the ReferencedBy2 action method instead. Scheduled for removal in Umbraco 19, when ReferencedBy2 will be renamed back to ReferencedBy.")]
|
||||
[NonAction]
|
||||
public async Task<ActionResult<PagedViewModel<IReferenceResponseModel>>> ReferencedBy(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
@@ -47,4 +43,37 @@ public class ReferencedByMediaController : MediaControllerBase
|
||||
|
||||
return pagedViewModel;
|
||||
}
|
||||
|
||||
/// <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)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ReferencedBy2(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
int skip = 0,
|
||||
int take = 20)
|
||||
{
|
||||
Attempt<PagedModel<RelationItemModel>, GetReferencesOperationStatus> relationItemsAttempt = await _trackedReferencesService.GetPagedRelationsForItemAsync(id, UmbracoObjectTypes.Media, skip, take, true);
|
||||
|
||||
if (relationItemsAttempt.Success is false)
|
||||
{
|
||||
return GetReferencesOperationStatusResult(relationItemsAttempt.Status);
|
||||
}
|
||||
|
||||
var pagedViewModel = new PagedViewModel<IReferenceResponseModel>
|
||||
{
|
||||
Total = relationItemsAttempt.Result.Total,
|
||||
Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItemsAttempt.Result.Items),
|
||||
};
|
||||
|
||||
return Ok(pagedViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
+35
-7
@@ -3,9 +3,11 @@ 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;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Media.References;
|
||||
|
||||
@@ -14,13 +16,32 @@ public class ReferencedDescendantsMediaController : MediaControllerBase
|
||||
{
|
||||
private readonly ITrackedReferencesService _trackedReferencesSkipTakeService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public ReferencedDescendantsMediaController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper)
|
||||
public ReferencedDescendantsMediaController(
|
||||
ITrackedReferencesService trackedReferencesSkipTakeService,
|
||||
IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_trackedReferencesSkipTakeService = trackedReferencesSkipTakeService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
[Obsolete("Use the ReferencedDescendants2 action method instead. Scheduled for removal in Umbraco 19, when ReferencedDescendants2 will be renamed back to ReferencedDescendants.")]
|
||||
[NonAction]
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a page list of the child nodes of the current item used in any kind of relation.
|
||||
/// </summary>
|
||||
@@ -32,19 +53,26 @@ public class ReferencedDescendantsMediaController : MediaControllerBase
|
||||
[HttpGet("{id:guid}/referenced-descendants")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<ReferenceByIdModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<ReferenceByIdModel>>> ReferencedDescendants(
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ReferencedDescendants2(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
int skip = 0,
|
||||
int take = 20)
|
||||
{
|
||||
PagedModel<RelationItemModel> relationItems = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(id, skip, take, true);
|
||||
Attempt<PagedModel<RelationItemModel>, GetReferencesOperationStatus> relationItemsAttempt = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(id, UmbracoObjectTypes.Media, skip, take, true);
|
||||
|
||||
if (relationItemsAttempt.Success is false)
|
||||
{
|
||||
return GetReferencesOperationStatusResult(relationItemsAttempt.Status);
|
||||
}
|
||||
|
||||
var pagedViewModel = new PagedViewModel<ReferenceByIdModel>
|
||||
{
|
||||
Total = relationItems.Total,
|
||||
Items = _umbracoMapper.MapEnumerable<RelationItemModel, ReferenceByIdModel>(relationItems.Items),
|
||||
Total = relationItemsAttempt.Result.Total,
|
||||
Items = _umbracoMapper.MapEnumerable<RelationItemModel, ReferenceByIdModel>(relationItemsAttempt.Result.Items),
|
||||
};
|
||||
|
||||
return pagedViewModel;
|
||||
return Ok(pagedViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -24,15 +24,15 @@ public class SearchMemberItemController : MemberItemControllerBase
|
||||
[HttpGet("search")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedModel<MemberItemResponseModel>), StatusCodes.Status200OK)]
|
||||
public Task<IActionResult> SearchWithAllowedTypes(CancellationToken cancellationToken, string query, int skip = 0, int take = 100, [FromQuery]IEnumerable<Guid>? allowedMemberTypes = null)
|
||||
public async Task<IActionResult> SearchWithAllowedTypes(CancellationToken cancellationToken, string query, int skip = 0, int take = 100, [FromQuery]IEnumerable<Guid>? allowedMemberTypes = null)
|
||||
{
|
||||
PagedModel<IEntitySlim> searchResult = _indexedEntitySearchService.Search(UmbracoObjectTypes.Member, query, null, allowedMemberTypes, skip, take);
|
||||
PagedModel<IEntitySlim> searchResult = await _indexedEntitySearchService.SearchAsync(UmbracoObjectTypes.Member, query, null, allowedMemberTypes, false, "*", skip, take);
|
||||
var result = new PagedModel<MemberItemResponseModel>
|
||||
{
|
||||
Items = searchResult.Items.OfType<IMemberEntitySlim>().Select(_memberPresentationFactory.CreateItemResponseModel),
|
||||
Total = searchResult.Total
|
||||
Total = searchResult.Total,
|
||||
};
|
||||
|
||||
return Task.FromResult<IActionResult>(Ok(result));
|
||||
return await Task.FromResult<IActionResult>(Ok(result));
|
||||
}
|
||||
}
|
||||
|
||||
+40
-11
@@ -4,8 +4,10 @@ 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;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Member.References;
|
||||
|
||||
@@ -15,22 +17,16 @@ public class ReferencedByMemberController : MemberControllerBase
|
||||
private readonly ITrackedReferencesService _trackedReferencesService;
|
||||
private readonly IRelationTypePresentationFactory _relationTypePresentationFactory;
|
||||
|
||||
public ReferencedByMemberController(ITrackedReferencesService trackedReferencesService, 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)]
|
||||
[Obsolete("Use the ReferencedBy2 action method instead. Scheduled for removal in Umbraco 19, when ReferencedBy2 will be renamed back to ReferencedBy.")]
|
||||
[NonAction]
|
||||
public async Task<ActionResult<PagedViewModel<IReferenceResponseModel>>> ReferencedBy(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
@@ -47,4 +43,37 @@ public class ReferencedByMemberController : MemberControllerBase
|
||||
|
||||
return pagedViewModel;
|
||||
}
|
||||
|
||||
/// <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)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ReferencedBy2(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
int skip = 0,
|
||||
int take = 20)
|
||||
{
|
||||
Attempt<PagedModel<RelationItemModel>, GetReferencesOperationStatus> relationItemsAttempt = await _trackedReferencesService.GetPagedRelationsForItemAsync(id, UmbracoObjectTypes.Member, skip, take, true);
|
||||
|
||||
if (relationItemsAttempt.Success is false)
|
||||
{
|
||||
return GetReferencesOperationStatusResult(relationItemsAttempt.Status);
|
||||
}
|
||||
|
||||
var pagedViewModel = new PagedViewModel<IReferenceResponseModel>
|
||||
{
|
||||
Total = relationItemsAttempt.Result.Total,
|
||||
Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItemsAttempt.Result.Items),
|
||||
};
|
||||
|
||||
return Ok(pagedViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
+41
-12
@@ -3,9 +3,11 @@ 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;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Member.References;
|
||||
|
||||
@@ -15,23 +17,16 @@ public class ReferencedDescendantsMemberController : MemberControllerBase
|
||||
private readonly ITrackedReferencesService _trackedReferencesSkipTakeService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public ReferencedDescendantsMemberController(ITrackedReferencesService trackedReferencesSkipTakeService, 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)]
|
||||
[Obsolete("Use the ReferencedDescendants2 action method instead. Scheduled for removal in Umbraco 19, when ReferencedDescendants2 will be renamed back to ReferencedDescendants.")]
|
||||
[NonAction]
|
||||
public async Task<ActionResult<PagedViewModel<ReferenceByIdModel>>> ReferencedDescendants(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
@@ -47,4 +42,38 @@ public class ReferencedDescendantsMemberController : MemberControllerBase
|
||||
|
||||
return pagedViewModel;
|
||||
}
|
||||
|
||||
/// <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)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ReferencedDescendants2(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
int skip = 0,
|
||||
int take = 20)
|
||||
{
|
||||
Attempt<PagedModel<RelationItemModel>, GetReferencesOperationStatus> relationItemsAttempt = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(id, UmbracoObjectTypes.Member, skip, take, true);
|
||||
|
||||
if (relationItemsAttempt.Success is false)
|
||||
{
|
||||
return GetReferencesOperationStatusResult(relationItemsAttempt.Status);
|
||||
}
|
||||
|
||||
var pagedViewModel = new PagedViewModel<ReferenceByIdModel>
|
||||
{
|
||||
Total = relationItemsAttempt.Result.Total,
|
||||
Items = _umbracoMapper.MapEnumerable<RelationItemModel, ReferenceByIdModel>(relationItemsAttempt.Result.Items),
|
||||
};
|
||||
|
||||
return Ok(pagedViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Folder;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.MemberType.Folder;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class ByKeyMemberTypeFolderController : MemberTypeFolderControllerBase
|
||||
{
|
||||
public ByKeyMemberTypeFolderController(
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
IMemberTypeContainerService memberTypeContainerService)
|
||||
: base(backOfficeSecurityAccessor, memberTypeContainerService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("{id:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(FolderResponseModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ByKey(CancellationToken cancellationToken, Guid id) => await GetFolderAsync(id);
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Folder;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.MemberType.Folder;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class CreateMemberTypeFolderController : MemberTypeFolderControllerBase
|
||||
{
|
||||
public CreateMemberTypeFolderController(
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
IMemberTypeContainerService memberTypeContainerService)
|
||||
: base(backOfficeSecurityAccessor, memberTypeContainerService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> Create(
|
||||
CancellationToken cancellationToken,
|
||||
CreateFolderRequestModel createFolderRequestModel)
|
||||
=> await CreateFolderAsync<ByKeyMemberTypeFolderController>(
|
||||
createFolderRequestModel,
|
||||
controller => nameof(controller.ByKey)).ConfigureAwait(false);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.MemberType.Folder;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class DeleteMemberTypeFolderController : MemberTypeFolderControllerBase
|
||||
{
|
||||
public DeleteMemberTypeFolderController(
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
IMemberTypeContainerService memberTypeContainerService)
|
||||
: base(backOfficeSecurityAccessor, memberTypeContainerService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpDelete("{id:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> Delete(CancellationToken cancellationToken, Guid id) => await DeleteFolderAsync(id);
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.MemberType.Folder;
|
||||
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MemberType}/folder")]
|
||||
[ApiExplorerSettings(GroupName = "Member Type")]
|
||||
[Authorize(Policy = AuthorizationPolicies.TreeAccessMemberTypes)]
|
||||
public abstract class MemberTypeFolderControllerBase : FolderManagementControllerBase<IMemberType>
|
||||
{
|
||||
protected MemberTypeFolderControllerBase(
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
IMemberTypeContainerService memberTypeContainerService)
|
||||
: base(backOfficeSecurityAccessor, memberTypeContainerService)
|
||||
{
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Folder;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.MemberType.Folder;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class UpdateMemberTypeFolderController : MemberTypeFolderControllerBase
|
||||
{
|
||||
public UpdateMemberTypeFolderController(
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
IMemberTypeContainerService memberTypeContainerService)
|
||||
: base(backOfficeSecurityAccessor, memberTypeContainerService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpPut("{id:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> Update(
|
||||
CancellationToken cancellationToken,
|
||||
Guid id,
|
||||
UpdateFolderResponseModel updateFolderResponseModel)
|
||||
=> await UpdateFolderAsync(id, updateFolderResponseModel);
|
||||
}
|
||||
+2
-1
@@ -30,6 +30,7 @@ public class RootMemberTypeTreeController : MemberTypeTreeControllerBase
|
||||
public async Task<ActionResult<PagedViewModel<MemberTypeTreeItemResponseModel>>> Root(
|
||||
CancellationToken cancellationToken,
|
||||
int skip = 0,
|
||||
int take = 100)
|
||||
int take = 100,
|
||||
bool foldersOnly = false)
|
||||
=> await GetRoot(skip, take);
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,4 +1,4 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Services.Flags;
|
||||
@@ -23,6 +23,7 @@ public class SiblingMemberTypeTreeController : MemberTypeTreeControllerBase
|
||||
CancellationToken cancellationToken,
|
||||
Guid target,
|
||||
int before,
|
||||
int after)
|
||||
int after,
|
||||
bool foldersOnly = false)
|
||||
=> await GetSiblings(target, before, after);
|
||||
}
|
||||
|
||||
+4
-8
@@ -1,21 +1,18 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Infrastructure.ModelsBuilder;
|
||||
using Umbraco.Cms.Infrastructure.ModelsBuilder.Building;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.ModelsBuilder;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class BuildModelsBuilderController : ModelsBuilderControllerBase
|
||||
{
|
||||
private ModelsBuilderSettings _modelsBuilderSettings;
|
||||
private readonly ModelsBuilderSettings _modelsBuilderSettings;
|
||||
private readonly ModelsGenerationError _mbErrors;
|
||||
private readonly IModelsGenerator _modelGenerator;
|
||||
|
||||
@@ -27,8 +24,6 @@ public class BuildModelsBuilderController : ModelsBuilderControllerBase
|
||||
_mbErrors = mbErrors;
|
||||
_modelGenerator = modelGenerator;
|
||||
_modelsBuilderSettings = modelsBuilderSettings.CurrentValue;
|
||||
|
||||
modelsBuilderSettings.OnChange(x => _modelsBuilderSettings = x);
|
||||
}
|
||||
|
||||
[HttpPost("build")]
|
||||
@@ -39,7 +34,8 @@ public class BuildModelsBuilderController : ModelsBuilderControllerBase
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_modelsBuilderSettings.ModelsMode.SupportsExplicitGeneration())
|
||||
if (_modelsBuilderSettings.ModelsMode != Constants.ModelsBuilder.ModelsModes.SourceCodeManual
|
||||
&& _modelsBuilderSettings.ModelsMode != Constants.ModelsBuilder.ModelsModes.SourceCodeAuto)
|
||||
{
|
||||
var problemDetailsModel = new ProblemDetails
|
||||
{
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Services.NewsDashboard;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.NewsDashboard;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.NewsDashboard;
|
||||
|
||||
public class NewsDashboardController : NewsDashboardControllerBase
|
||||
{
|
||||
private readonly INewsDashboardService _newsDashboardService;
|
||||
|
||||
public NewsDashboardController(INewsDashboardService newsDashboardService) => _newsDashboardService = newsDashboardService;
|
||||
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(NewsDashboardResponseModel), StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> GetDashboard()
|
||||
{
|
||||
NewsDashboardResponseModel content = await _newsDashboardService.GetItemsAsync();
|
||||
|
||||
return Ok(content);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.NewsDashboard;
|
||||
|
||||
[VersionedApiBackOfficeRoute("news-dashboard")]
|
||||
[ApiExplorerSettings(GroupName = "News Dashboard")]
|
||||
public abstract class NewsDashboardControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
+13
-15
@@ -1,34 +1,32 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.PartialView.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class AncestorsPartialViewTreeController : PartialViewTreeControllerBase
|
||||
{
|
||||
private readonly IPartialViewTreeService _partialViewTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public AncestorsPartialViewTreeController(IPartialViewTreeService partialViewTreeService)
|
||||
: this(partialViewTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>())
|
||||
=> _partialViewTreeService = partialViewTreeService;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
public AncestorsPartialViewTreeController(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
|
||||
: base(partialViewTreeService, fileSystems) =>
|
||||
_partialViewTreeService = partialViewTreeService;
|
||||
public AncestorsPartialViewTreeController(IPartialViewTreeService partialViewTreeService)
|
||||
: base(partialViewTreeService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public AncestorsPartialViewTreeController(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
|
||||
: base(partialViewTreeService, fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public AncestorsPartialViewTreeController(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IPartialViewTreeService>(), fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+14
-15
@@ -1,34 +1,33 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.PartialView.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class ChildrenPartialViewTreeController : PartialViewTreeControllerBase
|
||||
{
|
||||
private readonly IPartialViewTreeService _partialViewTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public ChildrenPartialViewTreeController(IPartialViewTreeService partialViewTreeService)
|
||||
: this(partialViewTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>())
|
||||
=> _partialViewTreeService = partialViewTreeService;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
public ChildrenPartialViewTreeController(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
|
||||
: base(partialViewTreeService, fileSystems) =>
|
||||
_partialViewTreeService = partialViewTreeService;
|
||||
public ChildrenPartialViewTreeController(IPartialViewTreeService partialViewTreeService)
|
||||
: base(partialViewTreeService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public ChildrenPartialViewTreeController(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
|
||||
: base(partialViewTreeService, fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public ChildrenPartialViewTreeController(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IPartialViewTreeService>(), fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+9
-13
@@ -1,11 +1,9 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Controllers.Tree;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
|
||||
@@ -16,30 +14,28 @@ namespace Umbraco.Cms.Api.Management.Controllers.PartialView.Tree;
|
||||
[Authorize(Policy = AuthorizationPolicies.TreeAccessPartialViews)]
|
||||
public class PartialViewTreeControllerBase : FileSystemTreeControllerBase
|
||||
{
|
||||
private readonly IPartialViewTreeService _partialViewTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public PartialViewTreeControllerBase(IPartialViewTreeService partialViewTreeService)
|
||||
: this(partialViewTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>()) =>
|
||||
_partialViewTreeService = partialViewTreeService;
|
||||
: base(partialViewTreeService)
|
||||
{
|
||||
FileSystem = null!;
|
||||
}
|
||||
|
||||
// FileSystem is required therefore, we can't remove it without some wizadry. When obsoletion is due, remove this.
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Scheduled for removal in Umbraco 18.")]
|
||||
// FileSystem is required therefore, we can't remove it without some wizardry. When obsoletion is due, remove this.
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public PartialViewTreeControllerBase(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
|
||||
: base(partialViewTreeService)
|
||||
{
|
||||
_partialViewTreeService = partialViewTreeService;
|
||||
FileSystem = fileSystems.PartialViewsFileSystem ??
|
||||
throw new ArgumentException("Missing scripts file system", nameof(fileSystems));
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 18.")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public PartialViewTreeControllerBase(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IPartialViewTreeService>())
|
||||
: base()
|
||||
=> FileSystem = fileSystems.PartialViewsFileSystem ??
|
||||
throw new ArgumentException("Missing scripts file system", nameof(fileSystems));
|
||||
|
||||
[Obsolete("Included in the service class. Scheduled to be removed in Umbraco 18.")]
|
||||
[Obsolete("Included in the service class. Scheduled to be removed in Umbraco 19.")]
|
||||
protected override IFileSystem FileSystem { get; }
|
||||
}
|
||||
|
||||
+14
-15
@@ -1,34 +1,33 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.PartialView.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class RootPartialViewTreeController : PartialViewTreeControllerBase
|
||||
{
|
||||
private readonly IPartialViewTreeService _partialViewTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public RootPartialViewTreeController(IPartialViewTreeService partialViewTreeService)
|
||||
: this(partialViewTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>())
|
||||
=> _partialViewTreeService = partialViewTreeService;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
public RootPartialViewTreeController(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
|
||||
: base(partialViewTreeService, fileSystems) =>
|
||||
_partialViewTreeService = partialViewTreeService;
|
||||
public RootPartialViewTreeController(IPartialViewTreeService partialViewTreeService)
|
||||
: base(partialViewTreeService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public RootPartialViewTreeController(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
|
||||
: base(partialViewTreeService, fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public RootPartialViewTreeController(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IPartialViewTreeService>(), fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+13
-14
@@ -1,32 +1,31 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.PartialView.Tree;
|
||||
|
||||
public class SiblingsPartialViewTreeController : PartialViewTreeControllerBase
|
||||
{
|
||||
private readonly IPartialViewTreeService _partialViewTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public SiblingsPartialViewTreeController(IPartialViewTreeService partialViewTreeService)
|
||||
: this(partialViewTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>())
|
||||
=> _partialViewTreeService = partialViewTreeService;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
public SiblingsPartialViewTreeController(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
|
||||
: base(partialViewTreeService, fileSystems) =>
|
||||
_partialViewTreeService = partialViewTreeService;
|
||||
public SiblingsPartialViewTreeController(IPartialViewTreeService partialViewTreeService)
|
||||
: base(partialViewTreeService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public SiblingsPartialViewTreeController(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
|
||||
: base(partialViewTreeService, fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public SiblingsPartialViewTreeController(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IPartialViewTreeService>(), fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Cms.Core.Services;
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Preview;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[Obsolete("Do not use this. Preview state is initiated implicitly by the preview URL generation. Scheduled for removal in V18.")]
|
||||
public class EnterPreviewController : PreviewControllerBase
|
||||
{
|
||||
private readonly IPreviewService _previewService;
|
||||
|
||||
+13
-14
@@ -1,10 +1,9 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Script.Tree;
|
||||
@@ -12,22 +11,22 @@ namespace Umbraco.Cms.Api.Management.Controllers.Script.Tree;
|
||||
[ApiVersion("1.0")]
|
||||
public class AncestorsScriptTreeController : ScriptTreeControllerBase
|
||||
{
|
||||
private readonly IScriptTreeService _scriptTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public AncestorsScriptTreeController(IScriptTreeService scriptTreeService)
|
||||
: this(scriptTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>())
|
||||
=> _scriptTreeService = scriptTreeService;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
public AncestorsScriptTreeController(IScriptTreeService scriptTreeService, FileSystems fileSystems)
|
||||
: base(scriptTreeService, fileSystems) =>
|
||||
_scriptTreeService = scriptTreeService;
|
||||
public AncestorsScriptTreeController(IScriptTreeService scriptTreeService)
|
||||
: base(scriptTreeService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public AncestorsScriptTreeController(IScriptTreeService scriptTreeService, FileSystems fileSystems)
|
||||
: base(scriptTreeService, fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public AncestorsScriptTreeController(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IScriptTreeService>(), fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+14
-15
@@ -1,34 +1,33 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Script.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class ChildrenScriptTreeController : ScriptTreeControllerBase
|
||||
{
|
||||
private readonly IScriptTreeService _scriptTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public ChildrenScriptTreeController(IScriptTreeService scriptTreeService)
|
||||
: this(scriptTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>())
|
||||
=> _scriptTreeService = scriptTreeService;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
public ChildrenScriptTreeController(IScriptTreeService scriptTreeService, FileSystems fileSystems)
|
||||
: base(scriptTreeService, fileSystems) =>
|
||||
_scriptTreeService = scriptTreeService;
|
||||
public ChildrenScriptTreeController(IScriptTreeService scriptTreeService)
|
||||
: base(scriptTreeService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public ChildrenScriptTreeController(IScriptTreeService scriptTreeService, FileSystems fileSystems)
|
||||
: base(scriptTreeService, fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public ChildrenScriptTreeController(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IScriptTreeService>(), fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+14
-15
@@ -1,34 +1,33 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Script.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class RootScriptTreeController : ScriptTreeControllerBase
|
||||
{
|
||||
private readonly IScriptTreeService _scriptTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public RootScriptTreeController(IScriptTreeService scriptTreeService)
|
||||
: this(scriptTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>())
|
||||
=> _scriptTreeService = scriptTreeService;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
public RootScriptTreeController(IScriptTreeService scriptTreeService, FileSystems fileSystems)
|
||||
: base(scriptTreeService, fileSystems) =>
|
||||
_scriptTreeService = scriptTreeService;
|
||||
public RootScriptTreeController(IScriptTreeService scriptTreeService)
|
||||
: base(scriptTreeService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public RootScriptTreeController(IScriptTreeService scriptTreeService, FileSystems fileSystems)
|
||||
: base(scriptTreeService, fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public RootScriptTreeController(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IScriptTreeService>(), fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Controllers.Tree;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
|
||||
@@ -16,30 +14,28 @@ namespace Umbraco.Cms.Api.Management.Controllers.Script.Tree;
|
||||
[Authorize(Policy = AuthorizationPolicies.TreeAccessScripts)]
|
||||
public class ScriptTreeControllerBase : FileSystemTreeControllerBase
|
||||
{
|
||||
private readonly IScriptTreeService _scriptTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public ScriptTreeControllerBase(IScriptTreeService scriptTreeService)
|
||||
: this(scriptTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>()) =>
|
||||
_scriptTreeService = scriptTreeService;
|
||||
: base(scriptTreeService)
|
||||
{
|
||||
FileSystem = null!;
|
||||
}
|
||||
|
||||
// FileSystem is required therefore, we can't remove it without some wizadry. When obsoletion is due, remove this.
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Scheduled for removal in Umbraco 18.")]
|
||||
// FileSystem is required therefore, we can't remove it without some wizardry. When obsoletion is due, remove this.
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public ScriptTreeControllerBase(IScriptTreeService scriptTreeService, FileSystems fileSystems)
|
||||
: base(scriptTreeService)
|
||||
{
|
||||
_scriptTreeService = scriptTreeService;
|
||||
FileSystem = fileSystems.ScriptsFileSystem ??
|
||||
throw new ArgumentException("Missing scripts file system", nameof(fileSystems));
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 18.")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public ScriptTreeControllerBase(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IScriptTreeService>())
|
||||
: base()
|
||||
=> FileSystem = fileSystems.ScriptsFileSystem ??
|
||||
throw new ArgumentException("Missing scripts file system", nameof(fileSystems));
|
||||
|
||||
[Obsolete("Included in the service class. Scheduled to be removed in Umbraco 18.")]
|
||||
[Obsolete("Included in the service class. Scheduled to be removed in Umbraco 19.")]
|
||||
protected override IFileSystem FileSystem { get; }
|
||||
}
|
||||
|
||||
+13
-14
@@ -1,32 +1,31 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Script.Tree;
|
||||
|
||||
public class SiblingsScriptTreeController : ScriptTreeControllerBase
|
||||
{
|
||||
private readonly IScriptTreeService _scriptTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public SiblingsScriptTreeController(IScriptTreeService scriptTreeService)
|
||||
: this(scriptTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>())
|
||||
=> _scriptTreeService = scriptTreeService;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
public SiblingsScriptTreeController(IScriptTreeService scriptTreeService, FileSystems fileSystems)
|
||||
: base(scriptTreeService, fileSystems) =>
|
||||
_scriptTreeService = scriptTreeService;
|
||||
public SiblingsScriptTreeController(IScriptTreeService scriptTreeService)
|
||||
: base(scriptTreeService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public SiblingsScriptTreeController(IScriptTreeService scriptTreeService, FileSystems fileSystems)
|
||||
: base(scriptTreeService, fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public SiblingsScriptTreeController(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IScriptTreeService>(), fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -254,7 +254,7 @@ public class BackOfficeController : SecurityControllerBase
|
||||
if (associatedUser is not null)
|
||||
{
|
||||
// log current datetime as last login (this also ensures that the user is not flagged as inactive)
|
||||
associatedUser.LastLoginDateUtc = DateTime.UtcNow;
|
||||
associatedUser.LastLoginDate = DateTime.UtcNow;
|
||||
await _backOfficeUserManager.UpdateAsync(associatedUser);
|
||||
|
||||
return await SignInBackOfficeUser(associatedUser, request);
|
||||
|
||||
@@ -17,12 +17,6 @@ public class BackOfficeDefaultController : Controller
|
||||
public BackOfficeDefaultController(IRuntime umbracoRuntime)
|
||||
=> _umbracoRuntime = umbracoRuntime;
|
||||
|
||||
[Obsolete("Use the non obsoleted constructor instead. Scheduled to be removed in v17")]
|
||||
public BackOfficeDefaultController()
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IRuntime>())
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> Index(CancellationToken cancellationToken)
|
||||
|
||||
+2
-2
@@ -34,14 +34,14 @@ public class StaticFileTreeControllerBase : FileSystemTreeControllerBase
|
||||
|
||||
protected override IFileSystem FileSystem { get; }
|
||||
|
||||
protected string[] GetDirectories(string path) =>
|
||||
protected override string[] GetDirectories(string path) =>
|
||||
IsTreeRootPath(path)
|
||||
? _allowedRootFolders
|
||||
: IsAllowedPath(path)
|
||||
? _fileSystemTreeService.GetDirectories(path)
|
||||
: Array.Empty<string>();
|
||||
|
||||
protected string[] GetFiles(string path)
|
||||
protected override string[] GetFiles(string path)
|
||||
=> IsTreeRootPath(path) || IsAllowedPath(path) == false
|
||||
? Array.Empty<string>()
|
||||
: _fileSystemTreeService.GetFiles(path);
|
||||
|
||||
+13
-14
@@ -1,10 +1,9 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Stylesheet.Tree;
|
||||
@@ -12,22 +11,22 @@ namespace Umbraco.Cms.Api.Management.Controllers.Stylesheet.Tree;
|
||||
[ApiVersion("1.0")]
|
||||
public class AncestorsStylesheetTreeController : StylesheetTreeControllerBase
|
||||
{
|
||||
private readonly IStyleSheetTreeService _styleSheetTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public AncestorsStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService)
|
||||
: this(styleSheetTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>())
|
||||
=> _styleSheetTreeService = styleSheetTreeService;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
public AncestorsStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService, FileSystems fileSystems)
|
||||
: base(styleSheetTreeService, fileSystems) =>
|
||||
_styleSheetTreeService = styleSheetTreeService;
|
||||
public AncestorsStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService)
|
||||
: base(styleSheetTreeService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public AncestorsStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService, FileSystems fileSystems)
|
||||
: base(styleSheetTreeService, fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public AncestorsStylesheetTreeController(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IStyleSheetTreeService>(), fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -1,36 +1,36 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Stylesheet.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class ChildrenStylesheetTreeController : StylesheetTreeControllerBase
|
||||
{
|
||||
private readonly IStyleSheetTreeService _styleSheetTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public ChildrenStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService)
|
||||
: this(styleSheetTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>())
|
||||
=> _styleSheetTreeService = styleSheetTreeService;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
public ChildrenStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService, FileSystems fileSystems)
|
||||
: base(styleSheetTreeService, fileSystems) =>
|
||||
_styleSheetTreeService = styleSheetTreeService;
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
public ChildrenStylesheetTreeController(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IStyleSheetTreeService>(), fileSystems)
|
||||
public ChildrenStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService)
|
||||
: base(styleSheetTreeService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public ChildrenStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService, FileSystems fileSystems)
|
||||
: base(styleSheetTreeService, fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public ChildrenStylesheetTreeController(FileSystems fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("children")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<FileSystemTreeItemPresentationModel>), StatusCodes.Status200OK)]
|
||||
|
||||
+14
-15
@@ -1,34 +1,33 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Stylesheet.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class RootStylesheetTreeController : StylesheetTreeControllerBase
|
||||
{
|
||||
private readonly IStyleSheetTreeService _styleSheetTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public RootStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService)
|
||||
: this(styleSheetTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>())
|
||||
=> _styleSheetTreeService = styleSheetTreeService;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
public RootStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService, FileSystems fileSystems)
|
||||
: base(styleSheetTreeService, fileSystems) =>
|
||||
_styleSheetTreeService = styleSheetTreeService;
|
||||
public RootStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService)
|
||||
: base(styleSheetTreeService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public RootStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService, FileSystems fileSystems)
|
||||
: base(styleSheetTreeService, fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public RootStylesheetTreeController(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IStyleSheetTreeService>(), fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+13
-14
@@ -1,32 +1,31 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Stylesheet.Tree;
|
||||
|
||||
public class SiblingsStylesheetTreeController : StylesheetTreeControllerBase
|
||||
{
|
||||
private readonly IStyleSheetTreeService _styleSheetTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public SiblingsStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService)
|
||||
: this(styleSheetTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>())
|
||||
=> _styleSheetTreeService = styleSheetTreeService;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
public SiblingsStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService, FileSystems fileSystems)
|
||||
: base(styleSheetTreeService, fileSystems) =>
|
||||
_styleSheetTreeService = styleSheetTreeService;
|
||||
public SiblingsStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService)
|
||||
: base(styleSheetTreeService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 19")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public SiblingsStylesheetTreeController(IStyleSheetTreeService styleSheetTreeService, FileSystems fileSystems)
|
||||
: base(styleSheetTreeService, fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public SiblingsStylesheetTreeController(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IStyleSheetTreeService>(), fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+8
-12
@@ -1,11 +1,9 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Controllers.Tree;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
|
||||
@@ -16,30 +14,28 @@ namespace Umbraco.Cms.Api.Management.Controllers.Stylesheet.Tree;
|
||||
[Authorize(Policy = AuthorizationPolicies.TreeAccessStylesheets)]
|
||||
public class StylesheetTreeControllerBase : FileSystemTreeControllerBase
|
||||
{
|
||||
private readonly IStyleSheetTreeService _styleSheetTreeService;
|
||||
|
||||
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
|
||||
public StylesheetTreeControllerBase(IStyleSheetTreeService styleSheetTreeService)
|
||||
: this(styleSheetTreeService, StaticServiceProvider.Instance.GetRequiredService<FileSystems>()) =>
|
||||
_styleSheetTreeService = styleSheetTreeService;
|
||||
: base(styleSheetTreeService)
|
||||
{
|
||||
FileSystem = null!;
|
||||
}
|
||||
|
||||
// FileSystem is required therefore, we can't remove it without some wizadry. When obsoletion is due, remove this.
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete("Scheduled for removal in Umbraco 18.")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public StylesheetTreeControllerBase(IStyleSheetTreeService styleSheetTreeService, FileSystems fileSystems)
|
||||
: base(styleSheetTreeService)
|
||||
{
|
||||
_styleSheetTreeService = styleSheetTreeService;
|
||||
FileSystem = fileSystems.ScriptsFileSystem ??
|
||||
throw new ArgumentException("Missing scripts file system", nameof(fileSystems));
|
||||
}
|
||||
|
||||
[Obsolete("Please use the other constructor. Scheduled to be removed in Umbraco 18.")]
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
|
||||
public StylesheetTreeControllerBase(FileSystems fileSystems)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IStyleSheetTreeService>())
|
||||
: base()
|
||||
=> FileSystem = fileSystems.ScriptsFileSystem ??
|
||||
throw new ArgumentException("Missing scripts file system", nameof(fileSystems));
|
||||
|
||||
[Obsolete("Included in the service class. Scheduled to be removed in Umbraco 18.")]
|
||||
[Obsolete("Included in the service class. Scheduled to be removed in Umbraco 19.")]
|
||||
protected override IFileSystem FileSystem { get; }
|
||||
}
|
||||
|
||||
-35
@@ -43,41 +43,6 @@ public class ExecuteTemplateQueryController : TemplateQueryControllerBase
|
||||
_publishedContentStatusFilteringService = publishedContentStatusFilteringService;
|
||||
}
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
|
||||
public ExecuteTemplateQueryController(
|
||||
IPublishedContentQuery publishedContentQuery,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
IContentTypeService contentTypeService,
|
||||
IPublishedContentCache contentCache,
|
||||
IDocumentNavigationQueryService documentNavigationQueryService,
|
||||
IPublishedContentStatusFilteringService publishedContentStatusFilteringService)
|
||||
: this(
|
||||
publishedContentQuery,
|
||||
publishedValueFallback,
|
||||
contentTypeService,
|
||||
documentNavigationQueryService,
|
||||
publishedContentStatusFilteringService)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
|
||||
public ExecuteTemplateQueryController(
|
||||
IPublishedContentQuery publishedContentQuery,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
IContentTypeService contentTypeService,
|
||||
IPublishedContentCache contentCache,
|
||||
IDocumentNavigationQueryService documentNavigationQueryService)
|
||||
: this(
|
||||
publishedContentQuery,
|
||||
publishedValueFallback,
|
||||
contentTypeService,
|
||||
documentNavigationQueryService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
}
|
||||
|
||||
[HttpPost("execute")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(TemplateQueryResultResponseModel), StatusCodes.Status200OK)]
|
||||
|
||||
@@ -5,7 +5,6 @@ using Umbraco.Cms.Api.Management.Extensions;
|
||||
using Umbraco.Cms.Api.Management.Services.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.FileSystem;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -13,23 +12,30 @@ namespace Umbraco.Cms.Api.Management.Controllers.Tree;
|
||||
|
||||
public abstract class FileSystemTreeControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
private readonly IFileSystemTreeService _fileSystemTreeService;
|
||||
private readonly IFileSystemTreeService _fileSystemTreeService = null!;
|
||||
|
||||
[Obsolete("Has been moved to the individual services. Scheduled to be removed in Umbraco 18.")]
|
||||
/// <summary>
|
||||
/// Indicates whether to use the IFileSystemTreeService or the legacy implementation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is retained to ensure that any controllers outside of the CMS that use this base class with the obsolete constructor
|
||||
/// continue to function until they can be updated to use the new service.
|
||||
/// To be removed along with the constructor taking no parameters in Umbraco 19.
|
||||
/// </remarks>
|
||||
private readonly bool _useFileSystemTreeService = true;
|
||||
|
||||
[Obsolete("Has been moved to the individual services. Scheduled to be removed in Umbraco 19.")]
|
||||
protected abstract IFileSystem FileSystem { get; }
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
protected FileSystemTreeControllerBase(IFileSystemTreeService fileSystemTreeService) => _fileSystemTreeService = fileSystemTreeService;
|
||||
|
||||
[Obsolete("Use the other constructor. Scheduled for removal in Umbraco 18.")]
|
||||
protected FileSystemTreeControllerBase()
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IScriptTreeService>())
|
||||
{
|
||||
}
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 19.")]
|
||||
protected FileSystemTreeControllerBase() => _useFileSystemTreeService = false;
|
||||
|
||||
protected Task<ActionResult<PagedViewModel<FileSystemTreeItemPresentationModel>>> GetRoot(int skip, int take)
|
||||
{
|
||||
FileSystemTreeItemPresentationModel[] viewModels = _fileSystemTreeService.GetPathViewModels(string.Empty, skip, take, out var totalItems);
|
||||
FileSystemTreeItemPresentationModel[] viewModels = GetPathViewModels(string.Empty, skip, take, out var totalItems);
|
||||
|
||||
PagedViewModel<FileSystemTreeItemPresentationModel> result = PagedViewModel(viewModels, totalItems);
|
||||
return Task.FromResult<ActionResult<PagedViewModel<FileSystemTreeItemPresentationModel>>>(Ok(result));
|
||||
@@ -37,14 +43,14 @@ public abstract class FileSystemTreeControllerBase : ManagementApiControllerBase
|
||||
|
||||
protected Task<ActionResult<PagedViewModel<FileSystemTreeItemPresentationModel>>> GetChildren(string path, int skip, int take)
|
||||
{
|
||||
FileSystemTreeItemPresentationModel[] viewModels = _fileSystemTreeService.GetPathViewModels(path, skip, take, out var totalItems);
|
||||
FileSystemTreeItemPresentationModel[] viewModels = GetPathViewModels(path, skip, take, out var totalItems);
|
||||
|
||||
PagedViewModel<FileSystemTreeItemPresentationModel> result = PagedViewModel(viewModels, totalItems);
|
||||
return Task.FromResult<ActionResult<PagedViewModel<FileSystemTreeItemPresentationModel>>>(Ok(result));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sibling of the targeted item based on its path.
|
||||
/// Gets the siblings of the targeted item based on its path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the item.</param>
|
||||
/// <param name="before">The amount of siblings you want to fetch from before the items position in the array.</param>
|
||||
@@ -61,17 +67,19 @@ public abstract class FileSystemTreeControllerBase : ManagementApiControllerBase
|
||||
protected virtual Task<ActionResult<IEnumerable<FileSystemTreeItemPresentationModel>>> GetAncestors(string path, bool includeSelf = true)
|
||||
{
|
||||
path = path.VirtualPathToSystemPath();
|
||||
FileSystemTreeItemPresentationModel[] models = _fileSystemTreeService.GetAncestorModels(path, includeSelf);
|
||||
FileSystemTreeItemPresentationModel[] models = GetAncestorModels(path, includeSelf);
|
||||
|
||||
return Task.FromResult<ActionResult<IEnumerable<FileSystemTreeItemPresentationModel>>>(Ok(models));
|
||||
}
|
||||
|
||||
private PagedViewModel<FileSystemTreeItemPresentationModel> PagedViewModel(IEnumerable<FileSystemTreeItemPresentationModel> viewModels, long totalItems)
|
||||
=> new() { Total = totalItems, Items = viewModels };
|
||||
|
||||
[Obsolete("Has been moved to FileSystemTreeServiceBase. Scheduled for removal in Umbraco 18.")]
|
||||
[Obsolete("Has been moved to FileSystemTreeServiceBase. Scheduled for removal in Umbraco 19.")]
|
||||
protected virtual FileSystemTreeItemPresentationModel[] GetAncestorModels(string path, bool includeSelf)
|
||||
{
|
||||
if (_useFileSystemTreeService)
|
||||
{
|
||||
return _fileSystemTreeService.GetAncestorModels(path, includeSelf);
|
||||
}
|
||||
|
||||
var directories = path.Split(Path.DirectorySeparatorChar).Take(Range.EndAt(Index.FromEnd(1))).ToArray();
|
||||
var result = directories
|
||||
.Select((directory, index) => MapViewModel(string.Join(Path.DirectorySeparatorChar, directories.Take(index + 1)), directory, true))
|
||||
@@ -86,28 +94,59 @@ public abstract class FileSystemTreeControllerBase : ManagementApiControllerBase
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
[Obsolete("Has been moved to FileSystemTreeServiceBase. Scheduled for removal in Umbraco 18.")]
|
||||
[Obsolete("Has been moved to FileSystemTreeServiceBase. Scheduled for removal in Umbraco 19.")]
|
||||
protected virtual string[] GetDirectories(string path) => FileSystem
|
||||
.GetDirectories(path)
|
||||
.OrderBy(directory => directory)
|
||||
.ToArray();
|
||||
|
||||
[Obsolete("Has been moved to FileSystemTreeServiceBase. Scheduled for removal in Umbraco 18.")]
|
||||
[Obsolete("Has been moved to FileSystemTreeServiceBase. Scheduled for removal in Umbraco 19.")]
|
||||
protected virtual string[] GetFiles(string path) => FileSystem
|
||||
.GetFiles(path)
|
||||
.OrderBy(file => file)
|
||||
.ToArray();
|
||||
|
||||
[Obsolete("Has been moved to FileSystemTreeServiceBase. Scheduled for removal in Umbraco 18.")]
|
||||
[Obsolete("Has been moved to FileSystemTreeServiceBase. Scheduled for removal in Umbraco 19.")]
|
||||
protected virtual bool DirectoryHasChildren(string path)
|
||||
=> FileSystem.GetFiles(path).Any() || FileSystem.GetDirectories(path).Any();
|
||||
|
||||
[Obsolete("Has been moved to FileSystemTreeServiceBase. Scheduled for removal in Umbraco 18.")]
|
||||
[Obsolete("Has been moved to FileSystemTreeServiceBase. Scheduled for removal in Umbraco 19.")]
|
||||
private string GetFileSystemItemName(bool isFolder, string itemPath) => isFolder
|
||||
? Path.GetFileName(itemPath)
|
||||
: FileSystem.GetFileName(itemPath);
|
||||
|
||||
[Obsolete("Has been moved to FileSystemTreeServiceBase. Scheduled for removal in Umbraco 18.")]
|
||||
private FileSystemTreeItemPresentationModel[] GetPathViewModels(string path, int skip, int take, out long totalItems)
|
||||
{
|
||||
if (_useFileSystemTreeService)
|
||||
{
|
||||
return _fileSystemTreeService.GetPathViewModels(path, skip, take, out totalItems);
|
||||
}
|
||||
|
||||
path = path.VirtualPathToSystemPath();
|
||||
var allItems = GetDirectories(path)
|
||||
.Select(directory => new { Path = directory, IsFolder = true })
|
||||
.Union(GetFiles(path).Select(file => new { Path = file, IsFolder = false }))
|
||||
.ToArray();
|
||||
|
||||
totalItems = allItems.Length;
|
||||
|
||||
FileSystemTreeItemPresentationModel ViewModel(string itemPath, bool isFolder)
|
||||
=> MapViewModel(
|
||||
itemPath,
|
||||
GetFileSystemItemName(isFolder, itemPath),
|
||||
isFolder);
|
||||
|
||||
return allItems
|
||||
.Skip(skip)
|
||||
.Take(take)
|
||||
.Select(item => ViewModel(item.Path, item.IsFolder))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private PagedViewModel<FileSystemTreeItemPresentationModel> PagedViewModel(IEnumerable<FileSystemTreeItemPresentationModel> viewModels, long totalItems)
|
||||
=> new() { Total = totalItems, Items = viewModels };
|
||||
|
||||
[Obsolete("Has been moved to FileSystemTreeServiceBase. Scheduled for removal in Umbraco 19.")]
|
||||
private FileSystemTreeItemPresentationModel MapViewModel(string path, string name, bool isFolder)
|
||||
{
|
||||
var parentPath = Path.GetDirectoryName(path);
|
||||
|
||||
@@ -11,13 +11,13 @@ internal static class AuditLogBuilderExtensions
|
||||
internal static IUmbracoBuilder AddAuditLogs(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddTransient<IAuditLogPresentationFactory, AuditLogPresentationFactory>();
|
||||
builder.AddNotificationHandler<UserLoginSuccessNotification, BackOfficeUserManagerAuditer>();
|
||||
builder.AddNotificationHandler<UserLogoutSuccessNotification, BackOfficeUserManagerAuditer>();
|
||||
builder.AddNotificationHandler<UserLoginFailedNotification, BackOfficeUserManagerAuditer>();
|
||||
builder.AddNotificationHandler<UserForgotPasswordRequestedNotification, BackOfficeUserManagerAuditer>();
|
||||
builder.AddNotificationHandler<UserForgotPasswordChangedNotification, BackOfficeUserManagerAuditer>();
|
||||
builder.AddNotificationHandler<UserPasswordChangedNotification, BackOfficeUserManagerAuditer>();
|
||||
builder.AddNotificationHandler<UserPasswordResetNotification, BackOfficeUserManagerAuditer>();
|
||||
builder.AddNotificationAsyncHandler<UserLoginSuccessNotification, BackOfficeUserManagerAuditer>();
|
||||
builder.AddNotificationAsyncHandler<UserLogoutSuccessNotification, BackOfficeUserManagerAuditer>();
|
||||
builder.AddNotificationAsyncHandler<UserLoginFailedNotification, BackOfficeUserManagerAuditer>();
|
||||
builder.AddNotificationAsyncHandler<UserForgotPasswordRequestedNotification, BackOfficeUserManagerAuditer>();
|
||||
builder.AddNotificationAsyncHandler<UserForgotPasswordChangedNotification, BackOfficeUserManagerAuditer>();
|
||||
builder.AddNotificationAsyncHandler<UserPasswordChangedNotification, BackOfficeUserManagerAuditer>();
|
||||
builder.AddNotificationAsyncHandler<UserPasswordResetNotification, BackOfficeUserManagerAuditer>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Services.NewsDashboard;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
|
||||
internal static class NewsDashboardBuilderExtensions
|
||||
{
|
||||
internal static IUmbracoBuilder AddNewsDashboard(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddSingleton<INewsDashboardService, NewsDashboardService>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ public static class InstallerBuilderExtensions
|
||||
|
||||
builder.AddInstallSteps();
|
||||
services.AddTransient<IInstallService, InstallService>();
|
||||
builder.AddNotificationAsyncHandler<UnattendedInstallNotification, CreateUnattendedUserNotificationHandler>();
|
||||
builder.AddNotificationAsyncHandler<UnattendedInstallNotification, PostUnattendedInstallNotificationHandler>();
|
||||
builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>().Add<InstallerViewModelsMapDefinition>();
|
||||
|
||||
return builder;
|
||||
|
||||
@@ -30,7 +30,7 @@ public static partial class UmbracoBuilderExtensions
|
||||
.AddMembersIdentity()
|
||||
.AddUmbracoProfiler()
|
||||
.AddMvcAndRazor(configureMvc)
|
||||
.AddRecurringBackgroundJobs()
|
||||
.AddBackgroundJobs()
|
||||
.AddUmbracoHybridCache()
|
||||
.AddDistributedCache()
|
||||
.AddCoreNotifications();
|
||||
|
||||
@@ -10,18 +10,26 @@ namespace Umbraco.Extensions
|
||||
{
|
||||
internal static void AddCollectionBuilders(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.SignProviders()
|
||||
builder.FlagProviders()
|
||||
.Append<HasScheduleFlagProvider>()
|
||||
.Append<IsProtectedFlagProvider>()
|
||||
.Append<HasPendingChangesFlagProvider>()
|
||||
.Append<HasCollectionFlagProvider>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag providers collection builder.
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
public static FlagProviderCollectionBuilder FlagProviders(this IUmbracoBuilder builder)
|
||||
=> builder.WithCollectionBuilder<FlagProviderCollectionBuilder>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sign providers collection builder.
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
[Obsolete("Please use the correctly named FlagProviders. Scheduled for removal in Umbraco 19.")]
|
||||
public static FlagProviderCollectionBuilder SignProviders(this IUmbracoBuilder builder)
|
||||
=> builder.WithCollectionBuilder<FlagProviderCollectionBuilder>();
|
||||
=> builder.FlagProviders();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,8 @@ public static partial class UmbracoBuilderExtensions
|
||||
.AddUserData()
|
||||
.AddSegment()
|
||||
.AddExport()
|
||||
.AddImport();
|
||||
.AddImport()
|
||||
.AddNewsDashboard();
|
||||
|
||||
services
|
||||
.ConfigureOptions<ConfigureApiBehaviorOptions>()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user