Compare commits
325
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6ab394947 | ||
|
|
d8f68d2c40 | ||
|
|
ff88617db0 | ||
|
|
9f912aea0e | ||
|
|
ba95c12f09 | ||
|
|
14fbd20665 | ||
|
|
2d8b5e8786 | ||
|
|
747e095178 | ||
|
|
1cfa5a225e | ||
|
|
7888b9a4ce | ||
|
|
e31582b297 | ||
|
|
75cc017a18 | ||
|
|
d60137e6da | ||
|
|
9f9c88781a | ||
|
|
c7014e159b | ||
|
|
31e1acce67 | ||
|
|
35c51a029a | ||
|
|
8c1128c85b | ||
|
|
c9021ab2d2 | ||
|
|
67a71f8f82 | ||
|
|
edd0a4a4a9 | ||
|
|
11270eaaf5 | ||
|
|
2d71b5a63b | ||
|
|
9bab74d30e | ||
|
|
0ee0db8071 | ||
|
|
c17d4e1a60 | ||
|
|
fee222daff | ||
|
|
119fde2033 | ||
|
|
52c21b0fca | ||
|
|
99d5a7e609 | ||
|
|
7e1d1a1968 | ||
|
|
b743f6a2df | ||
|
|
a2511ff09b | ||
|
|
2c23e67c65 | ||
|
|
8e837d387d | ||
|
|
801fb5f885 | ||
|
|
ed517ecd86 | ||
|
|
b5e46ba880 | ||
|
|
cbf9f9bcd1 | ||
|
|
2735f17ed8 | ||
|
|
e37cf30690 | ||
|
|
118ac8e230 | ||
|
|
3e28e10cdf | ||
|
|
b18b6cc5e7 | ||
|
|
7a04baf9ef | ||
|
|
bc1ddaed8c | ||
|
|
860afb35f2 | ||
|
|
9799c550f4 | ||
|
|
b7e43a8def | ||
|
|
cdbbd6a921 | ||
|
|
5198e7c52d | ||
|
|
49f5d2e2d4 | ||
|
|
57b3a196bf | ||
|
|
dff90c6ec0 | ||
|
|
040495f359 | ||
|
|
a2ad95d965 | ||
|
|
64f2447c0e | ||
|
|
b648126d19 | ||
|
|
812b414d96 | ||
|
|
ec91c47158 | ||
|
|
e04a41be45 | ||
|
|
d5156aced2 | ||
|
|
fa9b0e4718 | ||
|
|
8915064780 | ||
|
|
677ff59d04 | ||
|
|
1fe2ec08be | ||
|
|
5dfa76600a | ||
|
|
83bcd37250 | ||
|
|
4a7ad4a562 | ||
|
|
cdd4d2a000 | ||
|
|
be5a740c96 | ||
|
|
13cc320f19 | ||
|
|
b5544aa520 | ||
|
|
9e3f8c7d0c | ||
|
|
cb76554455 | ||
|
|
37e4d80ce8 | ||
|
|
d5d4edbe92 | ||
|
|
8d9343b564 | ||
|
|
1ee891c8d5 | ||
|
|
bf1a5b9b28 | ||
|
|
696b74cdb6 | ||
|
|
bf8af4588e | ||
|
|
4781fb1d8b | ||
|
|
d714741bee | ||
|
|
ea33b73daa | ||
|
|
ba64b46824 | ||
|
|
04e59e9a9d | ||
|
|
be39b9c19a | ||
|
|
9a143c0850 | ||
|
|
df4cd63962 | ||
|
|
a17e398f28 | ||
|
|
9af9dd2c02 | ||
|
|
2266a98fc6 | ||
|
|
151fccee97 | ||
|
|
abfa07367f | ||
|
|
e5de8bf56b | ||
|
|
00bb5170a0 | ||
|
|
919138c299 | ||
|
|
ed65645a33 | ||
|
|
d0be4ab263 | ||
|
|
ddb26632f9 | ||
|
|
d5b1f5875b | ||
|
|
f8c1a560ff | ||
|
|
9e5ff305c8 | ||
|
|
ffaa77eb25 | ||
|
|
6777145079 | ||
|
|
92af8ac881 | ||
|
|
bde88caf14 | ||
|
|
f4ac6cdded | ||
|
|
132935c2d7 | ||
|
|
04b05714e5 | ||
|
|
7be1852827 | ||
|
|
158cef1692 | ||
|
|
1a4f3981eb | ||
|
|
7765f98ee1 | ||
|
|
6b72880c0b | ||
|
|
d9f3936a43 | ||
|
|
bf09c87f87 | ||
|
|
ec2134dea0 | ||
|
|
8ccc2faee0 | ||
|
|
0a4cc27a5d | ||
|
|
c8e054baa2 | ||
|
|
95ffdae475 | ||
|
|
ca8ac21f4b | ||
|
|
f85a0d1715 | ||
|
|
2fc29e638c | ||
|
|
56486b1ace | ||
|
|
fd043f6ecf | ||
|
|
39a102ed8a | ||
|
|
f750bca453 | ||
|
|
7e36203f60 | ||
|
|
334dff8645 | ||
|
|
9fd140f81d | ||
|
|
1198c76d67 | ||
|
|
869b480dae | ||
|
|
12704b112e | ||
|
|
3c37653012 | ||
|
|
e5a8d01004 | ||
|
|
9fd0b1986c | ||
|
|
80ea5174c6 | ||
|
|
e1b4aebb0f | ||
|
|
38910a8d5c | ||
|
|
0dc18982ee | ||
|
|
311d322129 | ||
|
|
e7af98027d | ||
|
|
30f0cfc15d | ||
|
|
b743e715d4 | ||
|
|
d3243f8700 | ||
|
|
264e4f8b57 | ||
|
|
2b3f048db7 | ||
|
|
18ee016ae9 | ||
|
|
512199c8ab | ||
|
|
4e410a9dd5 | ||
|
|
e16e36c68f | ||
|
|
f384e0c30c | ||
|
|
04b74dddd0 | ||
|
|
43700d2bcb | ||
|
|
eed9feb5f3 | ||
|
|
b2a5ce3e48 | ||
|
|
0ae404ce03 | ||
|
|
ab836d2326 | ||
|
|
c12cce6d8c | ||
|
|
78e1da3231 | ||
|
|
82eae48d09 | ||
|
|
a5ba32bfd7 | ||
|
|
c695f2192f | ||
|
|
dc5c7a14a5 | ||
|
|
c751e90928 | ||
|
|
d86e4b2d38 | ||
|
|
3f196a9de4 | ||
|
|
30ec9d9bc7 | ||
|
|
3087fc3ba9 | ||
|
|
7fd7a8715d | ||
|
|
62fd860a9e | ||
|
|
76ad99beb0 | ||
|
|
ebe375d23b | ||
|
|
2f35967bb2 | ||
|
|
f93f3d88da | ||
|
|
dbd009ffcd | ||
|
|
b99e14e288 | ||
|
|
b8415ed7f0 | ||
|
|
8f6370a480 | ||
|
|
aa4a1b3498 | ||
|
|
b716e98780 | ||
|
|
e86fb0eeb6 | ||
|
|
fe1080dd64 | ||
|
|
1133c4e7f2 | ||
|
|
f5b834974d | ||
|
|
75113a90fb | ||
|
|
7e573c70a3 | ||
|
|
5c5de77965 | ||
|
|
2f486610bb | ||
|
|
d19a30768f | ||
|
|
b451bb802b | ||
|
|
785390efd7 | ||
|
|
3b3e1cec40 | ||
|
|
89d104e176 | ||
|
|
9a3e655645 | ||
|
|
5f15b8abb1 | ||
|
|
4df3e19af7 | ||
|
|
89d670372d | ||
|
|
b5ab115318 | ||
|
|
407263d090 | ||
|
|
b37bb517ab | ||
|
|
999a83995e | ||
|
|
b3a93ac86c | ||
|
|
e79246368b | ||
|
|
b67c7fbe77 | ||
|
|
16f448a802 | ||
|
|
b3bedb4efe | ||
|
|
5b73fc19ee | ||
|
|
4cdab08910 | ||
|
|
391cade75f | ||
|
|
e27e8d1189 | ||
|
|
1e0ace7a34 | ||
|
|
21c6828b59 | ||
|
|
f7f361239c | ||
|
|
05908e2c78 | ||
|
|
3a7ba530a2 | ||
|
|
f4073c1dc7 | ||
|
|
7b5233803e | ||
|
|
822febb8e8 | ||
|
|
bf7b9a20a8 | ||
|
|
9aadb41f4a | ||
|
|
aaec419672 | ||
|
|
66b1add0d0 | ||
|
|
81c6a1b8e6 | ||
|
|
747f2e7599 | ||
|
|
f9aaf1984a | ||
|
|
539640f995 | ||
|
|
07a45d02f9 | ||
|
|
bb7752c483 | ||
|
|
d1975ee2cf | ||
|
|
4d13345b14 | ||
|
|
02e16992b4 | ||
|
|
5d3546e1b5 | ||
|
|
5828080589 | ||
|
|
8e32ac3e20 | ||
|
|
22328598db | ||
|
|
ce47281c04 | ||
|
|
83ad463e81 | ||
|
|
06f527e87b | ||
|
|
6bc999bca4 | ||
|
|
5f8ba2e864 | ||
|
|
eee6207f19 | ||
|
|
888173ac5b | ||
|
|
7208f591c7 | ||
|
|
391bfb73e8 | ||
|
|
9f2e79e9db | ||
|
|
40285b76b9 | ||
|
|
aab6539d1f | ||
|
|
5598cc2491 | ||
|
|
b665099657 | ||
|
|
010f722fa0 | ||
|
|
4f8b8b3415 | ||
|
|
b1fd9e6f16 | ||
|
|
d31bd8755e | ||
|
|
6b208362cc | ||
|
|
9d1140165e | ||
|
|
f70533e3a0 | ||
|
|
53c547fb61 | ||
|
|
82c21da5b7 | ||
|
|
2d32f46d80 | ||
|
|
e9d17d3b9f | ||
|
|
7c77fd1ca9 | ||
|
|
d4970ed752 | ||
|
|
66c786a42d | ||
|
|
e6c0826d39 | ||
|
|
8a3cf2731e | ||
|
|
b8d6613bd8 | ||
|
|
12414013d5 | ||
|
|
5fcbd4e4cd | ||
|
|
6b309dc3be | ||
|
|
458688aed6 | ||
|
|
a56a6bae31 | ||
|
|
010ea5a2aa | ||
|
|
84d81b20ef | ||
|
|
d148e93b45 | ||
|
|
0ce2fdb23b | ||
|
|
273e7708e5 | ||
|
|
154021801e | ||
|
|
d418d06c02 | ||
|
|
c684b38247 | ||
|
|
dca1c1b1be | ||
|
|
86247b4578 | ||
|
|
ef4c9c508d | ||
|
|
f7ba2cfe96 | ||
|
|
80a2839797 | ||
|
|
aacac4997d | ||
|
|
815eb5b9c8 | ||
|
|
5966ad4a05 | ||
|
|
06b89963e4 | ||
|
|
caa4ca510e | ||
|
|
0ecb613b4a | ||
|
|
48ad91392f | ||
|
|
5e555c6129 | ||
|
|
47078bcc70 | ||
|
|
0ba1254e7d | ||
|
|
d0e34c38a0 | ||
|
|
6be8d2e885 | ||
|
|
bfd29dfc3b | ||
|
|
4b2af296be | ||
|
|
17aa9cee51 | ||
|
|
f035b2bee2 | ||
|
|
4d4b5b7007 | ||
|
|
c04b11cf17 | ||
|
|
6404f2dae8 | ||
|
|
0c6632eeb4 | ||
|
|
4e3ac66865 | ||
|
|
de6e9aeb31 | ||
|
|
a8a8a7fea5 | ||
|
|
84e20e74d3 | ||
|
|
220414114e | ||
|
|
2538f946fc | ||
|
|
eb07998cf2 | ||
|
|
7119253e6f | ||
|
|
9cfda6e05c | ||
|
|
555f6e2514 | ||
|
|
cc76289a36 | ||
|
|
f9ed9c0ae8 | ||
|
|
d9f342bee7 | ||
|
|
e529818560 | ||
|
|
45f8ffcd0b | ||
|
|
7d9e07ff7c | ||
|
|
b83c88e9f1 |
@@ -22,3 +22,7 @@ RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/
|
||||
# https://docs.npmjs.com/cli/v6/using-npm/config#unsafe-perm
|
||||
# Default: false if running as root, true otherwise (we are ROOT)
|
||||
#RUN npm -g config set user vscode && npm -g config set unsafe-perm
|
||||
|
||||
# Generate and trust a local developer certificate for Kestrel
|
||||
# This is needed for Kestrel to bind on https
|
||||
RUN dotnet dev-certs https --trust
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
"service": "app",
|
||||
"workspaceFolder": "/workspace",
|
||||
|
||||
|
||||
// Set *default* container specific settings.json values on container create.
|
||||
"settings": {
|
||||
"omnisharp.defaultLaunchSolution": "umbraco.sln",
|
||||
@@ -14,62 +13,13 @@
|
||||
"omnisharp.enableRoslynAnalyzers": true
|
||||
},
|
||||
|
||||
"features": {
|
||||
// Workaround until the image is updated to include the latest version of .NET Core - .NET7
|
||||
// https://github.com/devcontainers/templates/issues/38#issuecomment-1310803259
|
||||
"ghcr.io/devcontainers/features/dotnet:1": {
|
||||
"version": "7"
|
||||
},
|
||||
|
||||
// Adds SSH support to the container
|
||||
// Allowing the Github CLI `gh codespace ssh` to work
|
||||
"ghcr.io/devcontainers/features/sshd:1": {
|
||||
"version": "latest"
|
||||
},
|
||||
|
||||
// Adds SQLite feature from apt install
|
||||
// Also adds the SQLite VSCode extension
|
||||
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
|
||||
},
|
||||
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": [
|
||||
"ms-dotnettools.csharp"
|
||||
],
|
||||
|
||||
// This is used in the prebuilds - so dotnet build (nuget restore and node stuff) is done
|
||||
"updateContentCommand": "dotnet build umbraco.sln && dotnet dev-certs https --trust",
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locallAdd "forwardPorts": [9000, 5000, 25],
|
||||
// Port needed to help discover SMTP4Dev
|
||||
"forwardPorts": [
|
||||
5000
|
||||
],
|
||||
|
||||
"portsAttributes": {
|
||||
"5000": {
|
||||
"label": "SMTP4Dev",
|
||||
"protocol": "http",
|
||||
"onAutoForward": "notify"
|
||||
},
|
||||
"9000": {
|
||||
"label": "Umbraco HTTP",
|
||||
"protocol": "http",
|
||||
"onAutoForward": "notify"
|
||||
},
|
||||
"44331": {
|
||||
"label": "Umbraco HTTPS",
|
||||
"protocol": "https",
|
||||
"onAutoForward": "notify"
|
||||
}
|
||||
},
|
||||
"customizations": {
|
||||
"codespaces": {
|
||||
"openFiles": [
|
||||
".github/codespaces-readme.md"
|
||||
]
|
||||
}
|
||||
}
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
"forwardPorts": [9000, 5000, 25]
|
||||
|
||||
// [Optional] To reuse of your local HTTPS dev cert:
|
||||
//
|
||||
|
||||
@@ -248,7 +248,6 @@ csharp_preserve_single_line_blocks = true
|
||||
##########################################
|
||||
|
||||
[*.{cs,csx,cake,vb,vbx}]
|
||||
dotnet_diagnostic.CS1591.severity = suggestion
|
||||
|
||||
##########################################
|
||||
# Styles
|
||||
|
||||
+147
-16
@@ -1,4 +1,4 @@
|
||||
# Umbraco CMS Build
|
||||
# Umbraco CMS Build
|
||||
|
||||
## Are you sure?
|
||||
|
||||
@@ -99,33 +99,111 @@ When the page eventually loads in your web browser, you can follow the installer
|
||||
|
||||
Did you read ["Are you sure"](#are-you-sure)?
|
||||
|
||||
Do note that this is only required if you want to test out your custom changes in a separate site (not the one in the Umbraco.Web.UI), if you just want to test your changes you can run the included test site using: `dotnet run` from `src/Umbraco.Web.UI/`
|
||||
### Quick!
|
||||
|
||||
You may want to build a set of NuGet packages with your changes, this can be done using the dotnet pack command.
|
||||
To build Umbraco, fire up PowerShell and move to Umbraco's repository root (the directory that contains `src`, `build`, `LICENSE.md`...). There, trigger the build with the following command:
|
||||
|
||||
First enter the root of the project in a command line environment, and then use the following command to build the NuGet packages:
|
||||
build/build.ps1
|
||||
|
||||
`dotnet pack -c Release -o Build.Out`
|
||||
If you only see a build.bat-file, you're probably on the wrong branch. If you switch to the correct branch (v8/contrib) the file will appear and you can build it.
|
||||
|
||||
This will restore and build the project using the release configuration, and put all the outputted files in a folder called `Build.Out`
|
||||
You might run into [Powershell quirks](#powershell-quirks).
|
||||
|
||||
You can then add these as a local NuGet feed using the following command:
|
||||
If it runs without errors; Hooray! Now you can continue with [the next step](CONTRIBUTING.md#how-do-i-begin) and open the solution and build it.
|
||||
|
||||
`dotnet nuget add source <Path to Build.Out folder> -n MyLocalFeed`
|
||||
|
||||
This will add a local nuget feed with the name "MyLocalFeed" and you'll now be able to use your custom built NuGet packages.
|
||||
### Build Infrastructure
|
||||
|
||||
The Umbraco Build infrastructure relies on a PowerShell object. The object can be retrieved with:
|
||||
|
||||
$ubuild = build/build.ps1 -get
|
||||
|
||||
The object exposes various properties and methods that can be used to fine-grain build Umbraco. Some, but not all, of them are detailed below.
|
||||
|
||||
#### Properties
|
||||
|
||||
The object exposes the following properties:
|
||||
|
||||
* `SolutionRoot`: the absolute path to the solution root
|
||||
* `VisualStudio`: a Visual Studio object (see below)
|
||||
* `NuGet`: the absolute path to the NuGet executable
|
||||
* `Zip`: the absolute path to the 7Zip executable
|
||||
* `VsWhere`: the absolute path to the VsWhere executable
|
||||
* `NodePath`: the absolute path to the Node install
|
||||
* `NpmPath`: the absolute path to the Npm install
|
||||
|
||||
The Visual Studio object is `null` when Visual Studio has not been detected (eg on VSTS). When not null, the object exposes the following properties:
|
||||
|
||||
* `Path`: Visual Studio installation path (eg some place under `Program Files`)
|
||||
* `Major`: Visual Studio major version (eg `15` for VS 2017)
|
||||
* `Minor`: Visual Studio minor version
|
||||
* `MsBuild`: the absolute path to the MsBuild executable
|
||||
|
||||
#### GetUmbracoVersion
|
||||
|
||||
Gets an object representing the current Umbraco version. Example:
|
||||
|
||||
$v = $ubuild.GetUmbracoVersion()
|
||||
Write-Host $v.Semver
|
||||
|
||||
The object exposes the following properties:
|
||||
|
||||
* `Semver`: the semver object representing the version
|
||||
* `Release`: the main part of the version (eg `7.6.33`)
|
||||
* `Comment`: the pre release part of the version (eg `alpha02`)
|
||||
* `Build`: the build number part of the version (eg `1234`)
|
||||
|
||||
#### SetUmbracoVersion
|
||||
|
||||
Modifies Umbraco files with the new version.
|
||||
|
||||
>This entirely replaces the legacy `UmbracoVersion.txt` file. Do *not* edit version infos in files.
|
||||
|
||||
The version must be a valid semver version. It can include a *pre release* part (eg `alpha02`) and/or a *build number* (eg `1234`). Examples:
|
||||
|
||||
$ubuild.SetUmbracoVersion("7.6.33")
|
||||
$ubuild.SetUmbracoVersion("7.6.33-alpha.2")
|
||||
$ubuild.SetUmbracoVersion("7.6.33+1234")
|
||||
$ubuild.SetUmbracoVersion("7.6.33-beta.5+5678")
|
||||
|
||||
#### Build
|
||||
|
||||
Builds Umbraco. Temporary files are generated in `build.tmp` while the actual artifacts (zip files, NuGet packages...) are produced in `build.out`. Example:
|
||||
|
||||
$ubuild.Build()
|
||||
|
||||
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
|
||||
|
||||
**Note: web.config**
|
||||
|
||||
Building Umbraco requires a clean `web.config` file in the `Umbraco.Web.UI` project. If a `web.config` file already exists, the `pre-build` task (see below) will save it as `web.config.temp-build` and replace it with a clean copy of `web.Template.config`. The original file is replaced once it is safe to do so, by the `pre-packages` task.
|
||||
|
||||
#### Build-UmbracoDocs
|
||||
|
||||
Builds umbraco documentation. Temporary files are generated in `build.tmp` while the actual artifacts (docs...) are produced in `build.out`. Example:
|
||||
|
||||
Build-UmbracoDocs
|
||||
|
||||
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
|
||||
|
||||
#### Verify-NuGet
|
||||
|
||||
Verifies that projects all require the same version of their dependencies, and that NuSpec files require versions that are consistent with projects. Example:
|
||||
|
||||
Verify-NuGet
|
||||
|
||||
### Cleaning up
|
||||
|
||||
Once the solution has been used to run a site, one may want to "reset" the solution in order to run a fresh new site again.
|
||||
|
||||
The easiest way to do this by deleting the following files and folders:
|
||||
* src/Umbraco.Web.UI/appsettings.json
|
||||
* src/Umbraco.Web.UI/umbraco/Data
|
||||
At the very minimum, you want
|
||||
|
||||
You only have to remove the connection strings from the appsettings, but removing the data folder ensures that the sqlite database gets deleted too.
|
||||
git clean -Xdf src/Umbraco.Web.UI/App_Data
|
||||
rm src/Umbraco.Web.UI/web.config
|
||||
|
||||
Next time you run a build the `appsettings.json` file will be re-created in its default state.
|
||||
Then, a simple 'Rebuild All' in Visual Studio will recreate a fresh `web.config` but should be quite fast (since it does not really need to rebuild anything).
|
||||
|
||||
The `clean` Git command force (`-f`) removes (`-X`, note the capital X) all files and directories (`-d`) that are ignored by Git.
|
||||
|
||||
This will leave media files and views around, but in most cases, it will be enough.
|
||||
|
||||
@@ -141,12 +219,65 @@ For git documentation see:
|
||||
|
||||
## Azure DevOps
|
||||
|
||||
Umbraco uses Azure DevOps for continuous integration, nightly builds and release builds. The Umbraco CMS project on DevOps [is available for anonymous users](https://umbraco.visualstudio.com/Umbraco%20Cms)..
|
||||
Umbraco uses Azure DevOps for continuous integration, nightly builds and release builds. The Umbraco CMS project on DevOps [is available for anonymous users](https://umbraco.visualstudio.com/Umbraco%20Cms).
|
||||
|
||||
The produced artifacts are published in a container that can be downloaded from DevOps called "nupkg" which contains all the NuGet packages that got built.
|
||||
DevOps uses the `Build-Umbraco` command several times, each time passing a different *target* parameter. The supported targets are:
|
||||
|
||||
* `pre-build`: prepares the build
|
||||
* `compile-belle`: compiles Belle
|
||||
* `compile-umbraco`: compiles Umbraco
|
||||
* `pre-tests`: prepares the tests
|
||||
* `compile-tests`: compiles the tests
|
||||
* `pre-packages`: prepares the packages
|
||||
* `pkg-zip`: creates the zip files
|
||||
* `pre-nuget`: prepares NuGet packages
|
||||
* `pkg-nuget`: creates NuGet packages
|
||||
|
||||
All these targets are executed when `Build-Umbraco` is invoked without a parameter (or with the `all` parameter). On VSTS, compilations (of Umbraco and tests) are performed by dedicated DevOps tasks. Similarly, creating the NuGet packages is also performed by dedicated DevOps tasks.
|
||||
|
||||
Finally, the produced artifacts are published in two containers that can be downloaded from DevOps: `zips` contains the zip files while `nuget` contains the NuGet packages.
|
||||
|
||||
>During a DevOps build, some environment `UMBRACO_*` variables are exported by the `pre-build` target and can be reused in other targets *and* in DevOps tasks. The `UMBRACO_TMP` environment variable is used in `Umbraco.Tests` to disable some tests that have issues with DevOps at the moment.
|
||||
|
||||
## Quirks
|
||||
|
||||
### PowerShell Quirks
|
||||
|
||||
There is a good chance that running `build.ps1` ends up in error, with messages such as
|
||||
|
||||
>The file ...\build.ps1 is not digitally signed. You cannot run this script on the current system. For more information about running scripts and setting execution policy, see about_Execution_Policies.
|
||||
|
||||
PowerShell has *Execution Policies* that may prevent the script from running. You can check the current policies with:
|
||||
|
||||
PS> Get-ExecutionPolicy -List
|
||||
|
||||
Scope ExecutionPolicy
|
||||
----- ---------------
|
||||
MachinePolicy Undefined
|
||||
UserPolicy Undefined
|
||||
Process Undefined
|
||||
CurrentUser Undefined
|
||||
LocalMachine RemoteSigned
|
||||
|
||||
Policies can be `Restricted`, `AllSigned`, `RemoteSigned`, `Unrestricted` and `Bypass`. Scopes can be `MachinePolicy`, `UserPolicy`, `Process`, `CurrentUser`, `LocalMachine`. You need the current policy to be `RemoteSigned`—as long as it is `Undefined`, the script cannot run. You can change the current user policy with:
|
||||
|
||||
PS> Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
|
||||
|
||||
Alternatively, you can do it at machine level, from within an elevated PowerShell session:
|
||||
|
||||
PS> Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy RemoteSigned
|
||||
|
||||
And *then* the script should run. It *might* however still complain about executing scripts, with messages such as:
|
||||
|
||||
>Security warning - Run only scripts that you trust. While scripts from the internet can be useful, this script can potentially harm your computer. If you trust this script, use the Unblock-File cmdlet to allow the script to run without this warning message. Do you want to run ...\build.ps1?
|
||||
[D] Do not run [R] Run once [S] Suspend [?] Help (default is "D"):
|
||||
|
||||
This is usually caused by the scripts being *blocked*. And that usually happens when the source code has been downloaded as a Zip file. When Windows downloads Zip files, they are marked as *blocked* (technically, they have a Zone.Identifier alternate data stream, with a value of "3" to indicate that they were downloaded from the Internet). And when such a Zip file is un-zipped, each and every single file is also marked as blocked.
|
||||
|
||||
The best solution is to unblock the Zip file before un-zipping: right-click the files, open *Properties*, and there should be a *Unblock* checkbox at the bottom of the dialog. If, however, the Zip file has already been un-zipped, it is possible to recursively unblock all files from PowerShell with:
|
||||
|
||||
PS> Get-ChildItem -Recurse *.* | Unblock-File
|
||||
|
||||
### Git Quirks
|
||||
|
||||
Git might have issues dealing with long file paths during build. You may want/need to enable `core.longpaths` support (see [this page](https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path) for details).
|
||||
|
||||
@@ -103,7 +103,7 @@ Great question! The short version goes like this:
|
||||
|
||||
1. **Switch to the correct branch**
|
||||
|
||||
Switch to the `contrib` branch
|
||||
Switch to the `v10/contrib` branch
|
||||
|
||||
1. **Build**
|
||||
|
||||
@@ -111,7 +111,7 @@ Great question! The short version goes like this:
|
||||
|
||||
1. **Branch**
|
||||
|
||||
Create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case issue number `12345`. Don't commit to `contrib`, create a new branch first.
|
||||
Create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case issue number `12345`. Don't commit to `v10/contrib`, create a new branch first.
|
||||
|
||||
1. **Change**
|
||||
|
||||
@@ -121,7 +121,7 @@ Great question! The short version goes like this:
|
||||
|
||||
Done? Yay! 🎉
|
||||
|
||||
Remember to commit to your new `temp` branch, and don't commit to `contrib`. Then you can push the changes up to your fork on GitHub.
|
||||
Remember to commit to your new `temp` branch, and don't commit to `v10/contrib`. Then you can push the changes up to your fork on GitHub.
|
||||
|
||||
#### Keeping your Umbraco fork in sync with the main repository
|
||||
[sync fork]: #keeping-your-umbraco-fork-in-sync-with-the-main-repository
|
||||
@@ -138,10 +138,10 @@ Then when you want to get the changes from the main repository:
|
||||
|
||||
```
|
||||
git fetch upstream
|
||||
git rebase upstream/contrib
|
||||
git rebase upstream/v10/contrib
|
||||
```
|
||||
|
||||
In this command we're syncing with the `contrib` branch, but you can of course choose another one if needed.
|
||||
In this command we're syncing with the `v10/contrib` branch, but you can of course choose another one if needed.
|
||||
|
||||
[More information on how this works can be found on the thoughtbot blog.][sync fork ext]
|
||||
|
||||
@@ -169,7 +169,7 @@ We recommend you to [sync with our repository][sync fork] before you submit your
|
||||
GitHub will have picked up on the new branch you've pushed and will offer to create a Pull Request. Click that green button and away you go.
|
||||

|
||||
|
||||
We like to use [git flow][git flow] as much as possible, but don't worry if you are not familiar with it. The most important thing you need to know is that when you fork the Umbraco repository, the default branch is set to `contrib`. This is the branch you should be targeting.
|
||||
We like to use [git flow][git flow] as much as possible, but don't worry if you are not familiar with it. The most important thing you need to know is that when you fork the Umbraco repository, the default branch is set to something, usually `v10/contrib`. If you are working on v9, this is the branch you should be targeting.
|
||||
|
||||
Please note: we are no longer accepting features for v8 and below but will continue to merge security fixes as and when they arise.
|
||||
|
||||
|
||||
+2
-3
@@ -1,4 +1,4 @@
|
||||
# [Umbraco CMS](https://umbraco.com) · [](../LICENSE.md) [](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [](CONTRIBUTING.md) [](https://twitter.com/intent/follow?screen_name=umbraco) [](https://discord.gg/umbraco) [](https://github.com/codespaces/new?hide_repo_select=true&ref=v11%2Fcontrib&repo=10601208&machine=basicLinux32gb&devcontainer_path=.devcontainer%2Fdevcontainer.json&location=WestEurope)
|
||||
# [Umbraco CMS](https://umbraco.com) · [](../LICENSE.md) [](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [](CONTRIBUTING.md) [](https://twitter.com/intent/follow?screen_name=umbraco) [](https://discord.gg/umbraco)
|
||||
|
||||
Umbraco is the friendliest, most flexible and fastest growing ASP.NET CMS, and used by more than 500,000 websites worldwide. Our mission is to help you deliver delightful digital experiences by making Umbraco friendly, simpler and social.
|
||||
|
||||
@@ -31,8 +31,7 @@ The documentation for Umbraco CMS can be found [on Our Umbraco](https://docs.umb
|
||||
|
||||
Our friendly community is available 24/7 at the community hub, we call ["Our Umbraco"](https://our.umbraco.com/). Our Umbraco features forums for questions and answers, documentation, downloadable plugins for Umbraco, and a rich collection of community resources.
|
||||
|
||||
Besides "Our", we all support each other in our [Community Discord Server](https://discord.gg/umbraco) and on Twitter: [Umbraco HQ](https://twitter.com/umbraco), [Release Updates](https://twitter.com/umbracoproject), [#umbraco](https://twitter.com/hashtag/umbraco)
|
||||
|
||||
Besides "Our", we all support each other also via Twitter: [Umbraco HQ](https://twitter.com/umbraco), [Release Updates](https://twitter.com/umbracoproject), [#umbraco](https://twitter.com/hashtag/umbraco)
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# GitHub CodeSpaces
|
||||
Umbraco source code can be edited inside the browser with VSCode and CodeSpaces.
|
||||
|
||||
This development environment comes with all the tools you need to build Umbraco source code.
|
||||
|
||||
|
||||
## Debugging and Running
|
||||
From VSCode browse to the Run and Debug section and then click the green button. This will build the Umbraco source code and attach a debugger and launch the site.
|
||||
|
||||
## Default Umbraco credentials
|
||||
Username: test@umbraco.com
|
||||
Password: password1234
|
||||
|
||||
## Test Email Server
|
||||
A SMTP4Dev instance for testing email is available on port 5000.
|
||||
|
||||
## SQLite Database
|
||||
The SQLite extension is preinstalled and allows you to open, query, edit the data inside the Umbraco SQLite database for ease of use.
|
||||
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
dotnetVersion: 7.x
|
||||
dotnetIncludePreviewVersions: true
|
||||
dotnetVersion: 6.x
|
||||
dotnetIncludePreviewVersions: false
|
||||
solution: umbraco.sln
|
||||
buildConfiguration: SkipTests
|
||||
DOTNET_NOLOGO: true
|
||||
|
||||
@@ -11,15 +11,10 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
npm install node-fetch@2
|
||||
- name: Fetch random comment 🗣️ and add it to the PR
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const fetch = require('node-fetch')
|
||||
|
||||
const response = await fetch('https://collaboratorsv2.euwest01.umbraco.io/umbraco/api/comments/PostComment', {
|
||||
method: 'post',
|
||||
body: JSON.stringify({
|
||||
@@ -46,13 +41,13 @@ jobs:
|
||||
});
|
||||
} else {
|
||||
console.log("Returned data not indicate success.");
|
||||
|
||||
|
||||
if(response.status !== 200) {
|
||||
console.log("Status code:", response.status)
|
||||
}
|
||||
|
||||
console.log("Returned data:", data);
|
||||
|
||||
|
||||
if(data === '') {
|
||||
console.log("An empty response usually indicates that either no comment was found or the actor user was not eligible for getting an automated response (HQ users are not getting auto-responses).")
|
||||
}
|
||||
|
||||
+2
-8
@@ -101,12 +101,6 @@ preserve.belle
|
||||
/tests/Umbraco.Tests.UnitTests/[Uu]mbraco/[Dd]ata/TEMP/
|
||||
|
||||
# Ignore auto-generated schema
|
||||
/src/Umbraco.Cms.Targets/tasks/
|
||||
/src/Umbraco.Cms.Targets/appsettings-schema.*.json
|
||||
/src/Umbraco.Web.UI/appsettings-schema.json
|
||||
/src/Umbraco.Web.UI/appsettings-schema.*.json
|
||||
/tests/Umbraco.Tests.Integration/appsettings-schema.json
|
||||
/tests/Umbraco.Tests.Integration/appsettings-schema.*.json
|
||||
/src/Umbraco.Cms/appsettings-schema.json
|
||||
playwright-report
|
||||
trace.zip
|
||||
/src/Umbraco.Web.UI/appsettings-schema.json
|
||||
/tests/Umbraco.Tests.Integration/appsettings-schema.json
|
||||
|
||||
Vendored
+1
@@ -8,6 +8,7 @@
|
||||
"name": ".NET Core Launch (web)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "Dotnet build",
|
||||
"program": "dotnet",
|
||||
"args": ["run"],
|
||||
"cwd": "${workspaceFolder}/src/Umbraco.Web.UI",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Company>Umbraco HQ</Company>
|
||||
<Authors>Umbraco</Authors>
|
||||
<Copyright>Copyright © Umbraco $([System.DateTime]::Today.ToString('yyyy'))</Copyright>
|
||||
@@ -29,9 +28,8 @@
|
||||
|
||||
<!-- Package Validation -->
|
||||
<PropertyGroup>
|
||||
<GenerateCompatibilitySuppressionFile>false</GenerateCompatibilitySuppressionFile>
|
||||
<EnablePackageValidation>true</EnablePackageValidation>
|
||||
<PackageValidationBaselineVersion>11.0.0-rc1</PackageValidationBaselineVersion>
|
||||
<PackageValidationBaselineVersion>10.0.0</PackageValidationBaselineVersion>
|
||||
<EnableStrictModeForCompatibleFrameworksInPackage>true</EnableStrictModeForCompatibleFrameworksInPackage>
|
||||
<EnableStrictModeForCompatibleTfms>true</EnableStrictModeForCompatibleTfms>
|
||||
</PropertyGroup>
|
||||
@@ -41,7 +39,7 @@
|
||||
<PackageReference Include="Nerdbank.GitVersioning" Version="3.5.113" PrivateAssets="all" IsImplicitlyDefined="true" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.406" PrivateAssets="all" IsImplicitlyDefined="true" />
|
||||
<PackageReference Include="Umbraco.Code" Version="2.0.0" PrivateAssets="all" IsImplicitlyDefined="true" />
|
||||
<PackageReference Include="Umbraco.GitVersioning.Extensions" Version="0.2.0" PrivateAssets="all" IsImplicitlyDefined="true" />
|
||||
<PackageReference Include="Umbraco.GitVersioning.Extensions" Version="0.1.1" PrivateAssets="all" IsImplicitlyDefined="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -51,4 +49,5 @@
|
||||
<PropertyGroup>
|
||||
<GitVersionBaseDirectory>$(MSBuildThisFileDirectory)</GitVersionBaseDirectory>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
+102
-57
@@ -21,11 +21,35 @@ parameters:
|
||||
displayName: Upload API docs
|
||||
type: boolean
|
||||
default: false
|
||||
- name: forceReleaseTestFilter
|
||||
displayName: Force to use the release test filters
|
||||
type: boolean
|
||||
default: false
|
||||
- name: integrationNonReleaseTestFilter
|
||||
displayName: TestFilter used for non-release type builds
|
||||
type: string
|
||||
default: '--filter TestCategory!=LongRunning&TestCategory!=NonCritical'
|
||||
- name: integrationReleaseTestFilter
|
||||
displayName: TestFilter used for release type builds
|
||||
type: string
|
||||
default: ' '
|
||||
- name: nonWindowsIntegrationNonReleaseTestFilter
|
||||
displayName: TestFilter used for non-release type builds on non windows agents
|
||||
type: string
|
||||
default: '--filter TestCategory!=LongRunning&TestCategory!=NonCritical'
|
||||
- name: nonWindowsIntegrationReleaseTestFilter
|
||||
displayName: TestFilter used for release type builds on non windows agents
|
||||
type: string
|
||||
default: ' '
|
||||
- name: isNightly
|
||||
displayName: 'Is nightly build (used for MyGet feed)'
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
variables:
|
||||
nodeVersion: 16.17.0
|
||||
dotnetVersion: 7.x
|
||||
dotnetIncludePreviewVersions: true
|
||||
nodeVersion: 20
|
||||
dotnetVersion: 6.x
|
||||
dotnetIncludePreviewVersions: false
|
||||
solution: umbraco.sln
|
||||
buildConfiguration: Release
|
||||
UMBRACO__CMS__GLOBAL__ID: 00000000-0000-0000-0000-000000000042
|
||||
@@ -49,6 +73,7 @@ stages:
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
displayName: Use Node.js $(nodeVersion)
|
||||
retryCountOnTaskFailure: 3
|
||||
inputs:
|
||||
versionSpec: $(nodeVersion)
|
||||
- task: Cache@2
|
||||
@@ -61,9 +86,9 @@ stages:
|
||||
path: $(npm_config_cache)
|
||||
- script: npm ci --no-fund --no-audit --prefer-offline
|
||||
workingDirectory: src/Umbraco.Web.UI.Client
|
||||
displayName: Run npm ci
|
||||
displayName: Run npm ci (Backoffice)
|
||||
- task: gulp@0
|
||||
displayName: Run gulp build
|
||||
displayName: Run gulp build (Backoffice)
|
||||
inputs:
|
||||
gulpFile: src/Umbraco.Web.UI.Client/gulpfile.js
|
||||
targets: coreBuild
|
||||
@@ -72,6 +97,7 @@ stages:
|
||||
displayName: Use .NET $(dotnetVersion)
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
performMultiLevelLookup: true
|
||||
includePreviewVersions: $(dotnetIncludePreviewVersions)
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Run dotnet restore
|
||||
@@ -79,44 +105,12 @@ stages:
|
||||
command: restore
|
||||
projects: $(solution)
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Run dotnet build
|
||||
name: build
|
||||
displayName: Run dotnet build and generate NuGet packages
|
||||
inputs:
|
||||
command: build
|
||||
projects: $(solution)
|
||||
arguments: '--configuration $(buildConfiguration) --no-restore -p:ContinuousIntegrationBuild=true'
|
||||
- script: |
|
||||
version="$(Build.BuildNumber)"
|
||||
echo "varsion: $version"
|
||||
|
||||
major="$(echo $version | cut -d '.' -f 1)"
|
||||
echo "major version: $major"
|
||||
|
||||
echo "##vso[task.setvariable variable=majorVersion;isOutput=true]$major"
|
||||
displayName: Set major version
|
||||
name: determineMajorVersion
|
||||
- task: PowerShell@2
|
||||
displayName: Prepare nupkg
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: |
|
||||
$umbracoVersion = "$(Build.BuildNumber)" -replace "\+",".g"
|
||||
$templatePaths = Get-ChildItem 'templates/**/.template.config/template.json'
|
||||
|
||||
foreach ($templatePath in $templatePaths) {
|
||||
$a = Get-Content $templatePath -Raw | ConvertFrom-Json
|
||||
if ($a.symbols -and $a.symbols.UmbracoVersion) {
|
||||
$a.symbols.UmbracoVersion.defaultValue = $umbracoVersion
|
||||
$a | ConvertTo-Json -Depth 32 | Set-Content $templatePath
|
||||
}
|
||||
}
|
||||
|
||||
dotnet pack $(solution) --configuration $(buildConfiguration) --no-build --property:PackageOutputPath=$(Build.ArtifactStagingDirectory)/nupkg
|
||||
- script: |
|
||||
sha="$(Build.SourceVersion)"
|
||||
sha=${sha:0:7}
|
||||
buildnumber="$(Build.BuildNumber)_$(Build.BuildId)_$sha"
|
||||
echo "##vso[build.updatebuildnumber]$buildnumber"
|
||||
displayName: Update build number
|
||||
arguments: '--configuration $(buildConfiguration) --no-restore --property:ContinuousIntegrationBuild=true --property:GeneratePackageOnBuild=true --property:PackageOutputPath=$(Build.ArtifactStagingDirectory)/nupkg'
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: Publish nupkg
|
||||
inputs:
|
||||
@@ -129,11 +123,11 @@ stages:
|
||||
artifactName: build_output
|
||||
|
||||
- stage: Build_Docs
|
||||
condition: and(succeeded(), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ${{parameters.buildApiDocs}}))
|
||||
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.buildApiDocs}}))
|
||||
displayName: Prepare API Documentation
|
||||
dependsOn: Build
|
||||
variables:
|
||||
umbracoMajorVersion: $[ stageDependencies.Build.A.outputs['determineMajorVersion.majorVersion'] ]
|
||||
umbracoMajorVersion: $[ stageDependencies.Build.A.outputs['build.NBGV_VersionMajor'] ]
|
||||
jobs:
|
||||
# C# API Reference
|
||||
- job:
|
||||
@@ -187,9 +181,10 @@ stages:
|
||||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
displayName: Use Node.js 10.15.0
|
||||
displayName: Use Node.js 10.15.x
|
||||
retryCountOnTaskFailure: 3
|
||||
inputs:
|
||||
versionSpec: 10.15.0 # Won't work with higher versions
|
||||
versionSpec: 10.15.x # Won't work with higher versions
|
||||
- script: |
|
||||
npm ci --no-fund --no-audit --prefer-offline
|
||||
npx gulp docs
|
||||
@@ -245,6 +240,7 @@ stages:
|
||||
displayName: Use .NET $(dotnetVersion)
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
performMultiLevelLookup: true
|
||||
includePreviewVersions: $(dotnetIncludePreviewVersions)
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Run dotnet test
|
||||
@@ -257,6 +253,8 @@ stages:
|
||||
- stage: Integration
|
||||
displayName: Integration Tests
|
||||
dependsOn: Build
|
||||
variables:
|
||||
releaseTestFilter: eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True')
|
||||
jobs:
|
||||
# Integration Tests (SQLite)
|
||||
- job:
|
||||
@@ -281,14 +279,33 @@ stages:
|
||||
displayName: Use .NET $(dotnetVersion)
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
performMultiLevelLookup: true
|
||||
includePreviewVersions: $(dotnetIncludePreviewVersions)
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Run dotnet test
|
||||
displayName: Run dotnet test Windows
|
||||
condition: eq(variables['Agent.OS'],'Windows_NT')
|
||||
inputs:
|
||||
command: test
|
||||
projects: '**/*.Tests.Integration.csproj'
|
||||
arguments: '--configuration $(buildConfiguration) --no-build'
|
||||
testRunTitle: Integration Tests SQLite - $(Agent.OS)
|
||||
${{ if or(variables.releaseTestFilter, parameters.forceReleaseTestFilter) }}:
|
||||
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.integrationReleaseTestFilter}}'
|
||||
${{ else }}:
|
||||
arguments: '--configuration $(buildConfiguration) ${{parameters.integrationNonReleaseTestFilter}}'
|
||||
env:
|
||||
Tests__Database__DatabaseType: 'Sqlite'
|
||||
Umbraco__CMS__Global__MainDomLock: 'FileSystemMainDomLock'
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Run dotnet test Non Windows
|
||||
condition: ne(variables['Agent.OS'],'Windows_NT')
|
||||
inputs:
|
||||
command: test
|
||||
projects: '**/*.Tests.Integration.csproj'
|
||||
testRunTitle: Integration Tests SQLite - $(Agent.OS)
|
||||
${{ if or(variables.releaseTestFilter, parameters.forceReleaseTestFilter) }}:
|
||||
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.nonWindowsIntegrationReleaseTestFilter}}'
|
||||
${{ else }}:
|
||||
arguments: '--configuration $(buildConfiguration) ${{parameters.nonWindowsIntegrationNonReleaseTestFilter}}'
|
||||
env:
|
||||
Tests__Database__DatabaseType: 'Sqlite'
|
||||
Umbraco__CMS__Global__MainDomLock: 'FileSystemMainDomLock'
|
||||
@@ -296,7 +313,7 @@ stages:
|
||||
# Integration Tests (SQL Server)
|
||||
- job:
|
||||
timeoutInMinutes: 120
|
||||
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ${{parameters.sqlServerIntegrationTests}})
|
||||
condition: or(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), ${{parameters.sqlServerIntegrationTests}})
|
||||
displayName: Integration Tests (SQL Server)
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -307,7 +324,7 @@ stages:
|
||||
Linux:
|
||||
vmImage: 'ubuntu-latest'
|
||||
testDb: SqlServer
|
||||
connectionString: 'Server=localhost,1433;User Id=sa;Password=$(SA_PASSWORD);'
|
||||
connectionString: 'Server=localhost,1433;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=true'
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
variables:
|
||||
@@ -330,12 +347,31 @@ stages:
|
||||
displayName: Start SQL Server (Linux only)
|
||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Run dotnet test
|
||||
displayName: Run dotnet test Windows
|
||||
condition: eq(variables['Agent.OS'],'Windows_NT')
|
||||
inputs:
|
||||
command: test
|
||||
projects: '**/*.Tests.Integration.csproj'
|
||||
arguments: '--configuration $(buildConfiguration) --no-build'
|
||||
testRunTitle: Integration Tests SQL Server - $(Agent.OS)
|
||||
${{ if or(variables.releaseTestFilter, parameters.forceReleaseTestFilter) }}:
|
||||
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.integrationReleaseTestFilter}}'
|
||||
${{ else }}:
|
||||
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.integrationNonReleaseTestFilter}}'
|
||||
env:
|
||||
Tests__Database__DatabaseType: $(testDb)
|
||||
Tests__Database__SQLServerMasterConnectionString: $(connectionString)
|
||||
Umbraco__CMS__Global__MainDomLock: 'SqlMainDomLock'
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Run dotnet test NonWindows
|
||||
condition: ne(variables['Agent.OS'],'Windows_NT')
|
||||
inputs:
|
||||
command: test
|
||||
projects: '**/*.Tests.Integration.csproj'
|
||||
testRunTitle: Integration Tests SQL Server - $(Agent.OS)
|
||||
${{ if or(variables.releaseTestFilter, parameters.forceReleaseTestFilter) }}:
|
||||
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.nonWindowsIntegrationReleaseTestFilter}}'
|
||||
${{ else }}:
|
||||
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.nonWindowsIntegrationNonReleaseTestFilter}}'
|
||||
env:
|
||||
Tests__Database__DatabaseType: $(testDb)
|
||||
Tests__Database__SQLServerMasterConnectionString: $(connectionString)
|
||||
@@ -371,7 +407,7 @@ stages:
|
||||
Umbraco__CMS__Unattended__InstallUnattended: true
|
||||
Umbraco__CMS__Global__InstallMissingDatabase: true
|
||||
UmbracoDatabaseServer: (LocalDB)\MSSQLLocalDB
|
||||
UmbracoDatabaseName: Playwright
|
||||
UmbracoDatabaseName: AcceptanceTestDB
|
||||
ConnectionStrings__umbracoDbDSN: Server=$(UmbracoDatabaseServer);Database=$(UmbracoDatabaseName);Integrated Security=true;
|
||||
# Custom Umbraco settings
|
||||
Umbraco__CMS__Global__VersionCheckPeriod: 0
|
||||
@@ -388,6 +424,7 @@ stages:
|
||||
path: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/misc/nupkg
|
||||
- task: NodeTool@0
|
||||
displayName: Use Node.js $(nodeVersion)
|
||||
retryCountOnTaskFailure: 3
|
||||
inputs:
|
||||
versionSpec: $(nodeVersion)
|
||||
- task: Cache@2
|
||||
@@ -417,6 +454,7 @@ stages:
|
||||
displayName: Use .NET $(dotnetVersion)
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
performMultiLevelLookup: true
|
||||
includePreviewVersions: $(dotnetIncludePreviewVersions)
|
||||
- pwsh: |
|
||||
$sha = 'g$(Build.SourceVersion)'.substring(0, 8)
|
||||
@@ -431,7 +469,7 @@ stages:
|
||||
workingDirectory: tests/Umbraco.Tests.AcceptanceTest/misc
|
||||
- pwsh: |
|
||||
dotnet new --install ./nupkg/Umbraco.Templates.*.nupkg
|
||||
dotnet new umbraco --name Playwright --no-restore --output .
|
||||
dotnet new umbraco --name AcceptanceTestProject --no-restore --output .
|
||||
dotnet restore --configfile ./nuget.config
|
||||
dotnet build --configuration $(buildConfiguration) --no-restore
|
||||
dotnet dev-certs https
|
||||
@@ -492,9 +530,11 @@ stages:
|
||||
- Unit
|
||||
- Integration
|
||||
# - E2E # TODO: Enable when stable.
|
||||
condition: and(succeeded(), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ${{parameters.myGetDeploy}}))
|
||||
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.myGetDeploy}}))
|
||||
jobs:
|
||||
- job:
|
||||
pool:
|
||||
vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available.
|
||||
displayName: Push to pre-release feed
|
||||
steps:
|
||||
- checkout: none
|
||||
@@ -509,15 +549,20 @@ stages:
|
||||
command: 'push'
|
||||
packagesToPush: $(Build.ArtifactStagingDirectory)/**/*.nupkg
|
||||
nuGetFeedType: 'external'
|
||||
publishFeedCredentials: 'MyGet - Pre-releases'
|
||||
${{ if eq(parameters.isNightly, true) }}:
|
||||
publishFeedCredentials: 'MyGet - Umbraco Nightly'
|
||||
${{ else }}:
|
||||
publishFeedCredentials: 'MyGet - Pre-releases'
|
||||
- stage: Deploy_NuGet
|
||||
displayName: NuGet release
|
||||
dependsOn:
|
||||
- Deploy_MyGet
|
||||
- Build_Docs
|
||||
condition: and(succeeded(), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ${{parameters.nuGetDeploy}}))
|
||||
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.nuGetDeploy}}))
|
||||
jobs:
|
||||
- job:
|
||||
pool:
|
||||
vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available.
|
||||
displayName: Push to NuGet
|
||||
steps:
|
||||
- checkout: none
|
||||
@@ -538,12 +583,12 @@ stages:
|
||||
pool:
|
||||
vmImage: 'windows-latest' # Apparently AzureFileCopy is windows only :(
|
||||
variables:
|
||||
umbracoMajorVersion: $[ stageDependencies.Build.A.outputs['determineMajorVersion.majorVersion'] ]
|
||||
umbracoMajorVersion: $[ stageDependencies.Build.A.outputs['build.NBGV_VersionMajor'] ]
|
||||
displayName: Upload API Documention
|
||||
dependsOn:
|
||||
- Build
|
||||
- Deploy_NuGet
|
||||
condition: and(succeeded(), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ${{parameters.uploadApiDocs}}))
|
||||
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.uploadApiDocs}}))
|
||||
jobs:
|
||||
- job:
|
||||
displayName: Upload C# Docs
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Nightly_$(TeamProject)_$(Build.DefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r)
|
||||
|
||||
pr: none
|
||||
trigger: none
|
||||
|
||||
schedules:
|
||||
- cron: '0 0 * * *'
|
||||
displayName: Daily midnight build
|
||||
branches:
|
||||
include:
|
||||
- v10/dev
|
||||
- v12/dev
|
||||
- v13/dev
|
||||
- v14/dev
|
||||
|
||||
steps:
|
||||
- checkout: none
|
||||
- task: TriggerBuild@4
|
||||
inputs:
|
||||
definitionIsInCurrentTeamProject: true
|
||||
buildDefinition: '301'
|
||||
queueBuildForUserThatTriggeredBuild: true
|
||||
ignoreSslCertificateErrors: false
|
||||
useSameSourceVersion: false
|
||||
useCustomSourceVersion: false
|
||||
useSameBranch: true
|
||||
waitForQueuedBuildsToFinish: false
|
||||
storeInEnvironmentVariable: false
|
||||
templateParameters: 'sqlServerIntegrationTests: true, forceReleaseTestFilter: true, myGetDeploy: true, isNightly: true'
|
||||
authenticationMethod: 'OAuth Token'
|
||||
enableBuildInQueueCondition: false
|
||||
dependentOnSuccessfulBuildCondition: false
|
||||
dependentOnFailedBuildCondition: false
|
||||
checkbuildsoncurrentbranch: false
|
||||
failTaskIfConditionsAreNotFulfilled: false
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "6.0.300",
|
||||
"rollForward": "latestFeature"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
name: issue-first-response
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
send-response:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
npm install node-fetch@2
|
||||
- name: Fetch random comment 🗣️ and add it to the issue
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const fetch = require('node-fetch')
|
||||
|
||||
const response = await fetch('https://collaboratorsv2.euwest01.umbraco.io/umbraco/api/comments/PostComment', {
|
||||
method: 'post',
|
||||
body: JSON.stringify({
|
||||
repo: '${{ github.repository }}',
|
||||
number: '${{ github.event.number }}',
|
||||
actor: '${{ github.actor }}',
|
||||
commentType: 'opened-issue-first-comment'
|
||||
}),
|
||||
headers: {
|
||||
'Authorization': 'Bearer ${{ secrets.OUR_BOT_API_TOKEN }}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
const data = await response.text();
|
||||
|
||||
if(response.status === 200 && data !== '') {
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: data
|
||||
});
|
||||
} else {
|
||||
console.log("Status code did not indicate success:", response.status);
|
||||
console.log("Returned data:", data);
|
||||
}
|
||||
} catch(error) {
|
||||
console.log(error);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
<NoWarn>NU1507</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Dazinator.Extensions.FileProviders" Version="2.0.0" />
|
||||
<PackageVersion Include="Examine" Version="3.0.1" />
|
||||
<PackageVersion Include="Examine.Core" Version="3.0.1" />
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.11.54" />
|
||||
<PackageVersion Include="IPNetwork2" Version="2.6.618" />
|
||||
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.6" />
|
||||
<PackageVersion Include="MailKit" Version="3.2.0" />
|
||||
<PackageVersion Include="Markdown" Version="2.2.1" />
|
||||
<PackageVersion Include="MessagePack" Version="2.5.187" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.24" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.24" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="6.0.24" />
|
||||
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.24" />
|
||||
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="6.0.24" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="6.0.24" />
|
||||
<PackageVersion Include="MiniProfiler.AspNetCore.Mvc" Version="4.2.22" />
|
||||
<PackageVersion Include="MiniProfiler.Shared" Version="4.2.22" />
|
||||
<PackageVersion Include="ncrontab" Version="3.3.3" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageVersion Include="NPoco.SqlServer" Version="5.3.2" />
|
||||
<PackageVersion Include="Serilog" Version="2.12.0" />
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="5.0.0" />
|
||||
<PackageVersion Include="Serilog.Enrichers.Process" Version="2.0.2" />
|
||||
<PackageVersion Include="Serilog.Enrichers.Thread" Version="3.1.0" />
|
||||
<PackageVersion Include="Serilog.Expressions" Version="3.4.1" />
|
||||
<PackageVersion Include="Serilog.Extensions.Hosting" Version="4.2.0" />
|
||||
<PackageVersion Include="Serilog.Formatting.Compact" Version="1.1.0" />
|
||||
<PackageVersion Include="Serilog.Formatting.Compact.Reader" Version="1.0.5" />
|
||||
<PackageVersion Include="Serilog.Settings.Configuration" Version="3.4.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Async" Version="1.5.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Map" Version="1.0.2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="2.1.10" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="2.0.2" />
|
||||
<PackageVersion Include="Smidge.InMemory" Version="4.3.0" />
|
||||
<PackageVersion Include="Smidge.Nuglify" Version="4.2.1" />
|
||||
<PackageVersion Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
||||
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="6.0.4" />
|
||||
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="6.0.0" />
|
||||
<PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
<PackageVersion Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="6.0.0" />
|
||||
<PackageVersion Include="Umbraco.CSharpTest.Net.Collections" Version="14.906.1403.1085" />
|
||||
<!-- Add dependencies that we force an update to, even that we do not use them explicitly and they seems to be taken from the framework instead of from Nuget -->
|
||||
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageVersion Include="System.Security.Cryptography.Xml" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,152 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Deploy.Core.Configuration.DebugConfiguration;
|
||||
using Umbraco.Deploy.Core.Configuration.DeployConfiguration;
|
||||
using Umbraco.Deploy.Core.Configuration.DeployProjectConfiguration;
|
||||
using Umbraco.Forms.Core.Configuration;
|
||||
using SecuritySettings = Umbraco.Cms.Core.Configuration.Models.SecuritySettings;
|
||||
|
||||
namespace JsonSchema
|
||||
{
|
||||
internal class AppSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Umbraco
|
||||
/// </summary>
|
||||
public UmbracoDefinition? Umbraco { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configuration of Umbraco CMS and packages
|
||||
/// </summary>
|
||||
internal class UmbracoDefinition
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public CmsDefinition? CMS { get; set; }
|
||||
|
||||
public FormsDefinition? Forms { get; set; }
|
||||
|
||||
public DeployDefinition? Deploy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configurations for the Umbraco CMS
|
||||
/// </summary>
|
||||
public class CmsDefinition
|
||||
{
|
||||
public ContentSettings? Content { get; set; }
|
||||
public CoreDebugSettings? Debug { get; set; }
|
||||
|
||||
public ExceptionFilterSettings? ExceptionFilter { get; set; }
|
||||
|
||||
public ModelsBuilderSettings? ModelsBuilder { get; set; }
|
||||
|
||||
public GlobalSettings? Global { get; set; }
|
||||
|
||||
public HealthChecksSettings? HealthChecks { get; set; }
|
||||
|
||||
public HostingSettings? Hosting { get; set; }
|
||||
|
||||
public ImagingSettings? Imaging { get; set; }
|
||||
|
||||
public IndexCreatorSettings? Examine { get; set; }
|
||||
public IndexingSettings? Indexing { get; set; }
|
||||
|
||||
public KeepAliveSettings? KeepAlive { get; set; }
|
||||
|
||||
public LoggingSettings? Logging { get; set; }
|
||||
|
||||
public NuCacheSettings? NuCache { get; set; }
|
||||
|
||||
public RequestHandlerSettings? RequestHandler { get; set; }
|
||||
|
||||
public RuntimeSettings? Runtime { get; set; }
|
||||
|
||||
public SecuritySettings? Security { get; set; }
|
||||
|
||||
public TourSettings? Tours { get; set; }
|
||||
|
||||
public TypeFinderSettings? TypeFinder { get; set; }
|
||||
|
||||
public WebRoutingSettings? WebRouting { get; set; }
|
||||
|
||||
public UmbracoPluginSettings? Plugins { get; set; }
|
||||
|
||||
public UnattendedSettings? Unattended { get; set; }
|
||||
|
||||
public RichTextEditorSettings? RichTextEditor { get; set; }
|
||||
|
||||
public RuntimeMinificationSettings? RuntimeMinification { get; set; }
|
||||
|
||||
public BasicAuthSettings? BasicAuth { get; set; }
|
||||
|
||||
public PackageMigrationSettings? PackageMigration { get; set; }
|
||||
|
||||
public LegacyPasswordMigrationSettings? LegacyPasswordMigration { get; set; }
|
||||
|
||||
public ContentDashboardSettings? ContentDashboard { get; set; }
|
||||
|
||||
public HelpPageSettings? HelpPage { get; set; }
|
||||
|
||||
public InstallDefaultData? InstallDefaultData { get; set; }
|
||||
|
||||
public DataTypesSettings? DataTypes { get; set; }
|
||||
|
||||
public MarketplaceSettings? Marketplace { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configurations for the Umbraco CMS InstallDefaultData configuration.
|
||||
/// </summary>
|
||||
public class InstallDefaultData
|
||||
{
|
||||
public InstallDefaultDataSettings? Languages { get; set; }
|
||||
|
||||
public InstallDefaultDataSettings? DataTypes { get; set; }
|
||||
|
||||
public InstallDefaultDataSettings? MediaTypes { get; set; }
|
||||
|
||||
public InstallDefaultDataSettings? MemberTypes { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configurations for the Umbraco Forms package to Umbraco CMS
|
||||
/// </summary>
|
||||
public class FormsDefinition
|
||||
{
|
||||
public FormDesignSettings? FormDesign { get; set; }
|
||||
|
||||
public PackageOptionSettings? Options { get; set; }
|
||||
|
||||
public Umbraco.Forms.Core.Configuration.SecuritySettings? Security { get; set; }
|
||||
|
||||
public FieldTypesDefinition? FieldTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configurations for the Umbraco Forms Field Types
|
||||
/// </summary>
|
||||
public class FieldTypesDefinition
|
||||
{
|
||||
public DatePickerSettings? DatePicker { get; set; }
|
||||
|
||||
public Recaptcha2Settings? Recaptcha2 { get; set; }
|
||||
|
||||
public Recaptcha3Settings? Recaptcha3 { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configurations for the Umbraco Deploy package to Umbraco CMS
|
||||
/// </summary>
|
||||
public class DeployDefinition
|
||||
{
|
||||
public DeploySettings? Settings { get; set; }
|
||||
|
||||
public DeployProjectConfig? Project { get; set; }
|
||||
|
||||
public DebugSettings? Debug { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<IsPackable>false</IsPackable>
|
||||
<EnablePackageValidation>false</EnablePackageValidation>
|
||||
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageReference Include="NJsonSchema" Version="10.9.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj" />
|
||||
<PackageReference Include="Umbraco.Deploy.Core" Version="10.4.0" />
|
||||
<PackageReference Include="Umbraco.Forms.Core" Version="10.5.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using NJsonSchema.Generation;
|
||||
|
||||
namespace JsonSchema
|
||||
{
|
||||
internal class NamespacePrefixedSchemaNameGenerator : DefaultSchemaNameGenerator
|
||||
{
|
||||
public override string Generate(Type type) => type.Namespace?.Replace(".", string.Empty) + base.Generate(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using CommandLine;
|
||||
|
||||
namespace JsonSchema
|
||||
{
|
||||
internal class Options
|
||||
{
|
||||
[Option('o', "outputFile", Required = false, HelpText = "Set path of the output file.", Default = "appsettings-schema.json")]
|
||||
public string OutputFile { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using CommandLine;
|
||||
|
||||
namespace JsonSchema
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Parser.Default.ParseArguments<Options>(args)
|
||||
.WithParsedAsync(Execute);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task Execute(Options options)
|
||||
{
|
||||
var generator = new UmbracoJsonSchemaGenerator();
|
||||
var schema = await generator.Generate();
|
||||
|
||||
var path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, options.OutputFile));
|
||||
Console.WriteLine("Path to use {0}", path);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
||||
Console.WriteLine("Ensured directory exists");
|
||||
await File.WriteAllTextAsync(path, schema);
|
||||
|
||||
Console.WriteLine("File written at {0}", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NJsonSchema.Generation;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
|
||||
namespace JsonSchema
|
||||
{
|
||||
/// <summary>
|
||||
/// Generator of the JsonSchema for AppSettings.json including A specific Umbraco version.
|
||||
/// </summary>
|
||||
public class UmbracoJsonSchemaGenerator
|
||||
{
|
||||
private static readonly HttpClient s_client = new ();
|
||||
private readonly JsonSchemaGenerator _innerGenerator;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UmbracoJsonSchemaGenerator" /> class.
|
||||
/// </summary>
|
||||
public UmbracoJsonSchemaGenerator()
|
||||
=> _innerGenerator = new JsonSchemaGenerator(new UmbracoJsonSchemaGeneratorSettings());
|
||||
|
||||
/// <summary>
|
||||
/// Generates a json representing the JsonSchema for AppSettings.json including A specific Umbraco version..
|
||||
/// </summary>
|
||||
public async Task<string> Generate()
|
||||
{
|
||||
JObject umbracoSchema = GenerateUmbracoSchema();
|
||||
JObject officialSchema = await GetOfficialAppSettingsSchema();
|
||||
|
||||
officialSchema.Merge(umbracoSchema);
|
||||
|
||||
return officialSchema.ToString();
|
||||
}
|
||||
|
||||
private async Task<JObject> GetOfficialAppSettingsSchema()
|
||||
{
|
||||
HttpResponseMessage response = await s_client.GetAsync("https://json.schemastore.org/appsettings.json")
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
|
||||
return JsonConvert.DeserializeObject<JObject>(result)!;
|
||||
}
|
||||
|
||||
private JObject GenerateUmbracoSchema()
|
||||
{
|
||||
NJsonSchema.JsonSchema schema = _innerGenerator.Generate(typeof(AppSettings));
|
||||
|
||||
// TODO: when the "UmbracoPath" setter is removed from "GlobalSettings" (scheduled for V12), remove this line as well
|
||||
schema.Definitions["UmbracoCmsCoreConfigurationModelsGlobalSettings"]?.Properties?.Remove(nameof(GlobalSettings.UmbracoPath));
|
||||
|
||||
return JsonConvert.DeserializeObject<JObject>(schema.ToJson())!;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using NJsonSchema.Generation;
|
||||
|
||||
namespace JsonSchema
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class UmbracoJsonSchemaGeneratorSettings : JsonSchemaGeneratorSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UmbracoJsonSchemaGeneratorSettings"/> class.
|
||||
/// </summary>
|
||||
public UmbracoJsonSchemaGeneratorSettings()
|
||||
{
|
||||
AlwaysAllowAdditionalObjectProperties = true;
|
||||
SerializerSettings = new JsonSerializerSettings()
|
||||
{
|
||||
ContractResolver = new WritablePropertiesOnlyResolver()
|
||||
};
|
||||
DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull;
|
||||
SchemaNameGenerator = new NamespacePrefixedSchemaNameGenerator();
|
||||
SerializerSettings.Converters.Add(new StringEnumConverter());
|
||||
IgnoreObsoleteProperties = true;
|
||||
GenerateExamples = true;
|
||||
}
|
||||
|
||||
private class WritablePropertiesOnlyResolver : DefaultContractResolver
|
||||
{
|
||||
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||
{
|
||||
IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
|
||||
return props.Where(p => p.Writable).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Imaging.ImageSharp;
|
||||
|
||||
/// <summary>
|
||||
/// Adds imaging support using ImageSharp/ImageSharp.Web.
|
||||
/// </summary>
|
||||
/// <seealso cref="Umbraco.Cms.Core.Composing.IComposer" />
|
||||
public sealed class ImageSharpComposer : IComposer
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void Compose(IUmbracoBuilder builder)
|
||||
=> builder.AddUmbracoImageSharp();
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Title>Umbraco CMS - Imaging - ImageSharp</Title>
|
||||
<Description>Adds imaging support using ImageSharp/ImageSharp.Web to Umbraco CMS.</Description>
|
||||
<!-- TODO: Enable when final version is shipped (because there's currently no previous version) -->
|
||||
<EnablePackageValidation>false</EnablePackageValidation>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Web" Version="2.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Umbraco.Web.Common\Umbraco.Web.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Umbraco.Tests.UnitTests</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,54 +0,0 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Web.Caching;
|
||||
using SixLabors.ImageSharp.Web.DependencyInjection;
|
||||
using SixLabors.ImageSharp.Web.Middleware;
|
||||
using SixLabors.ImageSharp.Web.Providers;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Media;
|
||||
using Umbraco.Cms.Imaging.ImageSharp.ImageProcessors;
|
||||
using Umbraco.Cms.Imaging.ImageSharp.Media;
|
||||
using Umbraco.Cms.Web.Common.ApplicationBuilder;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Imaging.ImageSharp;
|
||||
|
||||
public static class UmbracoBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds Image Sharp with Umbraco settings
|
||||
/// </summary>
|
||||
public static IServiceCollection AddUmbracoImageSharp(this IUmbracoBuilder builder)
|
||||
{
|
||||
// Add default ImageSharp configuration and service implementations
|
||||
builder.Services.AddSingleton(Configuration.Default);
|
||||
builder.Services.AddUnique<IImageDimensionExtractor, ImageSharpDimensionExtractor>();
|
||||
|
||||
builder.Services.AddSingleton<IImageUrlGenerator, ImageSharpImageUrlGenerator>();
|
||||
|
||||
builder.Services.AddImageSharp()
|
||||
// Replace default image provider
|
||||
.ClearProviders()
|
||||
.AddProvider<WebRootImageProvider>()
|
||||
// Add custom processors
|
||||
.AddProcessor<CropWebProcessor>();
|
||||
|
||||
// Configure middleware
|
||||
builder.Services.AddTransient<IConfigureOptions<ImageSharpMiddlewareOptions>, ConfigureImageSharpMiddlewareOptions>();
|
||||
|
||||
// Configure cache options
|
||||
builder.Services.AddTransient<IConfigureOptions<PhysicalFileSystemCacheOptions>, ConfigurePhysicalFileSystemCacheOptions>();
|
||||
|
||||
// Important we handle image manipulations before the static files, otherwise the querystring is just ignored
|
||||
builder.Services.Configure<UmbracoPipelineOptions>(options =>
|
||||
{
|
||||
options.AddFilter(new UmbracoPipelineFilter(nameof(ImageSharpComposer))
|
||||
{
|
||||
PrePipeline = prePipeline => prePipeline.UseImageSharp()
|
||||
});
|
||||
});
|
||||
|
||||
return builder.Services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Analytics;
|
||||
|
||||
public class AllAnalyticsController : AnalyticsControllerBase
|
||||
{
|
||||
[HttpGet("all")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<TelemetryLevel>), StatusCodes.Status200OK)]
|
||||
public async Task<PagedViewModel<TelemetryLevel>> GetAll(int skip, int take)
|
||||
{
|
||||
TelemetryLevel[] levels = Enum.GetValues<TelemetryLevel>();
|
||||
return await Task.FromResult(new PagedViewModel<TelemetryLevel>
|
||||
{
|
||||
Total = levels.Length,
|
||||
Items = levels.Skip(skip).Take(take),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Analytics;
|
||||
|
||||
[ApiController]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/analytics")]
|
||||
[OpenApiTag("Analytics")]
|
||||
[ApiVersion("1.0")]
|
||||
public abstract class AnalyticsControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Analytics;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Analytics;
|
||||
|
||||
public class GetAnalyticsController : AnalyticsControllerBase
|
||||
{
|
||||
private readonly IMetricsConsentService _metricsConsentService;
|
||||
|
||||
public GetAnalyticsController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService;
|
||||
|
||||
[HttpGet]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(AnalyticsLevelViewModel), StatusCodes.Status200OK)]
|
||||
public async Task<AnalyticsLevelViewModel> Get() => await Task.FromResult(new AnalyticsLevelViewModel { AnalyticsLevel = _metricsConsentService.GetConsentLevel() });
|
||||
}
|
||||
+11
-10
@@ -1,35 +1,36 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Telemetry;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Analytics;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Server;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Analytics;
|
||||
|
||||
public class SetTelemetryController : TelemetryControllerBase
|
||||
public class SetAnalyticsController : AnalyticsControllerBase
|
||||
{
|
||||
private readonly IMetricsConsentService _metricsConsentService;
|
||||
|
||||
public SetTelemetryController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService;
|
||||
public SetAnalyticsController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService;
|
||||
|
||||
[HttpPost("level")]
|
||||
[HttpPost]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> SetConsentLevel(TelemetryViewModel telemetryViewModel)
|
||||
public async Task<IActionResult> SetConsentLevel(AnalyticsLevelViewModel analyticsLevelViewModel)
|
||||
{
|
||||
if (!Enum.IsDefined(telemetryViewModel.TelemetryLevel))
|
||||
if (!Enum.IsDefined(analyticsLevelViewModel.AnalyticsLevel))
|
||||
{
|
||||
var invalidModelProblem = new ProblemDetails
|
||||
{
|
||||
Title = "Invalid TelemetryLevel value",
|
||||
Detail = "The provided value for TelemetryLevel is not valid",
|
||||
Title = "Invalid AnalyticsLevel value",
|
||||
Detail = "The provided value for AnalyticsLevel is not valid",
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Type = "Error",
|
||||
};
|
||||
return BadRequest(invalidModelProblem);
|
||||
}
|
||||
|
||||
_metricsConsentService.SetConsentLevel(telemetryViewModel.TelemetryLevel);
|
||||
_metricsConsentService.SetConsentLevel(analyticsLevelViewModel.AnalyticsLevel);
|
||||
return await Task.FromResult(Ok());
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Culture;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("culture")]
|
||||
[ApiExplorerSettings(GroupName = "Culture")]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/culture")]
|
||||
[OpenApiTag("Culture")]
|
||||
[ApiVersion("1.0")]
|
||||
public abstract class CultureControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
@@ -11,8 +12,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DataType.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DataType}")]
|
||||
[ApiExplorerSettings(GroupName = "Data Type")]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DataType}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.DataType))]
|
||||
public class DataTypeTreeControllerBase : FolderTreeControllerBase<FolderTreeItemViewModel>
|
||||
{
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ public class ItemsDataTypeTreeController : DataTypeTreeControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FolderTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FolderTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("dictionary")]
|
||||
[ApiExplorerSettings(GroupName = "Dictionary")]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/dictionary")]
|
||||
[OpenApiTag("Dictionary")]
|
||||
[ApiVersion("1.0")]
|
||||
// TODO: Add authentication
|
||||
public abstract class DictionaryControllerBase : ManagementApiControllerBase
|
||||
|
||||
@@ -23,13 +23,13 @@ public class ExportDictionaryController : DictionaryControllerBase
|
||||
[HttpGet("export/{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(NotFoundResult), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(NotFoundObjectResult), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ExportDictionary(Guid key, bool includeChildren = false)
|
||||
{
|
||||
IDictionaryItem? dictionaryItem = _localizationService.GetDictionaryItemById(key);
|
||||
if (dictionaryItem is null)
|
||||
{
|
||||
return await Task.FromResult(NotFound());
|
||||
return await Task.FromResult(NotFound("No dictionary item found with id "));
|
||||
}
|
||||
|
||||
XElement xml = _entityXmlSerializer.Serialize(dictionaryItem, includeChildren);
|
||||
|
||||
@@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Extensions;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Services;
|
||||
@@ -14,15 +13,18 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary;
|
||||
|
||||
public class ImportDictionaryController : DictionaryControllerBase
|
||||
{
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly IDictionaryService _dictionaryService;
|
||||
private readonly IWebHostEnvironment _webHostEnvironment;
|
||||
private readonly ILoadDictionaryItemService _loadDictionaryItemService;
|
||||
|
||||
public ImportDictionaryController(
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
IDictionaryService dictionaryService,
|
||||
IWebHostEnvironment webHostEnvironment,
|
||||
ILoadDictionaryItemService loadDictionaryItemService)
|
||||
{
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_dictionaryService = dictionaryService;
|
||||
_webHostEnvironment = webHostEnvironment;
|
||||
_loadDictionaryItemService = loadDictionaryItemService;
|
||||
@@ -39,7 +41,7 @@ public class ImportDictionaryController : DictionaryControllerBase
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var filePath = Path.Combine(_webHostEnvironment.MapPathContentRoot(Constants.SystemDirectories.Data), file);
|
||||
var filePath = Path.Combine(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Data), file);
|
||||
if (_webHostEnvironment.ContentRootFileProvider.GetFileInfo(filePath) is null)
|
||||
{
|
||||
return await Task.FromResult(NotFound());
|
||||
|
||||
+3
-3
@@ -6,11 +6,11 @@ using Umbraco.Cms.ManagementApi.Services.Paging;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree;
|
||||
|
||||
public class ChildrenDictionaryTreeController : DictionaryTreeControllerBase
|
||||
public class ChildrenDictionaryItemTreeController : DictionaryItemTreeControllerBase
|
||||
{
|
||||
public ChildrenDictionaryTreeController(IEntityService entityService, ILocalizationService localizationService)
|
||||
public ChildrenDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService)
|
||||
: base(entityService, localizationService)
|
||||
{
|
||||
}
|
||||
+7
-6
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
@@ -6,17 +7,17 @@ using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/dictionary")]
|
||||
[ApiExplorerSettings(GroupName = "Dictionary")]
|
||||
// NOTE: at the moment dictionary items (renamed to dictionary tree) aren't supported by EntityService, so we have little use of the
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DictionaryItem}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.DictionaryItem))]
|
||||
// NOTE: at the moment dictionary items aren't supported by EntityService, so we have little use of the
|
||||
// tree controller base. We'll keep it though, in the hope that we can mend EntityService.
|
||||
public class DictionaryTreeControllerBase : EntityTreeControllerBase<EntityTreeItemViewModel>
|
||||
public class DictionaryItemTreeControllerBase : EntityTreeControllerBase<EntityTreeItemViewModel>
|
||||
{
|
||||
public DictionaryTreeControllerBase(IEntityService entityService, ILocalizationService localizationService)
|
||||
public DictionaryItemTreeControllerBase(IEntityService entityService, ILocalizationService localizationService)
|
||||
: base(entityService) =>
|
||||
LocalizationService = localizationService;
|
||||
|
||||
+4
-4
@@ -4,16 +4,16 @@ using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree;
|
||||
|
||||
public class ItemsDictionaryTreeController : DictionaryTreeControllerBase
|
||||
public class ItemsDictionaryItemTreeController : DictionaryItemTreeControllerBase
|
||||
{
|
||||
public ItemsDictionaryTreeController(IEntityService entityService, ILocalizationService localizationService)
|
||||
public ItemsDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService)
|
||||
: base(entityService, localizationService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FolderTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FolderTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
+3
-3
@@ -6,11 +6,11 @@ using Umbraco.Cms.ManagementApi.Services.Paging;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree;
|
||||
|
||||
public class RootDictionaryTreeController : DictionaryTreeControllerBase
|
||||
public class RootDictionaryItemTreeController : DictionaryItemTreeControllerBase
|
||||
{
|
||||
public RootDictionaryTreeController(IEntityService entityService, ILocalizationService localizationService)
|
||||
public RootDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService)
|
||||
: base(entityService, localizationService)
|
||||
{
|
||||
}
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
@@ -13,10 +14,10 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Document.RecycleBin;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.RecycleBin}/{Constants.UdiEntityType.Document}")]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Document}/recycle-bin")]
|
||||
[RequireDocumentTreeRootAccess]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Document))]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.Document))]
|
||||
public class DocumentRecycleBinControllerBase : RecycleBinControllerBase<RecycleBinItemViewModel>
|
||||
{
|
||||
public DocumentRecycleBinControllerBase(IEntityService entityService)
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
@@ -15,8 +16,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Document.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Document}")]
|
||||
[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Document))]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Document}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.Document))]
|
||||
public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBase<DocumentTreeItemViewModel>
|
||||
{
|
||||
private readonly IPublicAccessService _publicAccessService;
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ public class ItemsDocumentTreeController : DocumentTreeControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<DocumentTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<DocumentTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys, Guid? dataTypeKey = null, string? culture = null)
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
@@ -11,8 +12,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DocumentBlueprint.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DocumentBlueprint}")]
|
||||
[ApiExplorerSettings(GroupName = "Document Blueprint")]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DocumentBlueprint}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.DocumentBlueprint))]
|
||||
public class DocumentBlueprintTreeControllerBase : EntityTreeControllerBase<DocumentBlueprintTreeItemViewModel>
|
||||
{
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ public class ItemsDocumentBlueprintTreeController : DocumentBlueprintTreeControl
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<DocumentBlueprintTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<DocumentBlueprintTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
@@ -11,8 +12,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DocumentType.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DocumentType}")]
|
||||
[ApiExplorerSettings(GroupName = "Document Type")]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DocumentType}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.DocumentType))]
|
||||
public class DocumentTypeTreeControllerBase : FolderTreeControllerBase<DocumentTypeTreeItemViewModel>
|
||||
{
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ public class ItemsDocumentTypeTreeController : DocumentTypeTreeControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<DocumentTypeTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<DocumentTypeTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ExamineManagement;
|
||||
|
||||
[ApiController]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/examineManagement")]
|
||||
[OpenApiTag("ExamineManagement")]
|
||||
public class ExamineManagementControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
+11
-11
@@ -2,21 +2,21 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.ManagementApi.Factories;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Search;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.ExamineManagement;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Indexer;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ExamineManagement;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class DetailsIndexerController : IndexerControllerBase
|
||||
public class IndexExamineManagementController : ExamineManagementControllerBase
|
||||
{
|
||||
private readonly IIndexViewModelFactory _indexViewModelFactory;
|
||||
private readonly IExamineIndexViewModelFactory _examineIndexViewModelFactory;
|
||||
private readonly IExamineManager _examineManager;
|
||||
|
||||
public DetailsIndexerController(
|
||||
IIndexViewModelFactory indexViewModelFactory,
|
||||
public IndexExamineManagementController(
|
||||
IExamineIndexViewModelFactory examineIndexViewModelFactory,
|
||||
IExamineManager examineManager)
|
||||
{
|
||||
_indexViewModelFactory = indexViewModelFactory;
|
||||
_examineIndexViewModelFactory = examineIndexViewModelFactory;
|
||||
_examineManager = examineManager;
|
||||
}
|
||||
|
||||
@@ -29,15 +29,15 @@ public class DetailsIndexerController : IndexerControllerBase
|
||||
/// This is kind of rudimentary since there's no way we can know that the index has rebuilt, we
|
||||
/// have a listener for the index op complete so we'll just check if that key is no longer there in the runtime cache
|
||||
/// </remarks>
|
||||
[HttpGet("{indexName}")]
|
||||
[HttpGet("index")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(IndexViewModel), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IndexViewModel?>> Details(string indexName)
|
||||
[ProducesResponseType(typeof(ExamineIndexViewModel), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<ExamineIndexViewModel?>> Index(string indexName)
|
||||
{
|
||||
if (_examineManager.TryGetIndex(indexName, out IIndex? index))
|
||||
{
|
||||
return await Task.FromResult(_indexViewModelFactory.Create(index!));
|
||||
return await Task.FromResult(_examineIndexViewModelFactory.Create(index!));
|
||||
}
|
||||
|
||||
var invalidModelProblem = new ProblemDetails
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
using Examine;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Infrastructure.Examine;
|
||||
using Umbraco.Cms.ManagementApi.Factories;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.ExamineManagement;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ExamineManagement;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class IndexesExamineManagementController : ExamineManagementControllerBase
|
||||
{
|
||||
private readonly IExamineManager _examineManager;
|
||||
private readonly IExamineIndexViewModelFactory _examineIndexViewModelFactory;
|
||||
|
||||
public IndexesExamineManagementController(
|
||||
IExamineManager examineManager,
|
||||
IExamineIndexViewModelFactory examineIndexViewModelFactory)
|
||||
{
|
||||
_examineManager = examineManager;
|
||||
_examineIndexViewModelFactory = examineIndexViewModelFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the details for indexers
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("indexes")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<ExamineIndexViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<PagedViewModel<ExamineIndexViewModel>> Indexes(int skip, int take)
|
||||
{
|
||||
ExamineIndexViewModel[] indexes = _examineManager.Indexes
|
||||
.Select(_examineIndexViewModelFactory.Create)
|
||||
.OrderBy(examineIndexModel => examineIndexModel.Name?.TrimEnd("Indexer")).ToArray();
|
||||
|
||||
var viewModel = new PagedViewModel<ExamineIndexViewModel> { Items = indexes.Skip(skip).Take(take), Total = indexes.Length };
|
||||
return viewModel;
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -5,17 +5,17 @@ using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Infrastructure.Examine;
|
||||
using Umbraco.New.Cms.Infrastructure.Services;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Indexer;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ExamineManagement;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class RebuildIndexerController : IndexerControllerBase
|
||||
public class RebuildExamineManagementController : ExamineManagementControllerBase
|
||||
{
|
||||
private readonly ILogger<RebuildIndexerController> _logger;
|
||||
private readonly ILogger<RebuildExamineManagementController> _logger;
|
||||
private readonly IIndexingRebuilderService _indexingRebuilderService;
|
||||
private readonly IExamineManager _examineManager;
|
||||
|
||||
public RebuildIndexerController(
|
||||
ILogger<RebuildIndexerController> logger,
|
||||
public RebuildExamineManagementController(
|
||||
ILogger<RebuildExamineManagementController> logger,
|
||||
IIndexingRebuilderService indexingRebuilderService,
|
||||
IExamineManager examineManager)
|
||||
{
|
||||
@@ -29,7 +29,7 @@ public class RebuildIndexerController : IndexerControllerBase
|
||||
/// </summary>
|
||||
/// <param name="indexName"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{indexName}/rebuild")]
|
||||
[HttpPost("rebuild")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(OkResult), StatusCodes.Status200OK)]
|
||||
+10
-10
@@ -4,28 +4,28 @@ using Lucene.Net.QueryParsers.Classic;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.ManagementApi.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.ExamineManagement;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Search;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Searcher;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ExamineManagement;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class QuerySearcherController : SearcherControllerBase
|
||||
public class SearchExamineManagementController : ExamineManagementControllerBase
|
||||
{
|
||||
private readonly IExamineManagerService _examineManagerService;
|
||||
|
||||
public QuerySearcherController(IExamineManagerService examineManagerService) => _examineManagerService = examineManagerService;
|
||||
public SearchExamineManagementController(IExamineManagerService examineManagerService) => _examineManagerService = examineManagerService;
|
||||
|
||||
[HttpGet("{searcherName}/query")]
|
||||
[HttpGet("search")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<SearchResultViewModel>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(PagedViewModel<PagedViewModel<SearchResultViewModel>>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<PagedViewModel<SearchResultViewModel>>> Query(string searcherName, string? term, int skip, int take)
|
||||
public async Task<ActionResult<PagedViewModel<SearchResultViewModel>>> GetSearchResults(string searcherName, string? query, int skip, int take)
|
||||
{
|
||||
term = term?.Trim();
|
||||
query = query?.Trim();
|
||||
|
||||
if (term.IsNullOrWhiteSpace())
|
||||
if (query.IsNullOrWhiteSpace())
|
||||
{
|
||||
return new PagedViewModel<SearchResultViewModel>();
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class QuerySearcherController : SearcherControllerBase
|
||||
{
|
||||
results = searcher
|
||||
.CreateQuery()
|
||||
.NativeQuery(term)
|
||||
.NativeQuery(query)
|
||||
.Execute(QueryOptions.SkipTake(skip, take));
|
||||
}
|
||||
catch (ParseException)
|
||||
+7
-6
@@ -1,27 +1,28 @@
|
||||
using Examine;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Infrastructure.Examine;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.ExamineManagement;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Search;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Searcher;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ExamineManagement;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class AllSearcherController : SearcherControllerBase
|
||||
public class SearchersExamineManagementController : ExamineManagementControllerBase
|
||||
{
|
||||
private readonly IExamineManager _examineManager;
|
||||
|
||||
public AllSearcherController(IExamineManager examineManager) => _examineManager = examineManager;
|
||||
public SearchersExamineManagementController(IExamineManager examineManager) => _examineManager = examineManager;
|
||||
|
||||
/// <summary>
|
||||
/// Get the details for searchers
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[HttpGet("searchers")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<SearcherViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<SearcherViewModel>>> All(int skip, int take)
|
||||
public async Task<ActionResult<PagedViewModel<SearcherViewModel>>> Searchers(int skip, int take)
|
||||
{
|
||||
var searchers = new List<SearcherViewModel>(
|
||||
_examineManager.RegisteredSearchers.Select(searcher => new SearcherViewModel { Name = searcher.Name })
|
||||
@@ -1,11 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Help;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("help")]
|
||||
[ApiExplorerSettings(GroupName = "Help")]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/help")]
|
||||
[OpenApiTag("Help")]
|
||||
[ApiVersion("1.0")]
|
||||
public abstract class HelpControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
using Examine;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.ManagementApi.Factories;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Search;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Indexer;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class AllIndexerController : IndexerControllerBase
|
||||
{
|
||||
private readonly IExamineManager _examineManager;
|
||||
private readonly IIndexViewModelFactory _indexViewModelFactory;
|
||||
|
||||
public AllIndexerController(
|
||||
IExamineManager examineManager,
|
||||
IIndexViewModelFactory indexViewModelFactory)
|
||||
{
|
||||
_examineManager = examineManager;
|
||||
_indexViewModelFactory = indexViewModelFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the details for indexers
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<IndexViewModel>), StatusCodes.Status200OK)]
|
||||
public Task<PagedViewModel<IndexViewModel>> All(int skip, int take)
|
||||
{
|
||||
IndexViewModel[] indexes = _examineManager.Indexes
|
||||
.Select(_indexViewModelFactory.Create)
|
||||
.OrderBy(indexModel => indexModel.Name.TrimEnd("Indexer")).ToArray();
|
||||
|
||||
var viewModel = new PagedViewModel<IndexViewModel> { Items = indexes.Skip(skip).Take(take), Total = indexes.Length };
|
||||
return Task.FromResult(viewModel);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Indexer;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("indexer")]
|
||||
[ApiExplorerSettings(GroupName = "Indexer")]
|
||||
public class IndexerControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.ManagementApi.Filters;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
@@ -7,7 +8,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Install;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("install")]
|
||||
[ApiExplorerSettings(GroupName = "Install")]
|
||||
[OpenApiTag("Install")]
|
||||
[RequireRuntimeLevel(RuntimeLevel.Install)]
|
||||
public abstract class InstallControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ public class ValidateDatabaseInstallController : InstallControllerBase
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
[HttpPost("validate-database")]
|
||||
[HttpPost("validateDatabase")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Language;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("language")]
|
||||
[ApiExplorerSettings(GroupName = "Language")]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/language")]
|
||||
[OpenApiTag("Language")]
|
||||
[ApiVersion("1.0")]
|
||||
public abstract class LanguageControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
@@ -13,10 +14,10 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Media.RecycleBin;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.RecycleBin}/{Constants.UdiEntityType.Media}")]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Media}/recycle-bin")]
|
||||
[RequireMediaTreeRootAccess]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Media))]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.Media))]
|
||||
public class MediaRecycleBinControllerBase : RecycleBinControllerBase<RecycleBinItemViewModel>
|
||||
{
|
||||
public MediaRecycleBinControllerBase(IEntityService entityService)
|
||||
|
||||
@@ -20,7 +20,7 @@ public class ItemsMediaTreeController : MediaTreeControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<ContentTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<ContentTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys, Guid? dataTypeKey = null)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
@@ -15,8 +15,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Media.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Media}")]
|
||||
[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Media))]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Media}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.Media))]
|
||||
public class MediaTreeControllerBase : UserStartNodeTreeControllerBase<ContentTreeItemViewModel>
|
||||
{
|
||||
private readonly AppCaches _appCaches;
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ public class ItemsMediaTypeTreeController : MediaTypeTreeControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FolderTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FolderTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
@@ -11,8 +12,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MediaType.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MediaType}")]
|
||||
[ApiExplorerSettings(GroupName = "Media Type")]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MediaType}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.MediaType))]
|
||||
public class MediaTypeTreeControllerBase : FolderTreeControllerBase<FolderTreeItemViewModel>
|
||||
{
|
||||
private readonly IMediaTypeService _mediaTypeService;
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ public class ItemsMemberGroupTreeController : MemberGroupTreeControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<EntityTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<EntityTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
@@ -11,8 +12,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MemberGroup.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MemberGroup}")]
|
||||
[ApiExplorerSettings(GroupName = "Member Group")]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MemberGroup}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.MemberGroup))]
|
||||
public class MemberGroupTreeControllerBase : EntityTreeControllerBase<EntityTreeItemViewModel>
|
||||
{
|
||||
public MemberGroupTreeControllerBase(IEntityService entityService)
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ public class ItemsMemberTypeTreeController : MemberTypeTreeControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<EntityTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<EntityTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
@@ -11,8 +12,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MemberType.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MemberType}")]
|
||||
[ApiExplorerSettings(GroupName = "Member Type")]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MemberType}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.MemberType))]
|
||||
public class MemberTypeTreeControllerBase : EntityTreeControllerBase<EntityTreeItemViewModel>
|
||||
{
|
||||
public MemberTypeTreeControllerBase(IEntityService entityService)
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ using Umbraco.Cms.Infrastructure.ModelsBuilder;
|
||||
using Umbraco.Cms.Infrastructure.ModelsBuilder.Building;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard;
|
||||
|
||||
public class BuildModelsBuilderController : ModelsBuilderControllerBase
|
||||
{
|
||||
+5
-4
@@ -1,9 +1,10 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.ManagementApi.Factories;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.ModelsBuilderDashboard;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard;
|
||||
|
||||
public class GetModelsBuilderController : ModelsBuilderControllerBase
|
||||
{
|
||||
@@ -11,8 +12,8 @@ public class GetModelsBuilderController : ModelsBuilderControllerBase
|
||||
|
||||
public GetModelsBuilderController(IModelsBuilderViewModelFactory modelsBuilderViewModelFactory) => _modelsBuilderViewModelFactory = modelsBuilderViewModelFactory;
|
||||
|
||||
[HttpGet("dashboard")]
|
||||
[ProducesResponseType(typeof(ModelsBuilderViewModel), StatusCodes.Status200OK)]
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(CreatedResult), StatusCodes.Status200OK)]
|
||||
[MapToApiVersion("1.0")]
|
||||
public async Task<ActionResult<ModelsBuilderViewModel>> GetDashboard() => await Task.FromResult(Ok(_modelsBuilderViewModelFactory.Create()));
|
||||
}
|
||||
+4
-3
@@ -1,11 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("models-builder")]
|
||||
[ApiExplorerSettings(GroupName = "Models Builder")]
|
||||
[VersionedApiBackOfficeRoute("modelsBuilder")]
|
||||
[OpenApiTag("ModelsBuilder")]
|
||||
[ApiVersion("1.0")]
|
||||
|
||||
public class ModelsBuilderControllerBase : ManagementApiControllerBase
|
||||
+1
-1
@@ -5,7 +5,7 @@ using Umbraco.Cms.Infrastructure.ModelsBuilder;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.ModelsBuilderDashboard;
|
||||
using Umbraco.New.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard;
|
||||
|
||||
public class StatusModelsBuilderController : ModelsBuilderControllerBase
|
||||
{
|
||||
+1
-1
@@ -12,7 +12,7 @@ public class ItemsPartialViewTreeController : PartialViewTreeControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FileSystemTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FileSystemTreeItemViewModel>>> Items([FromQuery(Name = "path")] string[] paths)
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
@@ -8,8 +9,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.PartialView.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.PartialView}")]
|
||||
[ApiExplorerSettings(GroupName = "Partial View")]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.PartialView}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.PartialView))]
|
||||
public class PartialViewTreeControllerBase : FileSystemTreeControllerBase
|
||||
{
|
||||
public PartialViewTreeControllerBase(FileSystems fileSystems)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Profiling;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("profiling")]
|
||||
[ApiExplorerSettings(GroupName = "Profiling")]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/profiling")]
|
||||
[OpenApiTag("Profiling")]
|
||||
public class ProfilingControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,12 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.PublishedCache;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("published-cache")]
|
||||
[ApiExplorerSettings(GroupName = "Published Cache")]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/published-cache")]
|
||||
[OpenApiTag("PublishedCache")]
|
||||
public class PublishedCacheControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ public class RebuildPublishedCacheController : PublishedCacheControllerBase
|
||||
[HttpPost("rebuild")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult> Rebuild()
|
||||
public async Task<ActionResult> Collect()
|
||||
{
|
||||
_publishedSnapshotService.Rebuild();
|
||||
return await Task.FromResult(Ok());
|
||||
|
||||
@@ -22,7 +22,7 @@ public class ByChildRelationController : RelationControllerBase
|
||||
_relationViewModelFactory = relationViewModelFactory;
|
||||
}
|
||||
|
||||
[HttpGet("child-relation/{childId:int}")]
|
||||
[HttpGet("childRelations/{childId:int}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<RelationViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<PagedViewModel<RelationViewModel>> ByChild(int childId, int skip, int take, string? relationTypeAlias = "")
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Relation;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("relation")]
|
||||
[ApiExplorerSettings(GroupName = "Relation")]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/relation")]
|
||||
[OpenApiTag("Relation")]
|
||||
[ApiVersion("1.0")]
|
||||
// TODO: Implement Authentication
|
||||
public abstract class RelationControllerBase : ManagementApiControllerBase
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ public class ItemsRelationTypeTreeController : RelationTypeTreeControllerBase
|
||||
: base(entityService) =>
|
||||
_relationService = relationService;
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FolderTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FolderTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
@@ -10,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.RelationType.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.RelationType}")]
|
||||
[ApiExplorerSettings(GroupName = "Relation Type")]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.RelationType}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.RelationType))]
|
||||
// NOTE: at the moment relation types aren't supported by EntityService, so we have little use of the
|
||||
// tree controller base. We'll keep it though, in the hope that we can mend EntityService.
|
||||
public class RelationTypeTreeControllerBase : EntityTreeControllerBase<EntityTreeItemViewModel>
|
||||
|
||||
@@ -12,7 +12,7 @@ public class ItemsScriptTreeController : ScriptTreeControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FileSystemTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FileSystemTreeItemViewModel>>> Items([FromQuery(Name = "path")] string[] paths)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
@@ -8,8 +9,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Script.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Script}")]
|
||||
[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Script))]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Script}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.Script))]
|
||||
public class ScriptTreeControllerBase : FileSystemTreeControllerBase
|
||||
{
|
||||
public ScriptTreeControllerBase(FileSystems fileSystems)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Searcher;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("searcher")]
|
||||
[ApiExplorerSettings(GroupName = "Searcher")]
|
||||
public class SearcherControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using OpenIddict.Abstractions;
|
||||
using OpenIddict.Server.AspNetCore;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Web.BackOffice.Security;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
using SignInResult = Microsoft.AspNetCore.Mvc.SignInResult;
|
||||
using IdentitySignInResult = Microsoft.AspNetCore.Identity.SignInResult;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Security;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute(Paths.BackOfficeApiEndpointTemplate)]
|
||||
[ApiExplorerSettings(GroupName = "Security")]
|
||||
public class BackOfficeController : ManagementApiControllerBase
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IBackOfficeSignInManager _backOfficeSignInManager;
|
||||
private readonly IBackOfficeUserManager _backOfficeUserManager;
|
||||
private readonly IOptions<SecuritySettings> _securitySettings;
|
||||
|
||||
public BackOfficeController(
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IBackOfficeSignInManager backOfficeSignInManager,
|
||||
IBackOfficeUserManager backOfficeUserManager,
|
||||
IOptions<SecuritySettings> securitySettings)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_backOfficeSignInManager = backOfficeSignInManager;
|
||||
_backOfficeUserManager = backOfficeUserManager;
|
||||
_securitySettings = securitySettings;
|
||||
}
|
||||
|
||||
[HttpGet("authorize")]
|
||||
[HttpPost("authorize")]
|
||||
[MapToApiVersion("1.0")]
|
||||
public async Task<IActionResult> Authorize()
|
||||
{
|
||||
HttpContext context = _httpContextAccessor.GetRequiredHttpContext();
|
||||
OpenIddictRequest? request = context.GetOpenIddictServerRequest();
|
||||
if (request == null)
|
||||
{
|
||||
return BadRequest("Unable to obtain OpenID data from the current request");
|
||||
}
|
||||
|
||||
return request.IdentityProvider.IsNullOrWhiteSpace()
|
||||
? await AuthorizeInternal(request)
|
||||
: await AuthorizeExternal(request);
|
||||
}
|
||||
|
||||
private async Task<IActionResult> AuthorizeInternal(OpenIddictRequest request)
|
||||
{
|
||||
// TODO: ensure we handle sign-in notifications for internal logins.
|
||||
// when the new login screen is implemented for internal logins, make sure it still handles
|
||||
// user sign-in notifications (calls BackOfficeSignInManager.HandleSignIn) as part of the
|
||||
// sign-in process
|
||||
// for future reference, notifications are already handled for the external login flow by
|
||||
// by calling BackOfficeSignInManager.ExternalLoginSignInAsync
|
||||
|
||||
// retrieve the user principal stored in the authentication cookie.
|
||||
AuthenticateResult cookieAuthResult = await HttpContext.AuthenticateAsync(Constants.Security.BackOfficeAuthenticationType);
|
||||
var userName = cookieAuthResult.Succeeded
|
||||
? cookieAuthResult.Principal?.Identity?.Name
|
||||
: null;
|
||||
|
||||
if (userName != null)
|
||||
{
|
||||
BackOfficeIdentityUser? backOfficeUser = await _backOfficeUserManager.FindByNameAsync(userName);
|
||||
if (backOfficeUser != null)
|
||||
{
|
||||
return await SignInBackOfficeUser(backOfficeUser, request);
|
||||
}
|
||||
}
|
||||
|
||||
return DefaultChallengeResult();
|
||||
}
|
||||
|
||||
private async Task<IActionResult> AuthorizeExternal(OpenIddictRequest request)
|
||||
{
|
||||
var provider = request.IdentityProvider ?? throw new ArgumentException("No identity provider found in request", nameof(request));
|
||||
|
||||
ExternalLoginInfo? loginInfo = await _backOfficeSignInManager.GetExternalLoginInfoAsync();
|
||||
if (loginInfo?.Principal != null)
|
||||
{
|
||||
IdentitySignInResult result = await _backOfficeSignInManager.ExternalLoginSignInAsync(loginInfo, false, _securitySettings.Value.UserBypassTwoFactorForExternalLogins);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
// Update any authentication tokens if succeeded
|
||||
await _backOfficeSignInManager.UpdateExternalAuthenticationTokensAsync(loginInfo);
|
||||
|
||||
// sign in the backoffice user associated with the login provider and unique provider key
|
||||
BackOfficeIdentityUser? backOfficeUser = await _backOfficeUserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey);
|
||||
if (backOfficeUser != null)
|
||||
{
|
||||
return await SignInBackOfficeUser(backOfficeUser, request);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// avoid infinite auth loops when something fails by performing the default challenge (default login screen)
|
||||
return DefaultChallengeResult();
|
||||
}
|
||||
}
|
||||
|
||||
AuthenticationProperties properties = _backOfficeSignInManager.ConfigureExternalAuthenticationProperties(provider, null);
|
||||
return new ChallengeResult(provider, properties);
|
||||
}
|
||||
|
||||
private async Task<IActionResult> SignInBackOfficeUser(BackOfficeIdentityUser backOfficeUser, OpenIddictRequest request)
|
||||
{
|
||||
ClaimsPrincipal backOfficePrincipal = await _backOfficeSignInManager.CreateUserPrincipalAsync(backOfficeUser);
|
||||
backOfficePrincipal.SetClaim(OpenIddictConstants.Claims.Subject, backOfficeUser.Key.ToString());
|
||||
|
||||
// TODO: it is not optimal to append all claims to the token.
|
||||
// the token size grows with each claim, although it is still smaller than the old cookie.
|
||||
// see if we can find a better way so we do not risk leaking sensitive data in bearer tokens.
|
||||
// maybe work with scopes instead?
|
||||
Claim[] backOfficeClaims = backOfficePrincipal.Claims.ToArray();
|
||||
foreach (Claim backOfficeClaim in backOfficeClaims)
|
||||
{
|
||||
backOfficeClaim.SetDestinations(OpenIddictConstants.Destinations.AccessToken);
|
||||
}
|
||||
|
||||
if (request.GetScopes().Contains(OpenIddictConstants.Scopes.OfflineAccess))
|
||||
{
|
||||
// "offline_access" scope is required to use refresh tokens
|
||||
backOfficePrincipal.SetScopes(OpenIddictConstants.Scopes.OfflineAccess);
|
||||
}
|
||||
|
||||
return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, backOfficePrincipal);
|
||||
}
|
||||
|
||||
private static IActionResult DefaultChallengeResult() => new ChallengeResult(Constants.Security.BackOfficeAuthenticationType);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Security;
|
||||
|
||||
public static class Paths
|
||||
{
|
||||
public const string BackOfficeApiEndpointTemplate = "security/back-office";
|
||||
|
||||
public static readonly string BackOfficeApiAuthorizationEndpoint = BackOfficeApiEndpointPath($"{BackOfficeApiEndpointTemplate}/authorize");
|
||||
|
||||
public static readonly string BackOfficeApiTokenEndpoint = BackOfficeApiEndpointPath($"{BackOfficeApiEndpointTemplate}/token");
|
||||
|
||||
private static string BackOfficeApiEndpointPath(string relativePath) => $"/umbraco/management/api/v1.0/{relativePath}";
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Server;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("server")]
|
||||
[ApiExplorerSettings(GroupName = "Server")]
|
||||
[OpenApiTag("Server")]
|
||||
public abstract class ServerControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ public class ItemsStaticFileTreeController : StaticFileTreeControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FileSystemTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FileSystemTreeItemViewModel>>> Items([FromQuery(Name = "path")] string[] paths)
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
@@ -8,8 +9,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.StaticFile.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/static-file")]
|
||||
[ApiExplorerSettings(GroupName = "Static File")]
|
||||
[VersionedApiBackOfficeRoute("static-file/tree")]
|
||||
[OpenApiTag("StaticFile")]
|
||||
public class StaticFileTreeControllerBase : FileSystemTreeControllerBase
|
||||
{
|
||||
private static readonly string[] _allowedRootFolders = { "App_Plugins", "wwwroot" };
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ public class ItemsStylesheetTreeController : StylesheetTreeControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FileSystemTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FileSystemTreeItemViewModel>>> Items([FromQuery(Name = "path")] string[] paths)
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
@@ -8,8 +9,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Stylesheet.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Stylesheet}")]
|
||||
[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Stylesheet))]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Stylesheet}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.Stylesheet))]
|
||||
public class StylesheetTreeControllerBase : FileSystemTreeControllerBase
|
||||
{
|
||||
public StylesheetTreeControllerBase(FileSystems fileSystems)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Telemetry;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry;
|
||||
|
||||
public class AllTelemetryController : TelemetryControllerBase
|
||||
{
|
||||
[HttpGet]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<TelemetryViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<PagedViewModel<TelemetryViewModel>> GetAll(int skip, int take)
|
||||
{
|
||||
TelemetryLevel[] levels = Enum.GetValues<TelemetryLevel>();
|
||||
return await Task.FromResult(new PagedViewModel<TelemetryViewModel>
|
||||
{
|
||||
Total = levels.Length,
|
||||
Items = levels.Skip(skip).Take(take).Select(level => new TelemetryViewModel { TelemetryLevel = level }),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Telemetry;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry;
|
||||
|
||||
public class GetTelemetryController : TelemetryControllerBase
|
||||
{
|
||||
private readonly IMetricsConsentService _metricsConsentService;
|
||||
|
||||
public GetTelemetryController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService;
|
||||
|
||||
[HttpGet("level")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(TelemetryViewModel), StatusCodes.Status200OK)]
|
||||
public async Task<TelemetryViewModel> Get() => await Task.FromResult(new TelemetryViewModel { TelemetryLevel = _metricsConsentService.GetConsentLevel() });
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("telemetry")]
|
||||
[ApiExplorerSettings(GroupName = "Telemetry")]
|
||||
[ApiVersion("1.0")]
|
||||
public abstract class TelemetryControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@ public class ItemsTemplateTreeController : TemplateTreeControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("item")]
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<EntityTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<EntityTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
@@ -11,8 +12,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Template.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Template}")]
|
||||
[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Template))]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Template}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.Template))]
|
||||
public class TemplateTreeControllerBase : EntityTreeControllerBase<EntityTreeItemViewModel>
|
||||
{
|
||||
public TemplateTreeControllerBase(IEntityService entityService)
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference;
|
||||
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute("tracked-reference")]
|
||||
[ApiExplorerSettings(GroupName = "Tracked Reference")]
|
||||
[ApiVersion("1.0")]
|
||||
public abstract class TrackedReferenceControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
+3
-3
@@ -7,14 +7,14 @@ using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.TrackedReferences;
|
||||
using Umbraco.New.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReferences;
|
||||
|
||||
public class DescendantsTrackedReferenceController : TrackedReferenceControllerBase
|
||||
public class DescendantsTrackedReferencesController : TrackedReferencesControllerBase
|
||||
{
|
||||
private readonly ITrackedReferencesService _trackedReferencesSkipTakeService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public DescendantsTrackedReferenceController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper)
|
||||
public DescendantsTrackedReferencesController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_trackedReferencesSkipTakeService = trackedReferencesSkipTakeService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user