Compare commits
209
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f1ce04871 | ||
|
|
0137b214d3 | ||
|
|
7dcf329d2c | ||
|
|
c422a9ea6a | ||
|
|
924af4b565 | ||
|
|
298db76cb1 | ||
|
|
2248eeb03e | ||
|
|
f6df0c1828 | ||
|
|
44d52392b5 | ||
|
|
63ae29a1aa | ||
|
|
6effe32136 | ||
|
|
4717264e10 | ||
|
|
f0ac7b4a50 | ||
|
|
5f0122e18b | ||
|
|
de5a9ca5af | ||
|
|
58068d1aa7 | ||
|
|
5f1c65e7ea | ||
|
|
20eedda4c7 | ||
|
|
1099332edb | ||
|
|
3a83e4a494 | ||
|
|
af3b17243b | ||
|
|
d05add4e1f | ||
|
|
68d1b9481a | ||
|
|
e6f48799a1 | ||
|
|
1efdde2473 | ||
|
|
c666c00ac5 | ||
|
|
94692cccb7 | ||
|
|
ab4be79da0 | ||
|
|
a808e3ab46 | ||
|
|
92dbaf7162 | ||
|
|
ae2c59b703 | ||
|
|
72d44decd4 | ||
|
|
7e7d9da144 | ||
|
|
b142dcc84f | ||
|
|
8b1f18699d | ||
|
|
f82c0a90e7 | ||
|
|
5278b67f60 | ||
|
|
65798b5722 | ||
|
|
7d0adc4755 | ||
|
|
11bf60a67c | ||
|
|
de0503d90c | ||
|
|
4a504e8c95 | ||
|
|
271edb5214 | ||
|
|
8beb7f2acc | ||
|
|
498754e170 | ||
|
|
ae73fb3431 | ||
|
|
3830d75413 | ||
|
|
f1f2f702c0 | ||
|
|
c5c417d08e | ||
|
|
2503cabbce | ||
|
|
cee163ae1f | ||
|
|
7e8a7f0b40 | ||
|
|
126c8a56d5 | ||
|
|
2412c662df | ||
|
|
99ea8b9087 | ||
|
|
b25feac011 | ||
|
|
86dda9f9b3 | ||
|
|
a95fd9f340 | ||
|
|
9c082c613e | ||
|
|
5af9df1871 | ||
|
|
12297ea617 | ||
|
|
3892670d92 | ||
|
|
07d0d7d2ee | ||
|
|
d9de69c40f | ||
|
|
ac56bffef2 | ||
|
|
49f3fc9993 | ||
|
|
068183ac35 | ||
|
|
7f1cdf8ef5 | ||
|
|
37b239b8ca | ||
|
|
ea44850804 | ||
|
|
4dbb4eb48b | ||
|
|
b5662d9c89 | ||
|
|
4c42175ffc | ||
|
|
8be2d77006 | ||
|
|
a6eb2a1527 | ||
|
|
11fcfba091 | ||
|
|
3393febdca | ||
|
|
28e4caaad3 | ||
|
|
d6b9eb61b2 | ||
|
|
0367e56db6 | ||
|
|
e28153f004 | ||
|
|
2ce6c8f8c8 | ||
|
|
fd1c9f1673 | ||
|
|
2c284a702a | ||
|
|
c68a26cb5f | ||
|
|
c037323910 | ||
|
|
f60436270f | ||
|
|
9b0b60e6cb | ||
|
|
0996891c5c | ||
|
|
7a48c11ddd | ||
|
|
de449079dd | ||
|
|
10bcf5ba72 | ||
|
|
5f14365470 | ||
|
|
f539a27a19 | ||
|
|
296858c1ca | ||
|
|
832bd7eb61 | ||
|
|
64836e0b7a | ||
|
|
d6ce8d91a9 | ||
|
|
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 |
+3
-10
@@ -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,20 +73,13 @@ 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",
|
||||
"BackOfficeTokenCookie": {
|
||||
"Enabled": true,
|
||||
"SameSite": "None"
|
||||
}
|
||||
"AuthorizeCallbackErrorPathName": "/error"
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> If you get stuck in a login loop, try clearing your browser cookies for localhost, and make sure that the `BackOfficeTokenCookie` settings are correct. Namely, that `SameSite` should be set to `None` when running the front-end server separately.
|
||||
|
||||
Then run Umbraco from the command line.
|
||||
|
||||
```
|
||||
|
||||
@@ -38,14 +38,6 @@ Some important documentation links to get you started:
|
||||
- [Getting to know Umbraco](https://docs.umbraco.com/umbraco-cms/fundamentals/get-to-know-umbraco)
|
||||
- [Tutorials for creating a basic website and customizing the editing experience](https://docs.umbraco.com/umbraco-cms/tutorials/overview)
|
||||
|
||||
## Backoffice Preview
|
||||
|
||||
Want to see the latest backoffice UI in action? Check out our live preview:
|
||||
|
||||
**[backofficepreview.umbraco.com](https://backofficepreview.umbraco.com/)**
|
||||
|
||||
This preview is automatically deployed from the main branch and showcases the latest backoffice features and improvements. It runs from mock data and persistent edits are not supported.
|
||||
|
||||
## Get help
|
||||
|
||||
If you need a bit of feedback while building your Umbraco projects, we are [chatty on Discord](https://discord.umbraco.com). Our Discord server serves as a social space for all Umbracians. If you have any questions or need some help with a problem, head over to our [dedicated forum](https://forum.umbraco.com/) where the Umbraco Community will be happy to help.
|
||||
|
||||
@@ -110,11 +110,7 @@ Use this for frontend-only development with hot module reloading:
|
||||
"BackOfficeHost": "http://localhost:5173",
|
||||
"AuthorizeCallbackPathName": "/oauth_complete",
|
||||
"AuthorizeCallbackLogoutPathName": "/logout",
|
||||
"AuthorizeCallbackErrorPathName": "/error",
|
||||
"BackOfficeTokenCookie": {
|
||||
"Enabled": true,
|
||||
"SameSite": "None"
|
||||
}
|
||||
"AuthorizeCallbackErrorPathName": "/error"
|
||||
```
|
||||
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`
|
||||
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
for (const item of items) {
|
||||
const releaseLabels = (item.labels || [])
|
||||
.map(l => (typeof l === "string" ? l : l.name)) // always get the name
|
||||
.filter(n => typeof n === "string" && n.startsWith("release/") && n !== "release/no-notes");
|
||||
.filter(n => typeof n === "string" && n.startsWith("release/"));
|
||||
if (releaseLabels.length === 0) continue;
|
||||
|
||||
core.info(`#${item.number}: ${releaseLabels.join(", ")}`);
|
||||
|
||||
Vendored
+1
-4
@@ -105,10 +105,7 @@
|
||||
"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__KEEPUSERLOGGEDIN": "true",
|
||||
"UMBRACO__CMS__SECURITY__BACKOFFICETOKENCOOKIE__ENABLED": "true",
|
||||
"UMBRACO__CMS__SECURITY__BACKOFFICETOKENCOOKIE__SAMESITE": "None"
|
||||
"UMBRACO__CMS__SECURITY__AUTHORIZECALLBACKERRORPATHNAME": "/error"
|
||||
},
|
||||
"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
-58
@@ -5,36 +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.CSharp.Workspaces" 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.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-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.9.0" />
|
||||
<PackageVersion Include="System.Linq.Async" Version="6.0.3" />
|
||||
</ItemGroup>
|
||||
<!-- Umbraco packages -->
|
||||
<ItemGroup>
|
||||
@@ -47,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" />
|
||||
@@ -71,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="9.0.6" />
|
||||
</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>
|
||||
|
||||
+55
-91
@@ -34,10 +34,6 @@ 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
|
||||
@@ -83,6 +79,7 @@ stages:
|
||||
jobs:
|
||||
- job: A
|
||||
displayName: Build Umbraco CMS
|
||||
dependsOn: B
|
||||
pool:
|
||||
vmImage: "windows-latest"
|
||||
steps:
|
||||
@@ -90,7 +87,11 @@ stages:
|
||||
submodules: false
|
||||
lfs: false,
|
||||
fetchDepth: 500
|
||||
- template: templates/backoffice-install.yml
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Frontend Assets
|
||||
inputs:
|
||||
artifact: static-assets
|
||||
targetPath: src/Umbraco.Cms.StaticAssets/wwwroot/umbraco
|
||||
- task: UseDotNet@2
|
||||
displayName: Use .NET SDK from global.json
|
||||
inputs:
|
||||
@@ -107,15 +108,6 @@ 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:
|
||||
@@ -126,14 +118,19 @@ stages:
|
||||
inputs:
|
||||
targetPath: $(Build.SourcesDirectory)
|
||||
artifactName: build_output
|
||||
- script: |
|
||||
dotnet tool install --global CycloneDX
|
||||
dotnet-CycloneDX $(solution) --output $(Build.ArtifactStagingDirectory)/bom --filename bom-backend.xml
|
||||
name: Generate_Backend_BOM
|
||||
displayName: 'Generate Backend BOM'
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: Publish Backend BOM
|
||||
displayName: Publish Backend UI BOM
|
||||
inputs:
|
||||
targetPath: $(Build.ArtifactStagingDirectory)/bom
|
||||
artifactName: bom-backend
|
||||
artifactName: bom
|
||||
|
||||
- job: B
|
||||
displayName: Build Bellissima Package
|
||||
displayName: Build and package Frontend
|
||||
pool:
|
||||
vmImage: "ubuntu-latest"
|
||||
steps:
|
||||
@@ -142,13 +139,8 @@ 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
|
||||
displayName: Run Bellissima NPM build
|
||||
workingDirectory: src/Umbraco.Web.UI.Client
|
||||
- bash: |
|
||||
echo "##[command]Running npm pack"
|
||||
@@ -156,42 +148,43 @@ stages:
|
||||
mkdir $(Build.ArtifactStagingDirectory)/npm
|
||||
npm pack --pack-destination $(Build.ArtifactStagingDirectory)/npm
|
||||
mv .npmrc $(Build.ArtifactStagingDirectory)/npm/
|
||||
displayName: Run npm pack
|
||||
displayName: Package Bellissima NPM
|
||||
workingDirectory: src/Umbraco.Web.UI.Client
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: Publish Bellissima npm artifact
|
||||
displayName: Publish Bellissima NPM artifact
|
||||
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'
|
||||
- script: |
|
||||
rm -rf dist-cms
|
||||
npm run build:for:cms
|
||||
displayName: Run Backoffice CMS build
|
||||
workingDirectory: src/Umbraco.Web.UI.Client
|
||||
- script: |
|
||||
npm ci --no-fund --no-audit
|
||||
npm run build
|
||||
displayName: Run Login CMS build
|
||||
workingDirectory: src/Umbraco.Web.UI.Login
|
||||
- script: |
|
||||
mkdir -p $(Build.ArtifactStagingDirectory)/static-assets
|
||||
cp -R ../Umbraco.Cms.StaticAssets/wwwroot/umbraco/backoffice $(Build.ArtifactStagingDirectory)/static-assets/backoffice
|
||||
cp -R ../Umbraco.Cms.StaticAssets/wwwroot/umbraco/login $(Build.ArtifactStagingDirectory)/static-assets/login
|
||||
displayName: Aggregate Frontend Assets
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: Publish Frontend Assets artifact
|
||||
inputs:
|
||||
targetPath: $(Build.ArtifactStagingDirectory)/static-assets
|
||||
artifactName: static-assets
|
||||
- script: |
|
||||
npm install --global @cyclonedx/cyclonedx-npm
|
||||
cyclonedx-npm src/Umbraco.Web.UI.Client/package.json -o $(Build.ArtifactStagingDirectory)/bom/bom-backoffice.xml --ignore-npm-errors
|
||||
cyclonedx-npm src/Umbraco.Web.UI.Login/package.json -o $(Build.ArtifactStagingDirectory)/bom/bom-login.xml --ignore-npm-errors
|
||||
displayName: Generate UI BOMs
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: Publish UI BOMs
|
||||
inputs:
|
||||
targetPath: $(Build.ArtifactStagingDirectory)/bom
|
||||
artifactName: bom
|
||||
|
||||
- stage: Build_Docs
|
||||
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.buildApiDocs}}))
|
||||
@@ -365,8 +358,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
|
||||
@@ -377,8 +370,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:
|
||||
@@ -441,8 +434,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!
|
||||
@@ -462,8 +455,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:
|
||||
@@ -720,34 +713,6 @@ 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
|
||||
###############################################
|
||||
@@ -954,4 +919,3 @@ stages:
|
||||
ContainerName: "$web"
|
||||
BlobPrefix: v$(umbracoMajorVersion)/ui-api
|
||||
CleanTargetBeforeCopy: true
|
||||
|
||||
|
||||
@@ -26,18 +26,38 @@ 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
|
||||
|
||||
- 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 }}
|
||||
- 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
|
||||
|
||||
# Install Template
|
||||
- pwsh: |
|
||||
|
||||
@@ -4,11 +4,13 @@ pr: none
|
||||
trigger: none
|
||||
|
||||
schedules:
|
||||
- cron: '0 6 * * *'
|
||||
displayName: Daily 6 AM build (v16/dev)
|
||||
- cron: '0 0 * * *'
|
||||
displayName: Daily midnight build
|
||||
branches:
|
||||
include:
|
||||
- v16/dev
|
||||
- v15/dev
|
||||
- main
|
||||
- v17/dev
|
||||
|
||||
parameters:
|
||||
- name: skipIntegrationTests
|
||||
@@ -19,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
|
||||
@@ -121,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:
|
||||
@@ -185,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:
|
||||
@@ -293,8 +315,7 @@ stages:
|
||||
|
||||
- stage: DefaultConfigE2E
|
||||
displayName: Default Config E2E Tests
|
||||
dependsOn: Integration
|
||||
condition: always()
|
||||
dependsOn: Build
|
||||
variables:
|
||||
npm_config_cache: $(Pipeline.Workspace)/.npm_e2e
|
||||
# Enable console logging in Release mode
|
||||
@@ -475,8 +496,7 @@ stages:
|
||||
|
||||
- stage: AdditionalConfigE2E
|
||||
displayName: Additional Config E2E Tests
|
||||
dependsOn: DefaultConfigE2E
|
||||
condition: always()
|
||||
dependsOn: Build
|
||||
variables:
|
||||
npm_config_cache: $(Pipeline.Workspace)/.npm_e2e
|
||||
ASPNETCORE_URLS: https://localhost:44331
|
||||
@@ -485,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!
|
||||
@@ -675,4 +695,4 @@ stages:
|
||||
--data "$PAYLOAD" \
|
||||
"$SLACK_WEBHOOK_URL"
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: $(E2ESLACKWEBHOOKURL)
|
||||
SLACK_WEBHOOK_URL: $(E2ESLACKWEBHOOKURL)
|
||||
@@ -1,56 +0,0 @@
|
||||
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
|
||||
@@ -1,49 +0,0 @@
|
||||
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
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "9.0.306",
|
||||
"version": "10.0.100-rc.2.25502.107",
|
||||
"rollForward": "latestFeature",
|
||||
"allowPrerelease": false
|
||||
"allowPrerelease": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
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]";
|
||||
private const string AccessTokenCookieKey = "__Host-umbAccessToken";
|
||||
private const string RefreshTokenCookieKey = "__Host-umbRefreshToken";
|
||||
private const string PkceCodeCookieKey = "__Host-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, AccessTokenCookieKey, context.Response.AccessToken);
|
||||
context.Response.AccessToken = RedactedTokenValue;
|
||||
}
|
||||
|
||||
if (context.Response.RefreshToken is not null)
|
||||
{
|
||||
SetCookie(httpContext, RefreshTokenCookieKey, 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(), PkceCodeCookieKey, 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;
|
||||
}
|
||||
|
||||
// Handle when the PKCE code is being exchanged for an access token.
|
||||
if (context.Request.Code == RedactedTokenValue
|
||||
&& TryGetCookie(PkceCodeCookieKey, out var code))
|
||||
{
|
||||
context.Request.Code = code;
|
||||
|
||||
// We won't need the PKCE cookie after this, let's remove it.
|
||||
RemoveCookie(GetHttpContext(), PkceCodeCookieKey);
|
||||
}
|
||||
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(RefreshTokenCookieKey, 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(AccessTokenCookieKey, out var accessToken))
|
||||
{
|
||||
context.AccessToken = accessToken;
|
||||
}
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
public void Handle(UserLogoutSuccessNotification notification)
|
||||
{
|
||||
HttpContext? context = _httpContextAccessor.HttpContext;
|
||||
if (context 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;
|
||||
}
|
||||
|
||||
context.Response.Cookies.Delete(AccessTokenCookieKey);
|
||||
context.Response.Cookies.Delete(RefreshTokenCookieKey);
|
||||
}
|
||||
|
||||
private HttpContext GetHttpContext()
|
||||
=> _httpContextAccessor.GetRequiredHttpContext();
|
||||
|
||||
private void SetCookie(HttpContext httpContext, string key, string value)
|
||||
{
|
||||
var cookieValue = EncryptionHelper.Encrypt(value, _dataProtectionProvider);
|
||||
|
||||
RemoveCookie(httpContext, key);
|
||||
httpContext.Response.Cookies.Append(key, cookieValue, GetCookieOptions(httpContext));
|
||||
}
|
||||
|
||||
private void RemoveCookie(HttpContext httpContext, string key)
|
||||
=> 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(string key, [NotNullWhen(true)] out string? value)
|
||||
{
|
||||
if (GetHttpContext().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,9 +9,8 @@ using Umbraco.Cms.Api.Common.Security;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Cms.Infrastructure.BackgroundJobs;
|
||||
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||
|
||||
namespace Umbraco.Cms.Api.Common.DependencyInjection;
|
||||
|
||||
@@ -29,11 +28,6 @@ public static class UmbracoBuilderAuthExtensions
|
||||
|
||||
private static void ConfigureOpenIddict(IUmbracoBuilder builder)
|
||||
{
|
||||
// Optionally hide tokens from the back-office.
|
||||
var hideBackOfficeTokens = (builder.Config
|
||||
.GetSection(Constants.Configuration.ConfigBackOfficeTokenCookie)
|
||||
.Get<BackOfficeTokenCookieSettings>() ?? new BackOfficeTokenCookieSettings()).Enabled;
|
||||
|
||||
builder.Services.AddOpenIddict()
|
||||
// Register the OpenIddict server components.
|
||||
.AddServer(options =>
|
||||
@@ -119,28 +113,6 @@ public static class UmbracoBuilderAuthExtensions
|
||||
{
|
||||
configuration.UseSingletonHandler<ProcessRequestContextHandler>().SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.ResolveRequestUri.Descriptor.Order - 1);
|
||||
});
|
||||
|
||||
if (hideBackOfficeTokens)
|
||||
{
|
||||
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.
|
||||
@@ -165,25 +137,9 @@ public static class UmbracoBuilderAuthExtensions
|
||||
{
|
||||
configuration.UseSingletonHandler<ProcessRequestContextHandler>().SetOrder(OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers.ResolveRequestUri.Descriptor.Order - 1);
|
||||
});
|
||||
|
||||
if (hideBackOfficeTokens)
|
||||
{
|
||||
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>();
|
||||
|
||||
if (hideBackOfficeTokens)
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +1,31 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core.Actions;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Security.Authorization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Document;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class DomainsController : DocumentControllerBase
|
||||
{
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly IDomainService _domainService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public DomainsController(IAuthorizationService authorizationService, IDomainService domainService, IUmbracoMapper umbracoMapper)
|
||||
public DomainsController(IDomainService domainService, IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_authorizationService = authorizationService;
|
||||
_domainService = domainService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
|
||||
public DomainsController(IDomainService domainService, IUmbracoMapper umbracoMapper)
|
||||
: this(
|
||||
StaticServiceProvider.Instance.GetRequiredService<IAuthorizationService>(),
|
||||
domainService,
|
||||
umbracoMapper)
|
||||
{
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[HttpGet("{id:guid}/domains")]
|
||||
[ProducesResponseType(typeof(DomainsResponseModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> Domains(CancellationToken cancellationToken, Guid id)
|
||||
{
|
||||
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
|
||||
User,
|
||||
ContentPermissionResource.WithKeys(ActionBrowse.ActionLetter, id),
|
||||
AuthorizationPolicies.ContentPermissionByResource);
|
||||
|
||||
if (!authorizationResult.Succeeded)
|
||||
{
|
||||
return Forbidden();
|
||||
}
|
||||
|
||||
IDomain[] assignedDomains = (await _domainService.GetAssignedDomainsAsync(id, true))
|
||||
.OrderBy(d => d.SortOrder)
|
||||
.ToArray();
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Actions;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Cms.Core.Security.Authorization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Document;
|
||||
@@ -21,30 +15,17 @@ namespace Umbraco.Cms.Api.Management.Controllers.Document;
|
||||
[ApiVersion("1.0")]
|
||||
public class UpdateDomainsController : DocumentControllerBase
|
||||
{
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly IDomainService _domainService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
private readonly IDomainPresentationFactory _domainPresentationFactory;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public UpdateDomainsController(IAuthorizationService authorizationService, IDomainService domainService, IUmbracoMapper umbracoMapper, IDomainPresentationFactory domainPresentationFactory)
|
||||
public UpdateDomainsController(IDomainService domainService, IUmbracoMapper umbracoMapper, IDomainPresentationFactory domainPresentationFactory)
|
||||
{
|
||||
_authorizationService = authorizationService;
|
||||
_domainService = domainService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
_domainPresentationFactory = domainPresentationFactory;
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
|
||||
public UpdateDomainsController(IDomainService domainService, IUmbracoMapper umbracoMapper, IDomainPresentationFactory domainPresentationFactory)
|
||||
: this(
|
||||
StaticServiceProvider.Instance.GetRequiredService<IAuthorizationService>(),
|
||||
domainService,
|
||||
umbracoMapper,
|
||||
domainPresentationFactory)
|
||||
{
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[HttpPut("{id:guid}/domains")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
@@ -56,16 +37,6 @@ public class UpdateDomainsController : DocumentControllerBase
|
||||
Guid id,
|
||||
UpdateDomainsRequestModel updateModel)
|
||||
{
|
||||
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
|
||||
User,
|
||||
ContentPermissionResource.WithKeys(ActionAssignDomain.ActionLetter, id),
|
||||
AuthorizationPolicies.ContentPermissionByResource);
|
||||
|
||||
if (!authorizationResult.Succeeded)
|
||||
{
|
||||
return Forbidden();
|
||||
}
|
||||
|
||||
DomainsUpdateModel domainsUpdateModel = _umbracoMapper.Map<DomainsUpdateModel>(updateModel)!;
|
||||
|
||||
Attempt<DomainUpdateResult, DomainOperationStatus> result = await _domainService.UpdateDomainsAsync(id, domainsUpdateModel);
|
||||
|
||||
+1
-31
@@ -1,63 +1,33 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core.Actions;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Security.Authorization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Document;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class UpdateNotificationsController : DocumentControllerBase
|
||||
{
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly IContentEditingService _contentEditingService;
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public UpdateNotificationsController(IAuthorizationService authorizationService, IContentEditingService contentEditingService, INotificationService notificationService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
public UpdateNotificationsController(IContentEditingService contentEditingService, INotificationService notificationService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
{
|
||||
_authorizationService = authorizationService;
|
||||
_contentEditingService = contentEditingService;
|
||||
_notificationService = notificationService;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
|
||||
public UpdateNotificationsController(IContentEditingService contentEditingService, INotificationService notificationService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
: this(
|
||||
StaticServiceProvider.Instance.GetRequiredService<IAuthorizationService>(),
|
||||
contentEditingService,
|
||||
notificationService,
|
||||
backOfficeSecurityAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[HttpPut("{id:guid}/notifications")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> UpdateNotifications(CancellationToken cancellationToken, Guid id, UpdateDocumentNotificationsRequestModel updateModel)
|
||||
{
|
||||
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
|
||||
User,
|
||||
ContentPermissionResource.WithKeys(ActionBrowse.ActionLetter, id),
|
||||
AuthorizationPolicies.ContentPermissionByResource);
|
||||
|
||||
if (!authorizationResult.Succeeded)
|
||||
{
|
||||
return Forbidden();
|
||||
}
|
||||
|
||||
IContent? content = await _contentEditingService.GetAsync(id);
|
||||
if (content == null)
|
||||
{
|
||||
|
||||
-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)]
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
+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));
|
||||
}
|
||||
}
|
||||
|
||||
+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);
|
||||
}
|
||||
+3
-1
@@ -5,6 +5,7 @@ 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;
|
||||
@@ -39,7 +40,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
|
||||
{
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
-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)]
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
using Asp.Versioning;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Controllers.UserGroup;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.User;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Security.Authorization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
@@ -28,26 +25,11 @@ public class UpdateUserGroupsUserController : UserGroupControllerBase
|
||||
{
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly IUserGroupService _userGroupService;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public UpdateUserGroupsUserController(
|
||||
IAuthorizationService authorizationService,
|
||||
IUserGroupService userGroupService,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
public UpdateUserGroupsUserController(IAuthorizationService authorizationService, IUserGroupService userGroupService)
|
||||
{
|
||||
_authorizationService = authorizationService;
|
||||
_userGroupService = userGroupService;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor accepting all parameters. Scheduled for removal in Umbraco 19.")]
|
||||
public UpdateUserGroupsUserController(IAuthorizationService authorizationService, IUserGroupService userGroupService)
|
||||
: this(
|
||||
authorizationService,
|
||||
userGroupService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IBackOfficeSecurityAccessor>())
|
||||
{
|
||||
}
|
||||
|
||||
[HttpPost("set-user-groups")]
|
||||
@@ -69,8 +51,7 @@ public class UpdateUserGroupsUserController : UserGroupControllerBase
|
||||
|
||||
Attempt<UserGroupOperationStatus> result = await _userGroupService.UpdateUserGroupsOnUsersAsync(
|
||||
requestModel.UserGroupIds.Select(x => x.Id).ToHashSet(),
|
||||
requestModel.UserIds.Select(x => x.Id).ToHashSet(),
|
||||
CurrentUserKey(_backOfficeSecurityAccessor));
|
||||
requestModel.UserIds.Select(x => x.Id).ToHashSet());
|
||||
|
||||
return result.Success
|
||||
? Ok()
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -71,7 +71,8 @@ public static partial class UmbracoBuilderExtensions
|
||||
.AddUserData()
|
||||
.AddSegment()
|
||||
.AddExport()
|
||||
.AddImport();
|
||||
.AddImport()
|
||||
.AddNewsDashboard();
|
||||
|
||||
services
|
||||
.ConfigureOptions<ConfigureApiBehaviorOptions>()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Common.Accessors;
|
||||
using Umbraco.Cms.Api.Common.Rendering;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
@@ -16,10 +16,9 @@ internal static class WebhooksBuilderExtensions
|
||||
builder.Services.AddUnique<IWebhookPresentationFactory, WebhookPresentationFactory>();
|
||||
builder.AddMapDefinition<WebhookEventMapDefinition>();
|
||||
|
||||
// We have to use TryAdd here, as if they are registered by the delivery API, we don't want to register them
|
||||
// Delivery API will also overwrite these IF it is enabled.
|
||||
builder.Services.TryAddScoped<IOutputExpansionStrategy, ElementOnlyOutputExpansionStrategy>();
|
||||
builder.Services.TryAddSingleton<IOutputExpansionStrategyAccessor, RequestContextOutputExpansionStrategyAccessor>();
|
||||
// deliveryApi will overwrite these more basic ones.
|
||||
builder.Services.AddScoped<IOutputExpansionStrategy, ElementOnlyOutputExpansionStrategy>();
|
||||
builder.Services.AddSingleton<IOutputExpansionStrategyAccessor, RequestContextOutputExpansionStrategyAccessor>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -14,15 +14,6 @@ public class DocumentCollectionPresentationFactory : ContentCollectionPresentati
|
||||
private readonly IPublicAccessService _publicAccessService;
|
||||
private readonly IEntityService _entityService;
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Scheduled for removal in V17.")]
|
||||
public DocumentCollectionPresentationFactory(IUmbracoMapper mapper, IPublicAccessService publicAccessService)
|
||||
: this(
|
||||
mapper,
|
||||
publicAccessService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IEntityService>())
|
||||
{
|
||||
}
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public DocumentCollectionPresentationFactory(IUmbracoMapper mapper, IPublicAccessService publicAccessService, IEntityService entityService)
|
||||
: base(mapper)
|
||||
|
||||
@@ -65,22 +65,6 @@ internal sealed class DocumentPresentationFactory : IDocumentPresentationFactory
|
||||
_flagProviderCollection = flagProviderCollection;
|
||||
}
|
||||
|
||||
[Obsolete("Schedule for removal in v17")]
|
||||
public async Task<DocumentResponseModel> CreateResponseModelAsync(IContent content)
|
||||
{
|
||||
DocumentResponseModel responseModel = _umbracoMapper.Map<DocumentResponseModel>(content)!;
|
||||
|
||||
Guid? templateKey = content.TemplateId.HasValue
|
||||
? _templateService.GetAsync(content.TemplateId.Value).Result?.Key
|
||||
: null;
|
||||
|
||||
responseModel.Template = templateKey.HasValue
|
||||
? new ReferenceByIdModel { Id = templateKey.Value }
|
||||
: null;
|
||||
|
||||
return responseModel;
|
||||
}
|
||||
|
||||
public async Task<PublishedDocumentResponseModel> CreatePublishedResponseModelAsync(IContent content)
|
||||
{
|
||||
PublishedDocumentResponseModel responseModel = _umbracoMapper.Map<PublishedDocumentResponseModel>(content)!;
|
||||
@@ -181,68 +165,6 @@ internal sealed class DocumentPresentationFactory : IDocumentPresentationFactory
|
||||
public DocumentTypeReferenceResponseModel CreateDocumentTypeReferenceResponseModel(IDocumentEntitySlim entity)
|
||||
=> _umbracoMapper.Map<DocumentTypeReferenceResponseModel>(entity)!;
|
||||
|
||||
[Obsolete("Use CreateCulturePublishScheduleModels instead. Scheduled for removal in v17")]
|
||||
public Attempt<CultureAndScheduleModel, ContentPublishingOperationStatus> CreateCultureAndScheduleModel(PublishDocumentRequestModel requestModel)
|
||||
{
|
||||
var contentScheduleCollection = new ContentScheduleCollection();
|
||||
var culturesToPublishImmediately = new HashSet<string>();
|
||||
foreach (CultureAndScheduleRequestModel cultureAndScheduleRequestModel in requestModel.PublishSchedules)
|
||||
{
|
||||
if (cultureAndScheduleRequestModel.Schedule is null || (cultureAndScheduleRequestModel.Schedule.PublishTime is null && cultureAndScheduleRequestModel.Schedule.UnpublishTime is null))
|
||||
{
|
||||
culturesToPublishImmediately.Add(cultureAndScheduleRequestModel.Culture ?? Constants.System.InvariantCulture); // API have `null` for invariant, but service layer has "*".
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cultureAndScheduleRequestModel.Schedule.PublishTime is not null)
|
||||
{
|
||||
if (cultureAndScheduleRequestModel.Schedule.PublishTime <= _timeProvider.GetUtcNow())
|
||||
{
|
||||
return Attempt.FailWithStatus(ContentPublishingOperationStatus.PublishTimeNeedsToBeInFuture, new CultureAndScheduleModel()
|
||||
{
|
||||
Schedules = contentScheduleCollection,
|
||||
CulturesToPublishImmediately = culturesToPublishImmediately,
|
||||
});
|
||||
}
|
||||
|
||||
contentScheduleCollection.Add(new ContentSchedule(
|
||||
cultureAndScheduleRequestModel.Culture ?? Constants.System.InvariantCulture,
|
||||
cultureAndScheduleRequestModel.Schedule.PublishTime.Value.UtcDateTime,
|
||||
ContentScheduleAction.Release));
|
||||
}
|
||||
if (cultureAndScheduleRequestModel.Schedule.UnpublishTime is not null)
|
||||
{
|
||||
if (cultureAndScheduleRequestModel.Schedule.UnpublishTime <= cultureAndScheduleRequestModel.Schedule.PublishTime)
|
||||
{
|
||||
return Attempt.FailWithStatus(ContentPublishingOperationStatus.UnpublishTimeNeedsToBeAfterPublishTime, new CultureAndScheduleModel()
|
||||
{
|
||||
Schedules = contentScheduleCollection,
|
||||
CulturesToPublishImmediately = culturesToPublishImmediately,
|
||||
});
|
||||
}
|
||||
|
||||
if (cultureAndScheduleRequestModel.Schedule.UnpublishTime <= _timeProvider.GetUtcNow())
|
||||
{
|
||||
return Attempt.FailWithStatus(ContentPublishingOperationStatus.UpublishTimeNeedsToBeInFuture, new CultureAndScheduleModel()
|
||||
{
|
||||
Schedules = contentScheduleCollection,
|
||||
CulturesToPublishImmediately = culturesToPublishImmediately,
|
||||
});
|
||||
}
|
||||
|
||||
contentScheduleCollection.Add(new ContentSchedule(
|
||||
cultureAndScheduleRequestModel.Culture ?? Constants.System.InvariantCulture,
|
||||
cultureAndScheduleRequestModel.Schedule.UnpublishTime.Value.UtcDateTime,
|
||||
ContentScheduleAction.Expire));
|
||||
}
|
||||
}
|
||||
return Attempt.SucceedWithStatus(ContentPublishingOperationStatus.Success, new CultureAndScheduleModel()
|
||||
{
|
||||
Schedules = contentScheduleCollection,
|
||||
CulturesToPublishImmediately = culturesToPublishImmediately,
|
||||
});
|
||||
}
|
||||
|
||||
public Attempt<List<CulturePublishScheduleModel>, ContentPublishingOperationStatus> CreateCulturePublishScheduleModels(PublishDocumentRequestModel requestModel)
|
||||
{
|
||||
var model = new List<CulturePublishScheduleModel>();
|
||||
|
||||
@@ -1,24 +1,42 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public class DocumentUrlFactory : IDocumentUrlFactory
|
||||
{
|
||||
private readonly IPublishedUrlInfoProvider _publishedUrlInfoProvider;
|
||||
private readonly UrlProviderCollection _urlProviders;
|
||||
private readonly IPreviewService _previewService;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly ILogger<DocumentUrlFactory> _logger;
|
||||
|
||||
|
||||
public DocumentUrlFactory(IPublishedUrlInfoProvider publishedUrlInfoProvider)
|
||||
=> _publishedUrlInfoProvider = publishedUrlInfoProvider;
|
||||
public DocumentUrlFactory(
|
||||
IPublishedUrlInfoProvider publishedUrlInfoProvider,
|
||||
UrlProviderCollection urlProviders,
|
||||
IPreviewService previewService,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
ILogger<DocumentUrlFactory> logger)
|
||||
{
|
||||
_publishedUrlInfoProvider = publishedUrlInfoProvider;
|
||||
_urlProviders = urlProviders;
|
||||
_previewService = previewService;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<DocumentUrlInfo>> CreateUrlsAsync(IContent content)
|
||||
{
|
||||
ISet<UrlInfo> urlInfos = await _publishedUrlInfoProvider.GetAllAsync(content);
|
||||
|
||||
return urlInfos
|
||||
.Where(urlInfo => urlInfo.IsUrl)
|
||||
.Select(urlInfo => new DocumentUrlInfo { Culture = urlInfo.Culture, Url = urlInfo.Text })
|
||||
.Select(CreateDocumentUrlInfo)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
@@ -34,4 +52,52 @@ public class DocumentUrlFactory : IDocumentUrlFactory
|
||||
|
||||
return documentUrlInfoResourceSets;
|
||||
}
|
||||
|
||||
public async Task<DocumentUrlInfo?> GetPreviewUrlAsync(IContent content, string providerAlias, string? culture, string? segment)
|
||||
{
|
||||
IUrlProvider? provider = _urlProviders.FirstOrDefault(provider => provider.Alias.InvariantEquals(providerAlias));
|
||||
if (provider is null)
|
||||
{
|
||||
_logger.LogError("Could not resolve a URL provider requested for preview - it was not registered in the URL providers collection.");
|
||||
return null;
|
||||
}
|
||||
|
||||
UrlInfo? previewUrlInfo = await provider.GetPreviewUrlAsync(content, culture, segment);
|
||||
if (previewUrlInfo is null)
|
||||
{
|
||||
_logger.LogError("The URL provider could not generate a preview URL for content with key: {contentKey}", content.Key);
|
||||
return null;
|
||||
}
|
||||
|
||||
// must initiate preview state for internal preview URLs
|
||||
if (previewUrlInfo.Url is not null && previewUrlInfo.IsExternal is false)
|
||||
{
|
||||
IUser? currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;
|
||||
if (currentUser is null)
|
||||
{
|
||||
_logger.LogError("Could not access the current backoffice user while attempting to authenticate for preview.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (await _previewService.TryEnterPreviewAsync(currentUser) is false)
|
||||
{
|
||||
_logger.LogError("A server error occured, could not initiate an authenticated preview state for the current user.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return CreateDocumentUrlInfo(previewUrlInfo);
|
||||
}
|
||||
|
||||
private DocumentUrlInfo CreateDocumentUrlInfo(UrlInfo urlInfo)
|
||||
{
|
||||
var url = urlInfo.Url?.ToString();
|
||||
return new DocumentUrlInfo
|
||||
{
|
||||
Culture = urlInfo.Culture,
|
||||
Url = url,
|
||||
Message = urlInfo.Message,
|
||||
Provider = urlInfo.Provider,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,14 +41,6 @@ public class HealthCheckGroupPresentationFactory : IHealthCheckGroupPresentation
|
||||
return groups;
|
||||
}
|
||||
|
||||
[Obsolete("Use CreateHealthCheckGroupWithResultViewModelAsync instead. Will be removed in v17.")]
|
||||
public HealthCheckGroupWithResultResponseModel CreateHealthCheckGroupWithResultViewModel(IGrouping<string?, HealthCheck> healthCheckGroup)
|
||||
=> CreateHealthCheckGroupWithResultViewModelAsync(healthCheckGroup).GetAwaiter().GetResult();
|
||||
|
||||
[Obsolete("Use CreateHealthCheckGroupWithResultViewModelAsync instead. Will be removed in v17.")]
|
||||
public HealthCheckWithResultPresentationModel CreateHealthCheckWithResultViewModel(HealthCheck healthCheck) =>
|
||||
CreateHealthCheckWithResultViewModelAsync(healthCheck).GetAwaiter().GetResult();
|
||||
|
||||
public async Task<HealthCheckGroupWithResultResponseModel> CreateHealthCheckGroupWithResultViewModelAsync(IGrouping<string?, HealthCheck> healthCheckGroup)
|
||||
{
|
||||
var healthChecks = new List<HealthCheckWithResultPresentationModel>();
|
||||
|
||||
@@ -14,17 +14,9 @@ namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public interface IDocumentPresentationFactory
|
||||
{
|
||||
[Obsolete("Schedule for removal in v17")]
|
||||
Task<DocumentResponseModel> CreateResponseModelAsync(IContent content);
|
||||
|
||||
Task<PublishedDocumentResponseModel> CreatePublishedResponseModelAsync(IContent content);
|
||||
|
||||
Task<DocumentResponseModel> CreateResponseModelAsync(IContent content, ContentScheduleCollection schedule)
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
// Remove when obsolete CreateResponseModelAsync is removed
|
||||
=> CreateResponseModelAsync(content);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
Task<DocumentResponseModel> CreateResponseModelAsync(IContent content, ContentScheduleCollection schedule);
|
||||
DocumentItemResponseModel CreateItemResponseModel(IDocumentEntitySlim entity);
|
||||
|
||||
DocumentBlueprintItemResponseModel CreateBlueprintItemResponseModel(IDocumentEntitySlim entity);
|
||||
@@ -33,9 +25,6 @@ public interface IDocumentPresentationFactory
|
||||
|
||||
DocumentTypeReferenceResponseModel CreateDocumentTypeReferenceResponseModel(IDocumentEntitySlim entity);
|
||||
|
||||
[Obsolete("Use CreateCulturePublishScheduleModels instead. Scheduled for removal in v17")]
|
||||
Attempt<CultureAndScheduleModel, ContentPublishingOperationStatus> CreateCultureAndScheduleModel(PublishDocumentRequestModel requestModel);
|
||||
|
||||
Attempt<List<CulturePublishScheduleModel>, ContentPublishingOperationStatus> CreateCulturePublishScheduleModels(
|
||||
PublishDocumentRequestModel requestModel)
|
||||
{
|
||||
|
||||
@@ -8,4 +8,6 @@ public interface IDocumentUrlFactory
|
||||
Task<IEnumerable<DocumentUrlInfo>> CreateUrlsAsync(IContent content);
|
||||
|
||||
Task<IEnumerable<DocumentUrlInfoResponseModel>> CreateUrlSetsAsync(IEnumerable<IContent> contentItems);
|
||||
|
||||
Task<DocumentUrlInfo?> GetPreviewUrlAsync(IContent content, string providerAlias, string? culture, string? segment);
|
||||
}
|
||||
|
||||
@@ -7,19 +7,8 @@ public interface IHealthCheckGroupPresentationFactory
|
||||
{
|
||||
IEnumerable<IGrouping<string?, HealthCheck>> CreateGroupingFromHealthCheckCollection();
|
||||
|
||||
[Obsolete("Use CreateHealthCheckGroupWithResultViewModelAsync instead. Will be removed in v17.")]
|
||||
HealthCheckGroupWithResultResponseModel CreateHealthCheckGroupWithResultViewModel(IGrouping<string?, HealthCheck> healthCheckGroup);
|
||||
Task<HealthCheckGroupWithResultResponseModel> CreateHealthCheckGroupWithResultViewModelAsync(
|
||||
IGrouping<string?, HealthCheck> healthCheckGroup);
|
||||
|
||||
[Obsolete("Use CreateHealthCheckGroupWithResultViewModelAsync instead. Will be removed in v17.")]
|
||||
HealthCheckWithResultPresentationModel CreateHealthCheckWithResultViewModel(HealthCheck healthCheck);
|
||||
|
||||
Task<HealthCheckGroupWithResultResponseModel> CreateHealthCheckGroupWithResultViewModelAsync(IGrouping<string?, HealthCheck> healthCheckGroup)
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
=> Task.FromResult(CreateHealthCheckGroupWithResultViewModel(healthCheckGroup));
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
Task<HealthCheckWithResultPresentationModel> CreateHealthCheckWithResultViewModelAsync(HealthCheck healthCheck)
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
=> Task.FromResult(CreateHealthCheckWithResultViewModel(healthCheck));
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
Task<HealthCheckWithResultPresentationModel> CreateHealthCheckWithResultViewModelAsync(HealthCheck healthCheck);
|
||||
}
|
||||
|
||||
@@ -5,5 +5,8 @@ namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public interface IIndexPresentationFactory
|
||||
{
|
||||
[Obsolete("Use CreateAsync() instead. Scheduled for removal in v19.")]
|
||||
IndexResponseModel Create(IIndex index);
|
||||
|
||||
Task<IndexResponseModel> CreateAsync(IIndex index) => Task.FromResult(Create(index));
|
||||
}
|
||||
|
||||
@@ -28,21 +28,17 @@ public class IndexPresentationFactory : IIndexPresentationFactory
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[Obsolete("Use the non obsolete method instead. Scheduled for removal in v17")]
|
||||
public IndexPresentationFactory(IIndexDiagnosticsFactory indexDiagnosticsFactory, IIndexRebuilder indexRebuilder, IIndexingRebuilderService indexingRebuilderService)
|
||||
:this(
|
||||
indexDiagnosticsFactory,
|
||||
indexRebuilder,
|
||||
indexingRebuilderService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<ILogger<IndexPresentationFactory>>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("Use CreateAsync() instead. Scheduled for removal in v19.")]
|
||||
public IndexResponseModel Create(IIndex index)
|
||||
=> CreateAsync(index).GetAwaiter().GetResult();
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IndexResponseModel> CreateAsync(IIndex index)
|
||||
{
|
||||
var isCorrupt = !TryGetSearcherName(index, out var searcherName);
|
||||
|
||||
if (_indexingRebuilderService.IsRebuilding(index.Name))
|
||||
if (await _indexingRebuilderService.IsRebuildingAsync(index.Name))
|
||||
{
|
||||
return new IndexResponseModel
|
||||
{
|
||||
@@ -63,7 +59,7 @@ public class IndexPresentationFactory : IIndexPresentationFactory
|
||||
|
||||
var properties = new Dictionary<string, object?>();
|
||||
|
||||
foreach (var property in indexDiag.Metadata)
|
||||
foreach (KeyValuePair<string, object?> property in indexDiag.Metadata)
|
||||
{
|
||||
if (property.Value is null)
|
||||
{
|
||||
@@ -71,7 +67,7 @@ public class IndexPresentationFactory : IIndexPresentationFactory
|
||||
}
|
||||
else
|
||||
{
|
||||
var propertyType = property.Value.GetType();
|
||||
Type propertyType = property.Value.GetType();
|
||||
properties[property.Key] = propertyType.IsClass && !propertyType.IsArray ? property.Value?.ToString() : property.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class ModelsBuilderPresentationFactory : IModelsBuilderPresentationFactor
|
||||
new()
|
||||
{
|
||||
Mode = _modelsBuilderSettings.ModelsMode,
|
||||
CanGenerate = _modelsBuilderSettings.ModelsMode.SupportsExplicitGeneration(),
|
||||
CanGenerate = _modelsBuilderSettings.ModelsMode is Constants.ModelsBuilder.ModelsModes.SourceCodeManual or Constants.ModelsBuilder.ModelsModes.SourceCodeAuto,
|
||||
OutOfDateModels = _outOfDateModels.IsOutOfDate,
|
||||
LastError = _mbErrors.GetLastError(),
|
||||
Version = ApiVersion.Current.Version.ToString(),
|
||||
|
||||
@@ -18,16 +18,6 @@ public class RelationTypePresentationFactory : IRelationTypePresentationFactory
|
||||
private readonly IDocumentPresentationFactory _documentPresentationFactory;
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
|
||||
[Obsolete("Please use the non obsoleted constructor. Scheduled for removal in v17")]
|
||||
public RelationTypePresentationFactory(IUmbracoMapper umbracoMapper)
|
||||
: this(
|
||||
umbracoMapper,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IEntityRepository>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentPresentationFactory>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IScopeProvider>())
|
||||
{
|
||||
}
|
||||
|
||||
public RelationTypePresentationFactory(
|
||||
IUmbracoMapper umbracoMapper,
|
||||
IEntityRepository entityRepository,
|
||||
|
||||
@@ -43,39 +43,6 @@ public class UserPresentationFactory : IUserPresentationFactory
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UserPresentationFactory"/> class.
|
||||
/// </summary>
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")]
|
||||
public UserPresentationFactory(
|
||||
IEntityService entityService,
|
||||
AppCaches appCaches,
|
||||
MediaFileManager mediaFileManager,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IUserGroupPresentationFactory userGroupPresentationFactory,
|
||||
IAbsoluteUrlBuilder absoluteUrlBuilder,
|
||||
IEmailSender emailSender,
|
||||
IPasswordConfigurationPresentationFactory passwordConfigurationPresentationFactory,
|
||||
IOptionsSnapshot<SecuritySettings> securitySettings,
|
||||
IBackOfficeExternalLoginProviders externalLoginProviders)
|
||||
: this(
|
||||
entityService,
|
||||
appCaches,
|
||||
mediaFileManager,
|
||||
imageUrlGenerator,
|
||||
userGroupPresentationFactory,
|
||||
absoluteUrlBuilder,
|
||||
emailSender,
|
||||
passwordConfigurationPresentationFactory,
|
||||
securitySettings,
|
||||
externalLoginProviders,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IUserService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IContentService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IEnumerable<IPermissionPresentationMapper>>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UserPresentationFactory"/> class.
|
||||
/// </summary>
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")]
|
||||
public UserPresentationFactory(
|
||||
IEntityService entityService,
|
||||
AppCaches appCaches,
|
||||
@@ -87,45 +54,6 @@ public class UserPresentationFactory : IUserPresentationFactory
|
||||
IPasswordConfigurationPresentationFactory passwordConfigurationPresentationFactory,
|
||||
IOptionsSnapshot<SecuritySettings> securitySettings,
|
||||
IBackOfficeExternalLoginProviders externalLoginProviders,
|
||||
IUserService userService,
|
||||
IContentService contentService)
|
||||
: this(
|
||||
entityService,
|
||||
appCaches,
|
||||
mediaFileManager,
|
||||
imageUrlGenerator,
|
||||
userGroupPresentationFactory,
|
||||
absoluteUrlBuilder,
|
||||
emailSender,
|
||||
passwordConfigurationPresentationFactory,
|
||||
securitySettings,
|
||||
externalLoginProviders,
|
||||
userService,
|
||||
contentService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IEnumerable<IPermissionPresentationMapper>>())
|
||||
{
|
||||
}
|
||||
|
||||
// TODO (V17): Remove the unused userService and contentService parameters from this constructor.
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UserPresentationFactory"/> class.
|
||||
/// </summary>
|
||||
public UserPresentationFactory(
|
||||
IEntityService entityService,
|
||||
AppCaches appCaches,
|
||||
MediaFileManager mediaFileManager,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IUserGroupPresentationFactory userGroupPresentationFactory,
|
||||
IAbsoluteUrlBuilder absoluteUrlBuilder,
|
||||
IEmailSender emailSender,
|
||||
IPasswordConfigurationPresentationFactory passwordConfigurationPresentationFactory,
|
||||
IOptionsSnapshot<SecuritySettings> securitySettings,
|
||||
IBackOfficeExternalLoginProviders externalLoginProviders,
|
||||
#pragma warning disable IDE0060 // Remove unused parameter - need to keep these until the next major to avoid breaking changes and/or ambiguous constructor errors
|
||||
IUserService userService,
|
||||
IContentService contentService,
|
||||
#pragma warning restore IDE0060 // Remove unused parameter
|
||||
IEnumerable<IPermissionPresentationMapper> permissionPresentationMappers)
|
||||
{
|
||||
_entityService = entityService;
|
||||
|
||||
+10
-4
@@ -12,27 +12,31 @@ using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Install;
|
||||
|
||||
public class CreateUnattendedUserNotificationHandler : INotificationAsyncHandler<UnattendedInstallNotification>
|
||||
public class PostUnattendedInstallNotificationHandler : INotificationAsyncHandler<UnattendedInstallNotification>
|
||||
{
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly IOptions<UnattendedSettings> _unattendedSettings;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IMetricsConsentService _metricsConsentService;
|
||||
|
||||
public CreateUnattendedUserNotificationHandler(IOptions<UnattendedSettings> unattendedSettings, IUserService userService, IServiceScopeFactory serviceScopeFactory)
|
||||
public PostUnattendedInstallNotificationHandler(IOptions<UnattendedSettings> unattendedSettings, IUserService userService, IServiceScopeFactory serviceScopeFactory, IMetricsConsentService metricsConsentService)
|
||||
{
|
||||
_unattendedSettings = unattendedSettings;
|
||||
_userService = userService;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_metricsConsentService = metricsConsentService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Listening for when the UnattendedInstallNotification fired after a sucessfulk
|
||||
/// Listening for when the UnattendedInstallNotification fired after a successful unattended install
|
||||
/// This creates the user and sets the telemetry level based on the 'Unattended' settings.
|
||||
/// </summary>
|
||||
/// <param name="notification"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
public async Task HandleAsync(UnattendedInstallNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
UnattendedSettings? unattendedSettings = _unattendedSettings.Value;
|
||||
|
||||
// Ensure we have the setting enabled (Sanity check)
|
||||
// In theory this should always be true as the event only fired when a sucessfull
|
||||
if (_unattendedSettings.Value.InstallUnattended == false)
|
||||
@@ -83,7 +87,7 @@ public class CreateUnattendedUserNotificationHandler : INotificationAsyncHandler
|
||||
$"No user found in membership provider with id of {Constants.Security.SuperUserIdAsString}.");
|
||||
}
|
||||
|
||||
//To change the password here we actually need to reset it since we don't have an old one to use to change
|
||||
// To change the password here we actually need to reset it since we don't have an old one to use to change
|
||||
var resetToken = await backOfficeUserManager.GeneratePasswordResetTokenAsync(membershipUser);
|
||||
if (string.IsNullOrWhiteSpace(resetToken))
|
||||
{
|
||||
@@ -98,5 +102,7 @@ public class CreateUnattendedUserNotificationHandler : INotificationAsyncHandler
|
||||
throw new InvalidOperationException("Could not reset password: " +
|
||||
string.Join(", ", resetResult.Errors.ToErrorMessage()));
|
||||
}
|
||||
|
||||
await _metricsConsentService.SetConsentLevelAsync(unattendedSettings.UnattendedTelemetryLevel);
|
||||
}
|
||||
}
|
||||
@@ -22,17 +22,6 @@ public class DocumentPermissionMapper : IPermissionPresentationMapper, IPermissi
|
||||
private readonly Lazy<IEntityService> _entityService;
|
||||
private readonly Lazy<IUserService> _userService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DocumentPermissionMapper"/> class.
|
||||
/// </summary>
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")]
|
||||
public DocumentPermissionMapper()
|
||||
: this(
|
||||
StaticServiceProvider.Instance.GetRequiredService<Lazy<IEntityService>>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<Lazy<IUserService>>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DocumentPermissionMapper"/> class.
|
||||
/// </summary>
|
||||
|
||||
-12
@@ -21,18 +21,6 @@ public class BackOfficeAuthorizationInitializationMiddleware : IMiddleware
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly WebRoutingSettings _webRoutingSettings;
|
||||
|
||||
[Obsolete("Use the non-obsolete constructor. This will be removed in Umbraco 17.")]
|
||||
public BackOfficeAuthorizationInitializationMiddleware(
|
||||
UmbracoRequestPaths umbracoRequestPaths,
|
||||
IServiceProvider serviceProvider,
|
||||
IRuntimeState runtimeState,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IOptions<WebRoutingSettings> webRoutingSettings,
|
||||
IHostingEnvironment hostingEnvironment)
|
||||
: this(umbracoRequestPaths, serviceProvider, runtimeState, webRoutingSettings)
|
||||
{
|
||||
}
|
||||
|
||||
public BackOfficeAuthorizationInitializationMiddleware(
|
||||
UmbracoRequestPaths umbracoRequestPaths,
|
||||
IServiceProvider serviceProvider,
|
||||
|
||||
@@ -880,70 +880,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/data-type/{id}/references": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Data Type"
|
||||
],
|
||||
"operationId": "GetDataTypeByIdReferences",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DataTypeReferenceResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource"
|
||||
}
|
||||
},
|
||||
"deprecated": true,
|
||||
"security": [
|
||||
{
|
||||
"Backoffice-User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/data-type/configuration": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -8723,6 +8659,101 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/document/{id}/preview-url": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Document"
|
||||
],
|
||||
"operationId": "GetDocumentByIdPreviewUrl",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "providerAlias",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "culture",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "segment",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentUrlInfoModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice-User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/document/{id}/public-access": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -20290,6 +20321,461 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/member-type/folder": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Member Type"
|
||||
],
|
||||
"operationId": "PostMemberTypeFolder",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/CreateFolderRequestModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/CreateFolderRequestModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"application/*+json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/CreateFolderRequestModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"headers": {
|
||||
"Umb-Generated-Resource": {
|
||||
"description": "Identifier of the newly created resource",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"description": "Identifier of the newly created resource"
|
||||
}
|
||||
},
|
||||
"Location": {
|
||||
"description": "Location of the newly created resource",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"description": "Location of the newly created resource",
|
||||
"format": "uri"
|
||||
}
|
||||
},
|
||||
"Umb-Notifications": {
|
||||
"description": "The list of notifications produced during the request.",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NotificationHeaderModel"
|
||||
},
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"headers": {
|
||||
"Umb-Notifications": {
|
||||
"description": "The list of notifications produced during the request.",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NotificationHeaderModel"
|
||||
},
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"headers": {
|
||||
"Umb-Notifications": {
|
||||
"description": "The list of notifications produced during the request.",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NotificationHeaderModel"
|
||||
},
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource",
|
||||
"headers": {
|
||||
"Umb-Notifications": {
|
||||
"description": "The list of notifications produced during the request.",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NotificationHeaderModel"
|
||||
},
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice-User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/member-type/folder/{id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Member Type"
|
||||
],
|
||||
"operationId": "GetMemberTypeFolderById",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/FolderResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice-User": [ ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"Member Type"
|
||||
],
|
||||
"operationId": "DeleteMemberTypeFolderById",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"headers": {
|
||||
"Umb-Notifications": {
|
||||
"description": "The list of notifications produced during the request.",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NotificationHeaderModel"
|
||||
},
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"headers": {
|
||||
"Umb-Notifications": {
|
||||
"description": "The list of notifications produced during the request.",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NotificationHeaderModel"
|
||||
},
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"headers": {
|
||||
"Umb-Notifications": {
|
||||
"description": "The list of notifications produced during the request.",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NotificationHeaderModel"
|
||||
},
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource",
|
||||
"headers": {
|
||||
"Umb-Notifications": {
|
||||
"description": "The list of notifications produced during the request.",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NotificationHeaderModel"
|
||||
},
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice-User": [ ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"tags": [
|
||||
"Member Type"
|
||||
],
|
||||
"operationId": "PutMemberTypeFolderById",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/UpdateFolderResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/UpdateFolderResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"application/*+json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/UpdateFolderResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"headers": {
|
||||
"Umb-Notifications": {
|
||||
"description": "The list of notifications produced during the request.",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NotificationHeaderModel"
|
||||
},
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"headers": {
|
||||
"Umb-Notifications": {
|
||||
"description": "The list of notifications produced during the request.",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NotificationHeaderModel"
|
||||
},
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"headers": {
|
||||
"Umb-Notifications": {
|
||||
"description": "The list of notifications produced during the request.",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NotificationHeaderModel"
|
||||
},
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
},
|
||||
"403": {
|
||||
"description": "The authenticated user does not have access to this resource",
|
||||
"headers": {
|
||||
"Umb-Notifications": {
|
||||
"description": "The list of notifications produced during the request.",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NotificationHeaderModel"
|
||||
},
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice-User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/tree/member-type/root": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -21747,6 +22233,38 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/news-dashboard": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"News Dashboard"
|
||||
],
|
||||
"operationId": "GetNewsDashboard",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NewsDashboardResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice-User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/object-types": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -23933,6 +24451,7 @@
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
}
|
||||
},
|
||||
"deprecated": true,
|
||||
"security": [
|
||||
{
|
||||
"Backoffice-User": [ ]
|
||||
@@ -37641,34 +38160,6 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"DataTypeContentTypeReferenceModel": {
|
||||
"required": [
|
||||
"icon",
|
||||
"id",
|
||||
"name",
|
||||
"type"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DataTypeItemResponseModel": {
|
||||
"required": [
|
||||
"editorAlias",
|
||||
@@ -37724,49 +38215,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DataTypePropertyReferenceModel": {
|
||||
"required": [
|
||||
"alias",
|
||||
"name"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"alias": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DataTypeReferenceResponseModel": {
|
||||
"required": [
|
||||
"contentType",
|
||||
"properties"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"contentType": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DataTypeContentTypeReferenceModel"
|
||||
}
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DataTypePropertyReferenceModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DataTypeResponseModel": {
|
||||
"required": [
|
||||
"canIgnoreStartNodes",
|
||||
@@ -38720,7 +39168,6 @@
|
||||
"flags",
|
||||
"id",
|
||||
"isTrashed",
|
||||
"urls",
|
||||
"values",
|
||||
"variants"
|
||||
],
|
||||
@@ -38767,17 +39214,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"urls": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentUrlInfoModel"
|
||||
}
|
||||
]
|
||||
},
|
||||
"deprecated": true
|
||||
},
|
||||
"template": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -39499,6 +39935,8 @@
|
||||
"DocumentUrlInfoModel": {
|
||||
"required": [
|
||||
"culture",
|
||||
"message",
|
||||
"provider",
|
||||
"url"
|
||||
],
|
||||
"type": "object",
|
||||
@@ -39508,6 +39946,14 @@
|
||||
"nullable": true
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"provider": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
@@ -41074,7 +41520,6 @@
|
||||
"id",
|
||||
"isTrashed",
|
||||
"mediaType",
|
||||
"urls",
|
||||
"values",
|
||||
"variants"
|
||||
],
|
||||
@@ -41114,17 +41559,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"urls": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/MediaUrlInfoModel"
|
||||
}
|
||||
]
|
||||
},
|
||||
"deprecated": true
|
||||
},
|
||||
"isTrashed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -41716,7 +42150,8 @@
|
||||
"nullable": true
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -42658,7 +43093,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mode": {
|
||||
"$ref": "#/components/schemas/ModelsModeModel"
|
||||
"type": "string"
|
||||
},
|
||||
"canGenerate": {
|
||||
"type": "boolean"
|
||||
@@ -42684,15 +43119,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ModelsModeModel": {
|
||||
"enum": [
|
||||
"Nothing",
|
||||
"InMemoryAuto",
|
||||
"SourceCodeManual",
|
||||
"SourceCodeAuto"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"MoveDataTypeRequestModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -42831,6 +43257,61 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"NewsDashboardItemResponseModel": {
|
||||
"required": [
|
||||
"header",
|
||||
"priority"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"priority": {
|
||||
"type": "string"
|
||||
},
|
||||
"header": {
|
||||
"type": "string"
|
||||
},
|
||||
"body": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"buttonText": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"imageUrl": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"imageAltText": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"NewsDashboardResponseModel": {
|
||||
"required": [
|
||||
"items"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NewsDashboardItemResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"NoopSetupTwoFactorModel": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
@@ -44889,7 +45370,6 @@
|
||||
"flags",
|
||||
"id",
|
||||
"isTrashed",
|
||||
"urls",
|
||||
"values",
|
||||
"variants"
|
||||
],
|
||||
@@ -44936,17 +45416,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"urls": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentUrlInfoModel"
|
||||
}
|
||||
]
|
||||
},
|
||||
"deprecated": true
|
||||
},
|
||||
"template": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
||||
@@ -32,10 +32,6 @@ public class BackOfficeApplicationManager : OpenIdDictApplicationManagerBase, IB
|
||||
_authorizeCallbackLogoutPathName = securitySettings.Value.AuthorizeCallbackLogoutPathName;
|
||||
}
|
||||
|
||||
[Obsolete("Please use the overload that allows for multiple back-office hosts. Will be removed in V17.")]
|
||||
public async Task EnsureBackOfficeApplicationAsync(Uri backOfficeUrl, CancellationToken cancellationToken = default)
|
||||
=> await EnsureBackOfficeApplicationAsync([backOfficeUrl], cancellationToken);
|
||||
|
||||
public async Task EnsureBackOfficeApplicationAsync(IEnumerable<Uri> backOfficeHosts, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Install is okay without this, because we do not need a token to install,
|
||||
@@ -100,8 +96,7 @@ public class BackOfficeApplicationManager : OpenIdDictApplicationManagerBase, IB
|
||||
public async Task DeleteBackOfficeClientCredentialsApplicationAsync(string clientId, CancellationToken cancellationToken = default)
|
||||
=> await Delete(clientId, cancellationToken);
|
||||
|
||||
[Obsolete("Do not use - for internal usage only. Will be made internal in V17.")]
|
||||
public OpenIddictApplicationDescriptor BackofficeOpenIddictApplicationDescriptor(Uri backOfficeUrl)
|
||||
internal OpenIddictApplicationDescriptor BackofficeOpenIddictApplicationDescriptor(Uri backOfficeUrl)
|
||||
=> BackofficeOpenIddictApplicationDescriptor([backOfficeUrl]);
|
||||
|
||||
internal OpenIddictApplicationDescriptor BackofficeOpenIddictApplicationDescriptor(Uri[] backOfficeHosts)
|
||||
|
||||
@@ -18,19 +18,6 @@ public class BackOfficeExternalLoginProviders : IBackOfficeExternalLoginProvider
|
||||
|
||||
private const string ExternalLoginProvidersKey = "Umbraco.Cms.Web.BackOffice.Security.BackOfficeExternalLoginProviders";
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")]
|
||||
public BackOfficeExternalLoginProviders(
|
||||
IEnumerable<BackOfficeExternalLoginProvider> externalLogins,
|
||||
IAuthenticationSchemeProvider authenticationSchemeProvider)
|
||||
: this(
|
||||
externalLogins,
|
||||
authenticationSchemeProvider,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IKeyValueService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IExternalLoginWithKeyService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<ILogger<BackOfficeExternalLoginProviders>>())
|
||||
{
|
||||
}
|
||||
|
||||
public BackOfficeExternalLoginProviders(
|
||||
IEnumerable<BackOfficeExternalLoginProvider> externalLogins,
|
||||
IAuthenticationSchemeProvider authenticationSchemeProvider,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Globalization;
|
||||
using System.Globalization;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
@@ -12,24 +12,32 @@ namespace Umbraco.Cms.Api.Management.Security;
|
||||
/// Binds to notifications to write audit logs for the <see cref="BackOfficeUserManager" />
|
||||
/// </summary>
|
||||
internal sealed class BackOfficeUserManagerAuditer :
|
||||
INotificationHandler<UserLoginSuccessNotification>,
|
||||
INotificationHandler<UserLogoutSuccessNotification>,
|
||||
INotificationHandler<UserLoginFailedNotification>,
|
||||
INotificationHandler<UserForgotPasswordRequestedNotification>,
|
||||
INotificationHandler<UserForgotPasswordChangedNotification>,
|
||||
INotificationHandler<UserPasswordChangedNotification>,
|
||||
INotificationHandler<UserPasswordResetNotification>
|
||||
INotificationAsyncHandler<UserLoginSuccessNotification>,
|
||||
INotificationAsyncHandler<UserLogoutSuccessNotification>,
|
||||
INotificationAsyncHandler<UserLoginFailedNotification>,
|
||||
INotificationAsyncHandler<UserForgotPasswordRequestedNotification>,
|
||||
INotificationAsyncHandler<UserForgotPasswordChangedNotification>,
|
||||
INotificationAsyncHandler<UserPasswordChangedNotification>,
|
||||
INotificationAsyncHandler<UserPasswordResetNotification>
|
||||
{
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IAuditEntryService _auditEntryService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public BackOfficeUserManagerAuditer(IAuditService auditService, IUserService userService)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BackOfficeUserManagerAuditer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="auditEntryService">The audit entry service.</param>
|
||||
/// <param name="userService">The user service.</param>
|
||||
public BackOfficeUserManagerAuditer(
|
||||
IAuditEntryService auditEntryService,
|
||||
IUserService userService)
|
||||
{
|
||||
_auditService = auditService;
|
||||
_auditEntryService = auditEntryService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public void Handle(UserForgotPasswordChangedNotification notification) =>
|
||||
/// <inheritdoc />
|
||||
public Task HandleAsync(UserForgotPasswordChangedNotification notification, CancellationToken cancellationToken) =>
|
||||
WriteAudit(
|
||||
notification.PerformingUserId,
|
||||
notification.AffectedUserId,
|
||||
@@ -37,7 +45,8 @@ internal sealed class BackOfficeUserManagerAuditer :
|
||||
"umbraco/user/password/forgot/change",
|
||||
"password forgot/change");
|
||||
|
||||
public void Handle(UserForgotPasswordRequestedNotification notification) =>
|
||||
/// <inheritdoc />
|
||||
public Task HandleAsync(UserForgotPasswordRequestedNotification notification, CancellationToken cancellationToken) =>
|
||||
WriteAudit(
|
||||
notification.PerformingUserId,
|
||||
notification.AffectedUserId,
|
||||
@@ -45,7 +54,8 @@ internal sealed class BackOfficeUserManagerAuditer :
|
||||
"umbraco/user/password/forgot/request",
|
||||
"password forgot/request");
|
||||
|
||||
public void Handle(UserLoginFailedNotification notification) =>
|
||||
/// <inheritdoc />
|
||||
public Task HandleAsync(UserLoginFailedNotification notification, CancellationToken cancellationToken) =>
|
||||
WriteAudit(
|
||||
notification.PerformingUserId,
|
||||
null,
|
||||
@@ -53,7 +63,8 @@ internal sealed class BackOfficeUserManagerAuditer :
|
||||
"umbraco/user/sign-in/failed",
|
||||
"login failed");
|
||||
|
||||
public void Handle(UserLoginSuccessNotification notification)
|
||||
/// <inheritdoc />
|
||||
public Task HandleAsync(UserLoginSuccessNotification notification, CancellationToken cancellationToken)
|
||||
=> WriteAudit(
|
||||
notification.PerformingUserId,
|
||||
notification.AffectedUserId,
|
||||
@@ -61,7 +72,8 @@ internal sealed class BackOfficeUserManagerAuditer :
|
||||
"umbraco/user/sign-in/login",
|
||||
"login success");
|
||||
|
||||
public void Handle(UserLogoutSuccessNotification notification)
|
||||
/// <inheritdoc />
|
||||
public Task HandleAsync(UserLogoutSuccessNotification notification, CancellationToken cancellationToken)
|
||||
=> WriteAudit(
|
||||
notification.PerformingUserId,
|
||||
notification.AffectedUserId,
|
||||
@@ -69,7 +81,8 @@ internal sealed class BackOfficeUserManagerAuditer :
|
||||
"umbraco/user/sign-in/logout",
|
||||
"logout success");
|
||||
|
||||
public void Handle(UserPasswordChangedNotification notification) =>
|
||||
/// <inheritdoc />
|
||||
public Task HandleAsync(UserPasswordChangedNotification notification, CancellationToken cancellationToken) =>
|
||||
WriteAudit(
|
||||
notification.PerformingUserId,
|
||||
notification.AffectedUserId,
|
||||
@@ -77,7 +90,8 @@ internal sealed class BackOfficeUserManagerAuditer :
|
||||
"umbraco/user/password/change",
|
||||
"password change");
|
||||
|
||||
public void Handle(UserPasswordResetNotification notification) =>
|
||||
/// <inheritdoc />
|
||||
public Task HandleAsync(UserPasswordResetNotification notification, CancellationToken cancellationToken) =>
|
||||
WriteAudit(
|
||||
notification.PerformingUserId,
|
||||
notification.AffectedUserId,
|
||||
@@ -85,58 +99,52 @@ internal sealed class BackOfficeUserManagerAuditer :
|
||||
"umbraco/user/password/reset",
|
||||
"password reset");
|
||||
|
||||
private static string FormatEmail(IMembershipUser? user) =>
|
||||
user is null ? string.Empty : user.Email.IsNullOrWhiteSpace() ? string.Empty : $"<{user.Email}>";
|
||||
|
||||
private void WriteAudit(
|
||||
private async Task WriteAudit(
|
||||
string performingId,
|
||||
string? affectedId,
|
||||
string ipAddress,
|
||||
string eventType,
|
||||
string eventDetails)
|
||||
{
|
||||
int? performingIdAsInt = ParseUserId(performingId);
|
||||
int? affectedIdAsInt = ParseUserId(affectedId);
|
||||
var performingIdAsInt = ParseUserId(performingId);
|
||||
var affectedIdAsInt = ParseUserId(affectedId);
|
||||
|
||||
WriteAudit(performingIdAsInt, affectedIdAsInt, ipAddress, eventType, eventDetails);
|
||||
await WriteAudit(performingIdAsInt, affectedIdAsInt, ipAddress, eventType, eventDetails);
|
||||
}
|
||||
|
||||
private static int? ParseUserId(string? id)
|
||||
=> int.TryParse(id, NumberStyles.Integer, CultureInfo.InvariantCulture, out var isAsInt) ? isAsInt : null;
|
||||
|
||||
private void WriteAudit(
|
||||
private async Task WriteAudit(
|
||||
int? performingId,
|
||||
int? affectedId,
|
||||
string ipAddress,
|
||||
string eventType,
|
||||
string eventDetails)
|
||||
{
|
||||
var performingDetails = "User UNKNOWN:0";
|
||||
if (performingId.HasValue)
|
||||
{
|
||||
IUser? performingUser = _userService.GetUserById(performingId.Value);
|
||||
performingDetails = performingUser is null
|
||||
? $"User UNKNOWN:{performingId.Value}"
|
||||
: $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}";
|
||||
}
|
||||
IUser? performingUser = performingId is not null ? _userService.GetUserById(performingId.Value) : null;
|
||||
IUser? affectedUser = affectedId is not null ? _userService.GetUserById(affectedId.Value) : null;
|
||||
|
||||
var affectedDetails = "User UNKNOWN:0";
|
||||
if (affectedId.HasValue)
|
||||
{
|
||||
IUser? affectedUser = _userService.GetUserById(affectedId.Value);
|
||||
affectedDetails = affectedUser is null
|
||||
? $"User UNKNOWN:{affectedId.Value}"
|
||||
: $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}";
|
||||
}
|
||||
|
||||
_auditService.Write(
|
||||
performingId ?? 0,
|
||||
performingDetails,
|
||||
await _auditEntryService.WriteAsync(
|
||||
performingUser?.Key,
|
||||
FormatDetails(performingId, performingUser),
|
||||
ipAddress,
|
||||
DateTime.UtcNow,
|
||||
affectedId ?? 0,
|
||||
affectedDetails,
|
||||
affectedUser?.Key,
|
||||
FormatDetails(affectedId, affectedUser),
|
||||
eventType,
|
||||
eventDetails);
|
||||
}
|
||||
|
||||
private static string FormatDetails(int? id, IUser? user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
return $"User UNKNOWN:{id ?? 0}";
|
||||
}
|
||||
|
||||
return user.Email.IsNullOrWhiteSpace()
|
||||
? $"User \"{user.Name}\""
|
||||
: $"User \"{user.Name}\" <{user.Email}>";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,6 @@ public class UserStartNodeEntitiesService : IUserStartNodeEntitiesService
|
||||
private readonly ICoreScopeProvider _scopeProvider;
|
||||
private readonly IIdKeyMap _idKeyMap;
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Scheduled for removal in V17.")]
|
||||
public UserStartNodeEntitiesService(IEntityService entityService)
|
||||
: this(
|
||||
entityService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<ICoreScopeProvider>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IIdKeyMap>())
|
||||
{
|
||||
}
|
||||
|
||||
public UserStartNodeEntitiesService(IEntityService entityService, ICoreScopeProvider scopeProvider, IIdKeyMap idKeyMap)
|
||||
{
|
||||
_entityService = entityService;
|
||||
@@ -96,10 +87,12 @@ public class UserStartNodeEntitiesService : IUserStartNodeEntitiesService
|
||||
return ChildUserAccessEntities(children, userStartNodePaths);
|
||||
}
|
||||
|
||||
int[] allowedChildIds = GetAllowedIds(userStartNodePaths, parentId);
|
||||
// Need to use a List here because the expression tree cannot convert an array when used in Contains.
|
||||
// See ExpressionTests.Sql_In().
|
||||
List<int> allowedChildIds = GetAllowedIds(userStartNodePaths, parentId);
|
||||
|
||||
totalItems = allowedChildIds.Length;
|
||||
if (allowedChildIds.Length == 0)
|
||||
totalItems = allowedChildIds.Count;
|
||||
if (allowedChildIds.Count == 0)
|
||||
{
|
||||
// The requested parent is outside the scope of any user start nodes.
|
||||
return [];
|
||||
@@ -111,7 +104,7 @@ public class UserStartNodeEntitiesService : IUserStartNodeEntitiesService
|
||||
return ChildUserAccessEntities(children, userStartNodePaths);
|
||||
}
|
||||
|
||||
private static int[] GetAllowedIds(string[] userStartNodePaths, int parentId)
|
||||
private static List<int> GetAllowedIds(string[] userStartNodePaths, int parentId)
|
||||
{
|
||||
// If one or more of the user start nodes are descendants of the requested parent, find the "next child IDs" in those user start node paths
|
||||
// that are the final entries in the path.
|
||||
@@ -121,7 +114,7 @@ public class UserStartNodeEntitiesService : IUserStartNodeEntitiesService
|
||||
.Where(ids => ids.Contains(parentId))
|
||||
.Select(ids => ids[ids.IndexOf(parentId) + 1]) // Given the previous checks, the parent ID can never be the last in the user start node path, so this is safe
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -194,9 +187,9 @@ public class UserStartNodeEntitiesService : IUserStartNodeEntitiesService
|
||||
return ChildUserAccessEntities(siblings, userStartNodePaths);
|
||||
}
|
||||
|
||||
int[] allowedSiblingIds = GetAllowedIds(userStartNodePaths, targetParent.Id);
|
||||
List<int> allowedSiblingIds = GetAllowedIds(userStartNodePaths, targetParent.Id);
|
||||
|
||||
if (allowedSiblingIds.Length == 0)
|
||||
if (allowedSiblingIds.Count == 0)
|
||||
{
|
||||
// The requested target is outside the scope of any user start nodes.
|
||||
totalBefore = 0;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.NewsDashboard;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Services.NewsDashboard;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a service for retrieving news dashboard items.
|
||||
/// </summary>
|
||||
public interface INewsDashboardService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the collection of news dashboard items.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation.
|
||||
/// The task result contains a <see cref="NewsDashboardResponseModel"/> with the retrieved news dashboard items.
|
||||
/// </returns>
|
||||
Task<NewsDashboardResponseModel> GetItemsAsync();
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.NewsDashboard;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Telemetry;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Services.NewsDashboard;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class NewsDashboardService : INewsDashboardService
|
||||
{
|
||||
private readonly AppCaches _appCaches;
|
||||
private readonly IUmbracoVersion _umbracoVersion;
|
||||
private readonly ISiteIdentifierService _siteIdentifierService;
|
||||
private readonly ILogger<NewsDashboardService> _logger;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
private static readonly HttpClient _httpClient = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NewsDashboardService"/> class.
|
||||
/// </summary>
|
||||
public NewsDashboardService(
|
||||
AppCaches appCaches,
|
||||
IUmbracoVersion umbracoVersion,
|
||||
ISiteIdentifierService siteIdentifierService,
|
||||
ILogger<NewsDashboardService> logger,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
IOptions<GlobalSettings> globalSettings)
|
||||
{
|
||||
_appCaches = appCaches;
|
||||
_umbracoVersion = umbracoVersion;
|
||||
_siteIdentifierService = siteIdentifierService;
|
||||
_logger = logger;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
_globalSettings = globalSettings.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<NewsDashboardResponseModel> GetItemsAsync()
|
||||
{
|
||||
const string BaseUrl = "https://umbraco-dashboard-news.euwest01.umbraco.io";
|
||||
const string Path = "/api/News";
|
||||
|
||||
var version = _umbracoVersion.SemanticVersion.ToSemanticStringWithoutBuild();
|
||||
_siteIdentifierService.TryGetOrCreateSiteIdentifier(out Guid siteIdentifier);
|
||||
|
||||
var language = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Language ?? _globalSettings.DefaultUILanguage;
|
||||
|
||||
var url = $"{BaseUrl}/{Path}?version={version}&siteId={siteIdentifier}&language={language}";
|
||||
|
||||
const string CacheKey = "umbraco-dashboard-news";
|
||||
NewsDashboardResponseModel? content = _appCaches.RuntimeCache.GetCacheItem<NewsDashboardResponseModel>(CacheKey);
|
||||
if (content is not null)
|
||||
{
|
||||
return content;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = await _httpClient.GetStringAsync(url);
|
||||
|
||||
if (TryMapModel(json, out NewsDashboardResponseModel? model))
|
||||
{
|
||||
_appCaches.RuntimeCache.InsertCacheItem(CacheKey, () => model, new TimeSpan(0, 30, 0));
|
||||
content = model;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.InnerException ?? ex, "Error getting dashboard content from {Url}", url);
|
||||
}
|
||||
|
||||
return content ?? new NewsDashboardResponseModel { Items = [] };
|
||||
}
|
||||
|
||||
private bool TryMapModel(string json, [MaybeNullWhen(false)] out NewsDashboardResponseModel newsDashboardResponseModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, };
|
||||
|
||||
List<NewsDashboardItemResponseModel>? items = JsonSerializer.Deserialize<List<NewsDashboardItemResponseModel>>(json, options);
|
||||
newsDashboardResponseModel = new NewsDashboardResponseModel { Items = items ?? [] };
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
_logger.LogError(ex.InnerException ?? ex, "Error deserializing dashboard news items");
|
||||
newsDashboardResponseModel = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,12 @@
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Linq.Async">
|
||||
<ExcludeAssets>compile</ExcludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Umbraco.PublishedCache.HybridCache\Umbraco.PublishedCache.HybridCache.csproj" />
|
||||
<ProjectReference Include="..\Umbraco.Cms.Api.Common\Umbraco.Cms.Api.Common.csproj" />
|
||||
|
||||
@@ -4,5 +4,5 @@ public abstract class ContentUrlInfoBase
|
||||
{
|
||||
public required string? Culture { get; init; }
|
||||
|
||||
public required string Url { get; init; }
|
||||
public required string? Url { get; init; }
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@ namespace Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
|
||||
public class DocumentResponseModel : DocumentResponseModelBase<DocumentValueResponseModel, DocumentVariantResponseModel>
|
||||
{
|
||||
[Obsolete("This property is no longer populated. Please use /document/{id}/urls instead to retrieve the URLs for a document. Scheduled for removal in Umbraco 17.")]
|
||||
public IEnumerable<DocumentUrlInfo> Urls { get; set; } = Enumerable.Empty<DocumentUrlInfo>();
|
||||
|
||||
public ReferenceByIdModel? Template { get; set; }
|
||||
|
||||
public bool IsTrashed { get; set; }
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
|
||||
public sealed class DocumentUrlInfo : ContentUrlInfoBase
|
||||
public class DocumentUrlInfo : ContentUrlInfoBase
|
||||
{
|
||||
public required string? Message { get; init; }
|
||||
|
||||
public required string Provider { get; init; }
|
||||
}
|
||||
|
||||
@@ -5,9 +5,6 @@ namespace Umbraco.Cms.Api.Management.ViewModels.Media;
|
||||
|
||||
public class MediaResponseModel : ContentResponseModelBase<MediaValueResponseModel, MediaVariantResponseModel>
|
||||
{
|
||||
[Obsolete("This property is no longer populated. Please use /media/{id}/urls instead to retrieve the URLs for a document. Scheduled for removal in Umbraco 17.")]
|
||||
public IEnumerable<MediaUrlInfo> Urls { get; set; } = Enumerable.Empty<MediaUrlInfo>();
|
||||
|
||||
public bool IsTrashed { get; set; }
|
||||
|
||||
public MediaTypeReferenceResponseModel MediaType { get; set; } = new();
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace Umbraco.Cms.Api.Management.ViewModels.ModelsBuilderDashboard;
|
||||
|
||||
public class ModelsBuilderResponseModel
|
||||
{
|
||||
public ModelsMode Mode { get; set; }
|
||||
public required string Mode { get; set; }
|
||||
|
||||
public bool CanGenerate { get; set; }
|
||||
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.NewsDashboard;
|
||||
|
||||
public class NewsDashboardItemResponseModel
|
||||
{
|
||||
public required string Priority { get; set; }
|
||||
|
||||
public required string Header { get; set; }
|
||||
|
||||
public string? Body { get; set; }
|
||||
|
||||
public string? ButtonText { get; set; }
|
||||
|
||||
public string? ImageUrl { get; set; }
|
||||
|
||||
public string? ImageAltText { get; set; }
|
||||
|
||||
public string? Url { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.NewsDashboard;
|
||||
|
||||
public class NewsDashboardResponseModel
|
||||
{
|
||||
public required IEnumerable<NewsDashboardItemResponseModel> Items { get; set; }
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Cms.DevelopmentMode.Backoffice.DependencyInjection;
|
||||
|
||||
public class BackofficeDevelopmentComposer : IComposer
|
||||
{
|
||||
public void Compose(IUmbracoBuilder builder) => builder.AddBackofficeDevelopment();
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.DevelopmentMode.Backoffice.InMemoryAuto;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.DevelopmentMode.Backoffice.DependencyInjection;
|
||||
|
||||
|
||||
/*
|
||||
* OVERVIEW:
|
||||
*
|
||||
* The CSharpCompiler is responsible for the actual compilation of razor at runtime.
|
||||
* It creates a CSharpCompilation instance to do the compilation. This is where DLL references
|
||||
* are applied. However, the way this works is not flexible for dynamic assemblies since the references
|
||||
* are only discovered and loaded once before the first compilation occurs. This is done here:
|
||||
* https://github.com/dotnet/aspnetcore/blob/114f0f6d1ef1d777fb93d90c87ac506027c55ea0/src/Mvc/Mvc.Razor.RuntimeCompilation/src/CSharpCompiler.cs#L79
|
||||
* The CSharpCompiler is internal and cannot be replaced or extended, however it's references come from:
|
||||
* RazorReferenceManager. Unfortunately this is also internal and cannot be replaced, though it can be extended
|
||||
* using MvcRazorRuntimeCompilationOptions, except this is the place where references are only loaded once which
|
||||
* is done with a LazyInitializer. See https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Razor.RuntimeCompilation/src/RazorReferenceManager.cs#L35.
|
||||
*
|
||||
* The way that RazorReferenceManager works is by resolving references from the ApplicationPartsManager - either by
|
||||
* an application part that is specifically an ICompilationReferencesProvider or an AssemblyPart. So to fulfill this
|
||||
* requirement, we add the MB assembly to the assembly parts manager within the InMemoryModelFactory when the assembly
|
||||
* is (re)generated. But due to the above restrictions, when re-generating, this will have no effect since the references
|
||||
* have already been resolved with the LazyInitializer in the RazorReferenceManager. There is a known public API
|
||||
* where you can add reference paths to the runtime razor compiler via it's IOptions: MvcRazorRuntimeCompilationOptions
|
||||
* however this falls short too because those references are just loaded via the RazorReferenceManager and lazy initialized.
|
||||
*
|
||||
* The services that can be replaced are: IViewCompilerProvider (default is the internal RuntimeViewCompilerProvider) and
|
||||
* IViewCompiler (default is the internal RuntimeViewCompiler). There is one specific public extension point that I was
|
||||
* hoping would solve all of the problems which was IMetadataReferenceFeature (implemented by LazyMetadataReferenceFeature
|
||||
* which uses RazorReferencesManager) which is a razor feature that you can add
|
||||
* to the RazorProjectEngine. It is used to resolve roslyn references and by default is backed by RazorReferencesManager.
|
||||
* Unfortunately, this service is not used by the CSharpCompiler, it seems to only be used by some tag helper compilations.
|
||||
*
|
||||
* There are caches at several levels, all of which are not publicly accessible APIs (apart from RazorViewEngine.ViewLookupCache
|
||||
* which is possible to clear by casting and then calling cache.Compact(100); but that doesn't get us far enough).
|
||||
*
|
||||
* For this to work, several caches must be cleared:
|
||||
* - RazorViewEngine.ViewLookupCache
|
||||
* - RazorReferencesManager._compilationReferences
|
||||
* - RazorPageActivator._activationInfo (though this one may be optional)
|
||||
* - RuntimeViewCompiler._cache
|
||||
*
|
||||
* What are our options?
|
||||
*
|
||||
* a) We can copy a ton of code into our application: CSharpCompiler, RuntimeViewCompilerProvider, RuntimeViewCompiler and
|
||||
* RazorReferenceManager (probably more depending on the extent of Internal references).
|
||||
* b) We can use reflection to try to access all of the above resources and try to forcefully clear caches and reset initialization flags.
|
||||
* c) We hack these replace-able services with our own implementations that wrap the default services. To do this
|
||||
* requires re-resolving the original services from a pre-built DI container. In effect this re-creates these
|
||||
* services from scratch which means there is no caches.
|
||||
*
|
||||
* ... Option C worked, however after a breaking change from dotnet, we cannot go with this options any longer.
|
||||
* The reason for this is that when the default RuntimeViewCompiler loads in the assembly using Assembly.Load,
|
||||
* This will not work for us since this loads the compiled views into the default AssemblyLoadContext,
|
||||
* and our compiled models are loaded in the collectible UmbracoAssemblyLoadContext, and as per the breaking change
|
||||
* you're no longer allowed reference a collectible load context from a non-collectible one
|
||||
* That is the non-collectible compiled views are not allowed to reference the collectible InMemoryAuto models.
|
||||
* https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/7.0/collectible-assemblies
|
||||
*
|
||||
* So what do we do then?
|
||||
* We've had to go with option a unfortunately, and we've cloned the above classes
|
||||
* There has had to be some modifications to the ViewCompiler (CollectibleRuntimeViewCompiler)
|
||||
* First off we've added a new class InMemoryAssemblyLoadContextManager, the role of this class is to ensure that
|
||||
* no one will take a reference to the assembly load context (you cannot unload an assembly load context if there's any references to it).
|
||||
* This means that both the InMemoryAutoFactory and the ViewCompiler uses the LoadContextManager to load their assemblies.
|
||||
* This serves another purpose being that it keeps track of the location of the models assembly.
|
||||
* This means that we no longer use the RazorReferencesManager to resolve that specific dependency, but instead add and explicit dependency to the models assembly.
|
||||
*
|
||||
* With this our assembly load context issue is solved, however the caching issue still persists now that we no longer use the RefreshingRazorViewEngine
|
||||
* To clear these caches another class the RuntimeCompilationCacheBuster has been introduced,
|
||||
* this keeps a reference to the CollectibleRuntimeViewCompiler and the RazorViewEngine and is injected into the InMemoryModelsFactory to clear the caches when rebuilding modes.
|
||||
* In order to avoid having to copy all the RazorViewEngine code the cache buster uses reflection to call the internal ClearCache method of the RazorViewEngine.
|
||||
*/
|
||||
public static class UmbracoBuilderExtensions
|
||||
{
|
||||
public static IUmbracoBuilder AddBackofficeDevelopment(this IUmbracoBuilder builder)
|
||||
{
|
||||
if (builder.Config.GetRuntimeMode() != RuntimeMode.BackofficeDevelopment)
|
||||
{
|
||||
return builder;
|
||||
}
|
||||
|
||||
builder.AddMvcAndRazor(mvcBuilder =>
|
||||
{
|
||||
mvcBuilder.AddRazorRuntimeCompilation();
|
||||
});
|
||||
builder.AddInMemoryModelsRazorEngine();
|
||||
builder.RuntimeModeValidators()
|
||||
.Add<InMemoryModelsBuilderModeValidator>();
|
||||
builder.AddNotificationHandler<ModelBindingErrorNotification, ModelsBuilderBindingErrorHandler>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
// See notes in RefreshingRazorViewEngine for information on what this is doing.
|
||||
private static IUmbracoBuilder AddInMemoryModelsRazorEngine(this IUmbracoBuilder builder)
|
||||
{
|
||||
// We should only add/replace these services when models builder is InMemory, otherwise we'll cause issues.
|
||||
// Since these services expect the ModelsMode to be InMemoryAuto
|
||||
if (builder.Config.GetModelsMode() == ModelsModeConstants.InMemoryAuto)
|
||||
{
|
||||
builder.Services.AddSingleton<UmbracoRazorReferenceManager>();
|
||||
builder.Services.AddSingleton<CompilationOptionsProvider>();
|
||||
builder.Services.AddSingleton<IViewCompilerProvider, UmbracoViewCompilerProvider>();
|
||||
builder.Services.AddSingleton<RuntimeCompilationCacheBuster>();
|
||||
builder.Services.AddSingleton<InMemoryAssemblyLoadContextManager>();
|
||||
|
||||
builder.Services.AddSingleton<InMemoryModelFactory>();
|
||||
// Register the factory as IPublishedModelFactory
|
||||
builder.Services.AddSingleton<IPublishedModelFactory, InMemoryModelFactory>();
|
||||
return builder;
|
||||
}
|
||||
|
||||
// This is what the community MB would replace, all of the above services are fine to be registered
|
||||
builder.Services.AddSingleton<IPublishedModelFactory>(factory => factory.CreateDefaultPublishedModelFactory());
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ using System.Globalization;
|
||||
using Microsoft.AspNetCore.Razor.Hosting;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto;
|
||||
namespace Umbraco.Cms.DevelopmentMode.Backoffice.InMemoryAuto;
|
||||
|
||||
/*
|
||||
* This is a clone of Microsofts implementation.
|
||||
+1
-2
@@ -2,7 +2,6 @@ using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.AspNetCore.Razor.Hosting;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
@@ -16,7 +15,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto;
|
||||
namespace Umbraco.Cms.DevelopmentMode.Backoffice.InMemoryAuto;
|
||||
|
||||
internal sealed class CollectibleRuntimeViewCompiler : IViewCompiler
|
||||
{
|
||||
+1
-1
@@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto;
|
||||
namespace Umbraco.Cms.DevelopmentMode.Backoffice.InMemoryAuto;
|
||||
|
||||
/*
|
||||
* This is a partial clone of the frameworks CompilationFailedExceptionFactory, a few things has been simplified to fit our needs.
|
||||
+1
-1
@@ -9,7 +9,7 @@ using Microsoft.Extensions.DependencyModel;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using DependencyContextCompilationOptions = Microsoft.Extensions.DependencyModel.CompilationOptions;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto;
|
||||
namespace Umbraco.Cms.DevelopmentMode.Backoffice.InMemoryAuto;
|
||||
|
||||
/*
|
||||
* This is a partial Clone'n'Own of microsofts CSharpCompiler, this is just the parts relevant for getting the CompilationOptions
|
||||
+1
-2
@@ -1,8 +1,7 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using Umbraco.Cms.Infrastructure.ModelsBuilder;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto;
|
||||
namespace Umbraco.Cms.DevelopmentMode.Backoffice.InMemoryAuto;
|
||||
|
||||
internal sealed class InMemoryAssemblyLoadContextManager
|
||||
{
|
||||
+2
-3
@@ -8,7 +8,6 @@ using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Extensions;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
@@ -20,7 +19,7 @@ using Umbraco.Extensions;
|
||||
using File = System.IO.File;
|
||||
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto
|
||||
namespace Umbraco.Cms.DevelopmentMode.Backoffice.InMemoryAuto
|
||||
{
|
||||
internal sealed partial class InMemoryModelFactory : IAutoPublishedModelFactory, IRegisteredObject, IDisposable
|
||||
{
|
||||
@@ -143,7 +142,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Enabled => _config.ModelsMode == ModelsMode.InMemoryAuto;
|
||||
public bool Enabled => _config.ModelsMode == ModelsModeConstants.InMemoryAuto;
|
||||
|
||||
public IPublishedElement CreateModel(IPublishedElement element)
|
||||
{
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Infrastructure.Runtime;
|
||||
|
||||
namespace Umbraco.Cms.DevelopmentMode.Backoffice.InMemoryAuto;
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the ModelsBuilder mode is not set to InMemoryAuto when in development runtime mode.
|
||||
/// </summary>
|
||||
public class InMemoryModelsBuilderModeValidator : IRuntimeModeValidator
|
||||
{
|
||||
private readonly IOptionsMonitor<ModelsBuilderSettings> _modelsBuilderSettings;
|
||||
|
||||
public InMemoryModelsBuilderModeValidator(IOptionsMonitor<ModelsBuilderSettings> modelsBuilderSettings)
|
||||
{
|
||||
_modelsBuilderSettings = modelsBuilderSettings;
|
||||
}
|
||||
|
||||
public bool Validate(RuntimeMode runtimeMode, [NotNullWhen(false)] out string? validationErrorMessage)
|
||||
{
|
||||
if (runtimeMode != RuntimeMode.BackofficeDevelopment &&
|
||||
_modelsBuilderSettings.CurrentValue.ModelsMode == ModelsModeConstants.InMemoryAuto)
|
||||
{
|
||||
validationErrorMessage = "ModelsBuilder mode cannot be set to InMemoryAuto in development mode.";
|
||||
return false;
|
||||
}
|
||||
|
||||
validationErrorMessage = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user