Compare commits
440
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f5c21c631 | ||
|
|
4ad18dc963 | ||
|
|
881535af7d | ||
|
|
1e66fb6ab3 | ||
|
|
e1b9e5efad | ||
|
|
28570b43e8 | ||
|
|
a01382d756 | ||
|
|
1d6e7f15b9 | ||
|
|
97cc3ca581 | ||
|
|
0bcae3e87b | ||
|
|
ebd228c3d7 | ||
|
|
4b83a74bdb | ||
|
|
b348b84b63 | ||
|
|
7f4a8d5974 | ||
|
|
4d8ca457ec | ||
|
|
d677e948f1 | ||
|
|
d4e6af50bd | ||
|
|
c609cafa9b | ||
|
|
5556b0fe0c | ||
|
|
eee04f4703 | ||
|
|
127c6c4b53 | ||
|
|
7d6a1e54e6 | ||
|
|
8433b2b637 | ||
|
|
87b2153794 | ||
|
|
0fc5b2a95c | ||
|
|
6c2f93e85d | ||
|
|
4f1604fea1 | ||
|
|
059766291b | ||
|
|
e471c1fc8b | ||
|
|
0fb91ef859 | ||
|
|
c0a0a34cbb | ||
|
|
3d44a6fdf9 | ||
|
|
0eee307622 | ||
|
|
6a4aa04b47 | ||
|
|
643f073945 | ||
|
|
5ba40c65fc | ||
|
|
dcbbed4160 | ||
|
|
e94e165593 | ||
|
|
34709be6cc | ||
|
|
83107bb31a | ||
|
|
3caa43a5bf | ||
|
|
1efe860a89 | ||
|
|
d49965549c | ||
|
|
8d85259025 | ||
|
|
097d0456cc | ||
|
|
a62fa93c77 | ||
|
|
a22dcf05f2 | ||
|
|
afa6fa5ca2 | ||
|
|
c6effef622 | ||
|
|
ab31fbb0aa | ||
|
|
7d9a2c6e70 | ||
|
|
a486d5df33 | ||
|
|
3c8a621817 | ||
|
|
0e0aca55af | ||
|
|
0f02584143 | ||
|
|
a3db45609a | ||
|
|
d60a2a217c | ||
|
|
3e9ff6b5cb | ||
|
|
05a7d337de | ||
|
|
fdca086a47 | ||
|
|
42a81beeac | ||
|
|
9284b9e0b1 | ||
|
|
eb91f4fef4 | ||
|
|
68acc2aa51 | ||
|
|
5570583f70 | ||
|
|
eb979625d1 | ||
|
|
e1e5f527e4 | ||
|
|
f016dbd223 | ||
|
|
b2aa04ae1a | ||
|
|
e270adc50f | ||
|
|
fd5b8de533 | ||
|
|
e3d9b042c2 | ||
|
|
f4ae796b30 | ||
|
|
367d0386b6 | ||
|
|
5b54bed406 | ||
|
|
d3b60277d7 | ||
|
|
2ec6ff4ebc | ||
|
|
78a8d017a2 | ||
|
|
1a205d8727 | ||
|
|
396b5ea211 | ||
|
|
3cf9099f56 | ||
|
|
451b5f96e7 | ||
|
|
15ebae5025 | ||
|
|
32bfbadb8e | ||
|
|
5ed09ebefa | ||
|
|
eaea7a67ba | ||
|
|
de2114b8c5 | ||
|
|
64607ddbaa | ||
|
|
db1d999721 | ||
|
|
18047a7cfb | ||
|
|
def7ebd48c | ||
|
|
8c2b1ebdc5 | ||
|
|
9227517a50 | ||
|
|
5322d0f7b5 | ||
|
|
a282cc5691 | ||
|
|
048f8bcdf9 | ||
|
|
7bcbc748d4 | ||
|
|
f30e6cfe7f | ||
|
|
026e80e026 | ||
|
|
a3b77cff63 | ||
|
|
4ca68d6995 | ||
|
|
280cb7f2b1 | ||
|
|
095a73132c | ||
|
|
cfb0fc23ac | ||
|
|
6620aca9fe | ||
|
|
2422a02b23 | ||
|
|
b4a9dc0770 | ||
|
|
e7411244fd | ||
|
|
9f357173c3 | ||
|
|
7dc6e3ed5b | ||
|
|
5dfff212d0 | ||
|
|
17615f966b | ||
|
|
7a2d6b6c63 | ||
|
|
343867f6ee | ||
|
|
b9837ac77c | ||
|
|
bb73ec6c68 | ||
|
|
ee2d7bbb1b | ||
|
|
5d48bc7371 | ||
|
|
59a4649528 | ||
|
|
f54b6033ab | ||
|
|
bf340cd7d4 | ||
|
|
313417cb91 | ||
|
|
7850078623 | ||
|
|
44bf3b77b3 | ||
|
|
8485458896 | ||
|
|
3d253f5f06 | ||
|
|
64287d7e1a | ||
|
|
edc78a5a4c | ||
|
|
1a18d6c035 | ||
|
|
3e877182b5 | ||
|
|
0e4f883bc1 | ||
|
|
265cc62ab8 | ||
|
|
800c16970e | ||
|
|
6bd11bf233 | ||
|
|
a2fd82a3f3 | ||
|
|
7567990da1 | ||
|
|
c7d157bbfd | ||
|
|
2161edb871 | ||
|
|
e77e9c5691 | ||
|
|
e934a943b5 | ||
|
|
95eb58587b | ||
|
|
25628a8b76 | ||
|
|
7552e315fb | ||
|
|
d15b5b221e | ||
|
|
60e3fa97a9 | ||
|
|
fd1bd51aff | ||
|
|
b12620af9c | ||
|
|
d1af59fdf0 | ||
|
|
1b050ebc47 | ||
|
|
ff3e457c58 | ||
|
|
2ffaede07b | ||
|
|
2a83f57262 | ||
|
|
5be9193978 | ||
|
|
d3f264fc54 | ||
|
|
04ea002bf3 | ||
|
|
8f42a3a0ea | ||
|
|
4dd8fbe25d | ||
|
|
ee727c43e1 | ||
|
|
395213946b | ||
|
|
ed0b236208 | ||
|
|
8d56e75257 | ||
|
|
b3c27415d1 | ||
|
|
9839b0ee66 | ||
|
|
f3f57f9748 | ||
|
|
85d16a4354 | ||
|
|
c0517c3562 | ||
|
|
92e535a1c9 | ||
|
|
7411c2552b | ||
|
|
f2410f376e | ||
|
|
a9313c1367 | ||
|
|
5b3b9d770a | ||
|
|
4a54215fa1 | ||
|
|
52fbb34ddd | ||
|
|
c96dc7eaa1 | ||
|
|
f19409ba5b | ||
|
|
679c42ef90 | ||
|
|
3304c6f472 | ||
|
|
34f09fa260 | ||
|
|
fd4efd38cd | ||
|
|
e4e9bfc3e9 | ||
|
|
3774dbe38c | ||
|
|
4ef50642ad | ||
|
|
f95fb78022 | ||
|
|
0f9a8bde61 | ||
|
|
129d369c1a | ||
|
|
dd2c591a5b | ||
|
|
16749a724d | ||
|
|
a627930b58 | ||
|
|
404a62aa0b | ||
|
|
2d9cfc880b | ||
|
|
ee8bdfc4ac | ||
|
|
b55d484863 | ||
|
|
ed0058a503 | ||
|
|
b0aed393d6 | ||
|
|
a1ea203281 | ||
|
|
4590739fa5 | ||
|
|
7c617f2976 | ||
|
|
9141f61708 | ||
|
|
ba4120050f | ||
|
|
cbd4dc6e0d | ||
|
|
c7014e159b | ||
|
|
9febbc7db1 | ||
|
|
ec8e10f406 | ||
|
|
3233b24a4e | ||
|
|
5775e9cb18 | ||
|
|
570005f5e1 | ||
|
|
fbaa3440f2 | ||
|
|
e662468ecc | ||
|
|
c8efc30654 | ||
|
|
79d117026f | ||
|
|
2709054bdb | ||
|
|
2d4230c001 | ||
|
|
f7729508cf | ||
|
|
31e1acce67 | ||
|
|
aa1f3df76b | ||
|
|
3431f76320 | ||
|
|
1bc5466a8d | ||
|
|
d83daf44d3 | ||
|
|
2ad5ecd5d5 | ||
|
|
35c51a029a | ||
|
|
8c1128c85b | ||
|
|
c9021ab2d2 | ||
|
|
67a71f8f82 | ||
|
|
97d74f39e7 | ||
|
|
edd0a4a4a9 | ||
|
|
8a22672c7f | ||
|
|
11270eaaf5 | ||
|
|
1e32d59ecb | ||
|
|
eab27123dd | ||
|
|
a6253957c8 | ||
|
|
a8f5631144 | ||
|
|
6d98162e19 | ||
|
|
ee37ad0f4b | ||
|
|
2d71b5a63b | ||
|
|
f4f83bccbe | ||
|
|
30b114d538 | ||
|
|
c3db3457e7 | ||
|
|
7787af2df1 | ||
|
|
3730109cce | ||
|
|
b814608c06 | ||
|
|
9bab74d30e | ||
|
|
63a487bc0b | ||
|
|
a40eadcfce | ||
|
|
9b19d63a6a | ||
|
|
6939472f37 | ||
|
|
0387307301 | ||
|
|
25a5dde8e9 | ||
|
|
2e068bb73e | ||
|
|
842cacde19 | ||
|
|
6a453a0910 | ||
|
|
ff9903b14d | ||
|
|
d64bf5de22 | ||
|
|
9300d867fb | ||
|
|
eb0f8b5c24 | ||
|
|
d58768f26d | ||
|
|
3667217053 | ||
|
|
b237285703 | ||
|
|
a6c5581942 | ||
|
|
9a088d36b9 | ||
|
|
407734ff4a | ||
|
|
a74d963cfa | ||
|
|
02a47695e7 | ||
|
|
0f37cd3a45 | ||
|
|
5fe18bb78c | ||
|
|
b16dfa9ca1 | ||
|
|
3e6116fcba | ||
|
|
ef3bf496e9 | ||
|
|
bff293213d | ||
|
|
a47a1775f2 | ||
|
|
b2a2df7a8a | ||
|
|
34179f5b7d | ||
|
|
d38b744c08 | ||
|
|
ef58416814 | ||
|
|
62b7c9468e | ||
|
|
bafdb21b45 | ||
|
|
79080ffa93 | ||
|
|
f99f821a6d | ||
|
|
3b51475150 | ||
|
|
b41de5764b | ||
|
|
bb1c36f591 | ||
|
|
09f16c33d1 | ||
|
|
9d94658372 | ||
|
|
81f36df066 | ||
|
|
7afc2b9963 | ||
|
|
2e3369a09a | ||
|
|
212b2c999f | ||
|
|
2761f5cd20 | ||
|
|
d5831fea94 | ||
|
|
71afa21701 | ||
|
|
2736a513a3 | ||
|
|
bb654360bf | ||
|
|
ee0fd7dd5f | ||
|
|
dfe41d7f76 | ||
|
|
be81586b46 | ||
|
|
c33b06274d | ||
|
|
d72fc5c4e8 | ||
|
|
a41555cc0d | ||
|
|
c1bf3a7e15 | ||
|
|
5f6be40bdf | ||
|
|
8dc02fa081 | ||
|
|
a7ff32891e | ||
|
|
f6deaca452 | ||
|
|
ef01c27fad | ||
|
|
d8b4361936 | ||
|
|
0afb4f7283 | ||
|
|
4e746d367e | ||
|
|
e3d65967aa | ||
|
|
73408f1cce | ||
|
|
6e3a6917c8 | ||
|
|
b2b112eb8b | ||
|
|
56710d5b5b | ||
|
|
00ca9e0d2d | ||
|
|
65c76fcfab | ||
|
|
a686ba2a0e | ||
|
|
344245b1ea | ||
|
|
82b4f506c2 | ||
|
|
5e31fde416 | ||
|
|
b60665d096 | ||
|
|
24abc117fe | ||
|
|
5ae5fe338a | ||
|
|
5f8eac0464 | ||
|
|
fc951ee100 | ||
|
|
78bf04ef67 | ||
|
|
ae7db56f77 | ||
|
|
3dace4fc9d | ||
|
|
32912b0c35 | ||
|
|
f15be3ed65 | ||
|
|
46e99910e7 | ||
|
|
c3e7dad236 | ||
|
|
100f2c3bcd | ||
|
|
0aaac78cfa | ||
|
|
5795cf119a | ||
|
|
528583430f | ||
|
|
696a71166c | ||
|
|
5f082df3ab | ||
|
|
eb6bb99eaf | ||
|
|
fd2138c1fd | ||
|
|
0f3160f727 | ||
|
|
04ed514a21 | ||
|
|
4f382abc66 | ||
|
|
60f656006d | ||
|
|
5c777f3182 | ||
|
|
0ee0db8071 | ||
|
|
9bacd13ce2 | ||
|
|
c17d4e1a60 | ||
|
|
5f24de3085 | ||
|
|
d8df405db4 | ||
|
|
94cef504a3 | ||
|
|
ab32bac5d9 | ||
|
|
18765465ae | ||
|
|
ba9ddd11da | ||
|
|
cfcdc9c98d | ||
|
|
23d0a6b9b2 | ||
|
|
fee222daff | ||
|
|
edb516f71a | ||
|
|
b6031dea1a | ||
|
|
5b46c718e6 | ||
|
|
599ec18ecc | ||
|
|
cae106bfe8 | ||
|
|
119fde2033 | ||
|
|
0980350172 | ||
|
|
a27a4dcd84 | ||
|
|
adc2435073 | ||
|
|
a325ba339b | ||
|
|
096991ac79 | ||
|
|
a6a76d1815 | ||
|
|
6379f2fd35 | ||
|
|
2bb56f1b81 | ||
|
|
e993c23d5c | ||
|
|
71e16d20ef | ||
|
|
682b7656bc | ||
|
|
d765e69713 | ||
|
|
43920f9adc | ||
|
|
fdea8c28c8 | ||
|
|
ce372abf8a | ||
|
|
b1c3473856 | ||
|
|
52c21b0fca | ||
|
|
b7533b57b4 | ||
|
|
0dc13cf23f | ||
|
|
d3b96a163f | ||
|
|
95aba6d696 | ||
|
|
9030b1c19f | ||
|
|
79d241a910 | ||
|
|
c48d6f5a38 | ||
|
|
d9e890e954 | ||
|
|
ecdbd9653b | ||
|
|
e0bb4b6a23 | ||
|
|
2c41388096 | ||
|
|
0ec9423e0a | ||
|
|
efe24289bf | ||
|
|
03aa17e602 | ||
|
|
99d5a7e609 | ||
|
|
e267b41575 | ||
|
|
da43d2f08c | ||
|
|
827fa188f4 | ||
|
|
8789a643ec | ||
|
|
8696f66915 | ||
|
|
98de8acece | ||
|
|
0f076f7a13 | ||
|
|
7e1d1a1968 | ||
|
|
b743f6a2df | ||
|
|
7e822bb8a1 | ||
|
|
3e08ce1efb | ||
|
|
77bfff83f2 | ||
|
|
a34c63a313 | ||
|
|
6ea84db0ae | ||
|
|
a2511ff09b | ||
|
|
2c23e67c65 | ||
|
|
3718a9cdd4 | ||
|
|
e03ff2661b | ||
|
|
d82e262e13 | ||
|
|
0e7e16f455 | ||
|
|
7585918821 | ||
|
|
0e43c43609 | ||
|
|
233452cb55 | ||
|
|
659e74d831 | ||
|
|
b4e2191a4d | ||
|
|
fc86f2222c | ||
|
|
26958df79e | ||
|
|
8e837d387d | ||
|
|
801fb5f885 | ||
|
|
ed517ecd86 | ||
|
|
4a977b85a2 | ||
|
|
2fbda6e64b | ||
|
|
ac5539a8be | ||
|
|
d8b0616434 | ||
|
|
d97cbf486d | ||
|
|
90864d8e58 | ||
|
|
7813acfa2b | ||
|
|
90820483d5 | ||
|
|
b86eeb6b52 | ||
|
|
bf942d17bf | ||
|
|
3344274616 | ||
|
|
ce769ffff4 | ||
|
|
3b4ca0b0f5 | ||
|
|
0cf436fa5c | ||
|
|
4f9c793d00 | ||
|
|
eff25b5abe | ||
|
|
beb431900a | ||
|
|
7a7b8461c5 |
@@ -35,7 +35,7 @@
|
||||
<EnableStrictModeForCompatibleTfms>true</EnableStrictModeForCompatibleTfms>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Calculate version only once for the whole repository -->
|
||||
<!-- Calculate version only once for the whole repository -->
|
||||
<PropertyGroup>
|
||||
<GitVersionBaseDirectory>$(MSBuildThisFileDirectory)</GitVersionBaseDirectory>
|
||||
</PropertyGroup>
|
||||
|
||||
+43
-33
@@ -5,35 +5,35 @@
|
||||
</PropertyGroup>
|
||||
<!-- Global packages (private, build-time packages for all projects) -->
|
||||
<ItemGroup>
|
||||
<GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.6.133" />
|
||||
<GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.6.139" />
|
||||
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.507" />
|
||||
<GlobalPackageReference Include="Umbraco.Code" Version="2.1.0" />
|
||||
<GlobalPackageReference Include="Umbraco.Code" Version="2.2.0" />
|
||||
<GlobalPackageReference Include="Umbraco.GitVersioning.Extensions" Version="0.2.0" />
|
||||
</ItemGroup>
|
||||
<!-- Microsoft packages -->
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.11" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.11" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.11" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.11" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.11" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.11" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.11" />
|
||||
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="8.0.11" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="8.0.11" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
<!-- Umbraco packages -->
|
||||
<ItemGroup>
|
||||
@@ -45,13 +45,13 @@
|
||||
<PackageVersion Include="Asp.Versioning.Mvc" Version="7.1.1" />
|
||||
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="7.1.0" />
|
||||
<PackageVersion Include="Dazinator.Extensions.FileProviders" Version="2.0.0" />
|
||||
<PackageVersion Include="Examine" Version="3.2.0" />
|
||||
<PackageVersion Include="Examine.Core" Version="3.2.0" />
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.11.57" />
|
||||
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.6" />
|
||||
<PackageVersion Include="MailKit" Version="4.3.0" />
|
||||
<PackageVersion Include="Examine" Version="3.7.1" />
|
||||
<PackageVersion Include="Examine.Core" Version="3.7.1" />
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.11.71" />
|
||||
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.8" />
|
||||
<PackageVersion Include="MailKit" Version="4.8.0" />
|
||||
<PackageVersion Include="Markdown" Version="2.2.1" />
|
||||
<PackageVersion Include="MessagePack" Version="2.5.140" />
|
||||
<PackageVersion Include="MessagePack" Version="2.5.192" />
|
||||
<PackageVersion Include="MiniProfiler.AspNetCore.Mvc" Version="4.3.8" />
|
||||
<PackageVersion Include="MiniProfiler.Shared" Version="4.3.8" />
|
||||
<PackageVersion Include="ncrontab" Version="3.3.3" />
|
||||
@@ -62,32 +62,42 @@
|
||||
<PackageVersion Include="OpenIddict.AspNetCore" Version="4.10.1" />
|
||||
<PackageVersion Include="OpenIddict.EntityFrameworkCore" Version="4.10.1" />
|
||||
<PackageVersion Include="Serilog" Version="3.1.1" />
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.1" />
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.3" />
|
||||
<PackageVersion Include="Serilog.Enrichers.Process" Version="2.0.2" />
|
||||
<PackageVersion Include="Serilog.Enrichers.Thread" Version="3.1.0" />
|
||||
<PackageVersion Include="Serilog.Expressions" Version="4.0.0" />
|
||||
<PackageVersion Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageVersion Include="Serilog.Formatting.Compact" Version="2.0.0" />
|
||||
<PackageVersion Include="Serilog.Formatting.Compact.Reader" Version="3.0.0" />
|
||||
<PackageVersion Include="Serilog.Settings.Configuration" Version="8.0.0" />
|
||||
<PackageVersion Include="Serilog.Settings.Configuration" Version="8.0.4" />
|
||||
<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="3.1.2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.1.0" />
|
||||
<PackageVersion Include="Smidge.InMemory" Version="4.3.0" />
|
||||
<PackageVersion Include="Smidge.Nuglify" Version="4.3.0" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.1.3" />
|
||||
<PackageVersion Include="Smidge.InMemory" Version="4.4.0" />
|
||||
<PackageVersion Include="Smidge.Nuglify" Version="4.5.1" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.9.0" />
|
||||
</ItemGroup>
|
||||
<!-- Transitive pinned versions (only required because our direct dependencies have vulnerable versions of transitive dependencies) -->
|
||||
<ItemGroup>
|
||||
<!-- Both Microsoft.EntityFrameworkCore.SqlServer and NPoco.SqlServer bring in a vulnerable version of Azure.Identity -->
|
||||
<PackageVersion Include="Azure.Identity" Version="1.10.4" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.13.1" />
|
||||
<!-- Dazinator.Extensions.FileProviders brings in a vulnerable version of System.Net.Http -->
|
||||
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
||||
<!-- Examine brings in a vulnerable version of System.Security.Cryptography.Xml -->
|
||||
<PackageVersion Include="System.Security.Cryptography.Xml" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Security.Cryptography.Xml" Version="8.0.2" />
|
||||
<!-- Both Dazinator.Extensions.FileProviders and MiniProfiler.AspNetCore.Mvc bring in a vulnerable version of System.Text.RegularExpressions -->
|
||||
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
<!-- Both OpenIddict.AspNetCore, Npoco.SqlServer and Microsoft.EntityFrameworkCore.SqlServer bring in a vulnerable version of Microsoft.IdentityModel.JsonWebTokens -->
|
||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.7.1" />
|
||||
<!-- Examine.Lucene bring in a vulnerable version of Lucene.Net.Replicator -->
|
||||
<PackageVersion Include="Lucene.Net.Replicator" Version="4.8.0-beta00017" />
|
||||
<!-- Both OpenIddict.AspNetCore, Microsoft.EntityFrameworkCore.* bring in a vulnerable version of Microsoft.Extensions.Caching.Memory -->
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
|
||||
<!-- Both Azure.Identity, Microsoft.EntityFrameworkCore.SqlServer,NPoco.SqlServer, and more bring in a vulnerable version of System.Text.Json -->
|
||||
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
|
||||
<!-- Both Microsoft.EntityFrameworkCore.SqlServer and NPoco.SqlServer bring in a vulnerable version of Microsoft.Data.SqlClient -->
|
||||
<PackageVersion Include="Microsoft.Data.SqlClient" Version="5.2.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
+100
-24
@@ -71,8 +71,12 @@ stages:
|
||||
- job: A
|
||||
displayName: Build Umbraco CMS
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
vmImage: 'windows-latest'
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: false
|
||||
lfs: false,
|
||||
fetchDepth: 500
|
||||
- task: NodeTool@0
|
||||
displayName: Use Node.js $(nodeVersion)
|
||||
retryCountOnTaskFailure: 3
|
||||
@@ -198,6 +202,11 @@ stages:
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: false
|
||||
lfs: false,
|
||||
fetchDepth: 1
|
||||
fetchFilter: tree:0
|
||||
- task: NodeTool@0
|
||||
displayName: Use Node.js 10.15.x
|
||||
retryCountOnTaskFailure: 3
|
||||
@@ -249,6 +258,11 @@ stages:
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: false
|
||||
lfs: false,
|
||||
fetchDepth: 1
|
||||
fetchFilter: tree:0
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download build artifacts
|
||||
inputs:
|
||||
@@ -288,6 +302,11 @@ stages:
|
||||
variables:
|
||||
Tests__Database__DatabaseType: 'Sqlite'
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: false
|
||||
lfs: false,
|
||||
fetchDepth: 1
|
||||
fetchFilter: tree:0
|
||||
# Setup test environment
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download build artifacts
|
||||
@@ -319,7 +338,9 @@ stages:
|
||||
# Integration Tests (SQL Server)
|
||||
- job:
|
||||
timeoutInMinutes: 120
|
||||
condition: or(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), ${{parameters.sqlServerIntegrationTests}})
|
||||
# We are currently encountering issues when running SQL Server Linux tests Microsoft.Data.SqlClient.SqlException (0x80131904)
|
||||
# condition: or(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), ${{parameters.sqlServerIntegrationTests}})
|
||||
condition: eq(${{parameters.sqlServerIntegrationTests}}, True)
|
||||
displayName: Integration Tests (SQL Server)
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -415,10 +436,24 @@ stages:
|
||||
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.Sqlite
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
vmImage: 'ubuntu-latest'
|
||||
Windows:
|
||||
vmImage: 'windows-latest'
|
||||
LinuxPart1Of3:
|
||||
vmImage: "ubuntu-latest"
|
||||
testCommand: "npm run test -- --shard=1/3"
|
||||
LinuxPart2Of3:
|
||||
vmImage: "ubuntu-latest"
|
||||
testCommand: "npm run test -- --shard=2/3"
|
||||
LinuxPart3Of3:
|
||||
vmImage: "ubuntu-latest"
|
||||
testCommand: "npm run test -- --shard=3/3"
|
||||
WindowsPart1Of3:
|
||||
vmImage: "windows-latest"
|
||||
testCommand: "npm run test -- --shard=1/3"
|
||||
WindowsPart2Of3:
|
||||
vmImage: "windows-latest"
|
||||
testCommand: "npm run test -- --shard=2/3"
|
||||
WindowsPart3Of3:
|
||||
vmImage: "windows-latest"
|
||||
testCommand: "npm run test -- --shard=3/3"
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
steps:
|
||||
@@ -496,12 +531,12 @@ stages:
|
||||
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
|
||||
|
||||
# Install Playwright and dependencies
|
||||
- pwsh: npx playwright install --with-deps
|
||||
displayName: Install Playwright
|
||||
- pwsh: npx playwright install chromium
|
||||
displayName: Install Playwright only with Chromium browser
|
||||
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
|
||||
|
||||
# Test
|
||||
- pwsh: npm run test --ignore-certificate-errors
|
||||
- pwsh: $(testCommand)
|
||||
displayName: Run Playwright tests
|
||||
continueOnError: true
|
||||
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
|
||||
@@ -522,18 +557,28 @@ stages:
|
||||
# Copy artifacts
|
||||
- pwsh: |
|
||||
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) {
|
||||
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results $(Build.ArtifactStagingDirectory) -Recurse
|
||||
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse
|
||||
}
|
||||
displayName: Copy Playwright results
|
||||
condition: succeededOrFailed()
|
||||
|
||||
# Publish
|
||||
# Publish test artifacts
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: Publish test artifacts
|
||||
condition: succeededOrFailed()
|
||||
inputs:
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
artifact: 'Acceptance Tests - $(Agent.JobName) - Attempt #$(System.JobAttempt)'
|
||||
artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)"
|
||||
|
||||
# Publish test results
|
||||
- task: PublishTestResults@2
|
||||
displayName: "Publish test results"
|
||||
condition: succeededOrFailed()
|
||||
inputs:
|
||||
testResultsFormat: 'JUnit'
|
||||
testResultsFiles: '*.xml'
|
||||
searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results"
|
||||
testRunTitle: "$(Agent.JobName)"
|
||||
|
||||
- job:
|
||||
displayName: E2E Tests (SQL Server)
|
||||
@@ -544,12 +589,30 @@ stages:
|
||||
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
vmImage: 'ubuntu-latest'
|
||||
LinuxPart1Of3:
|
||||
testCommand: "npm run testSqlite -- --shard=1/3"
|
||||
vmImage: "ubuntu-latest"
|
||||
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
|
||||
CONNECTIONSTRINGS__UMBRACODBDSN: 'Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True'
|
||||
Windows:
|
||||
vmImage: 'windows-latest'
|
||||
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
|
||||
LinuxPart2Of3:
|
||||
testCommand: "npm run testSqlite -- --shard=2/3"
|
||||
vmImage: "ubuntu-latest"
|
||||
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
|
||||
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
|
||||
LinuxPart3Of3:
|
||||
testCommand: "npm run testSqlite -- --shard=3/3"
|
||||
vmImage: "ubuntu-latest"
|
||||
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
|
||||
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
|
||||
WindowsPart1Of3:
|
||||
vmImage: "windows-latest"
|
||||
testCommand: "npm run testSqlite -- --shard=1/3"
|
||||
WindowsPart2Of3:
|
||||
vmImage: "windows-latest"
|
||||
testCommand: "npm run testSqlite -- --shard=2/3"
|
||||
WindowsPart3Of3:
|
||||
vmImage: "windows-latest"
|
||||
testCommand: "npm run testSqlite -- --shard=3/3"
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
steps:
|
||||
@@ -635,14 +698,13 @@ stages:
|
||||
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
|
||||
|
||||
# Install Playwright and dependencies
|
||||
- pwsh: npx playwright install --with-deps
|
||||
displayName: Install Playwright
|
||||
- pwsh: npx playwright install chromium
|
||||
displayName: Install Playwright only with Chromium browser
|
||||
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
|
||||
|
||||
# Test
|
||||
- pwsh: npm run test --ignore-certificate-errors
|
||||
- pwsh: $(testCommand)
|
||||
displayName: Run Playwright tests
|
||||
continueOnError: true
|
||||
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
|
||||
env:
|
||||
CI: true
|
||||
@@ -670,18 +732,28 @@ stages:
|
||||
# Copy artifacts
|
||||
- pwsh: |
|
||||
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) {
|
||||
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results $(Build.ArtifactStagingDirectory) -Recurse
|
||||
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse
|
||||
}
|
||||
displayName: Copy Playwright results
|
||||
condition: succeededOrFailed()
|
||||
|
||||
# Publish
|
||||
# Publish test artifacts
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: Publish test artifacts
|
||||
condition: succeededOrFailed()
|
||||
inputs:
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
artifact: 'Acceptance Tests - $(Agent.JobName) - Attempt #$(System.JobAttempt)'
|
||||
artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)"
|
||||
|
||||
# Publish test results
|
||||
- task: PublishTestResults@2
|
||||
displayName: "Publish test results"
|
||||
condition: succeededOrFailed()
|
||||
inputs:
|
||||
testResultsFormat: 'JUnit'
|
||||
testResultsFiles: '*.xml'
|
||||
searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results"
|
||||
testRunTitle: "$(Agent.JobName)"
|
||||
|
||||
###############################################
|
||||
## Release
|
||||
@@ -695,6 +767,8 @@ stages:
|
||||
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.myGetDeploy}}))
|
||||
jobs:
|
||||
- job:
|
||||
pool:
|
||||
vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available.
|
||||
displayName: Push to pre-release feed
|
||||
steps:
|
||||
- checkout: none
|
||||
@@ -721,6 +795,8 @@ stages:
|
||||
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.nuGetDeploy}}))
|
||||
jobs:
|
||||
- job:
|
||||
pool:
|
||||
vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available.
|
||||
displayName: Push to NuGet
|
||||
steps:
|
||||
- checkout: none
|
||||
|
||||
@@ -40,7 +40,9 @@ public static class UmbracoBuilderAuthExtensions
|
||||
.SetLogoutEndpointUris(
|
||||
Paths.MemberApi.LogoutEndpoint.TrimStart(Constants.CharArrays.ForwardSlash))
|
||||
.SetRevocationEndpointUris(
|
||||
Paths.MemberApi.RevokeEndpoint.TrimStart(Constants.CharArrays.ForwardSlash));
|
||||
Paths.MemberApi.RevokeEndpoint.TrimStart(Constants.CharArrays.ForwardSlash))
|
||||
.SetUserinfoEndpointUris(
|
||||
Paths.MemberApi.UserinfoEndpoint.TrimStart(Constants.CharArrays.ForwardSlash));
|
||||
|
||||
// Enable authorization code flow with PKCE
|
||||
options
|
||||
@@ -52,7 +54,8 @@ public static class UmbracoBuilderAuthExtensions
|
||||
options
|
||||
.UseAspNetCore()
|
||||
.EnableAuthorizationEndpointPassthrough()
|
||||
.EnableLogoutEndpointPassthrough();
|
||||
.EnableLogoutEndpointPassthrough()
|
||||
.EnableUserinfoEndpointPassthrough();
|
||||
|
||||
// Enable reference tokens
|
||||
// - see https://documentation.openiddict.com/configuration/token-storage.html
|
||||
|
||||
@@ -14,6 +14,8 @@ public static class Paths
|
||||
|
||||
public static readonly string RevokeEndpoint = EndpointPath($"{EndpointTemplate}/revoke");
|
||||
|
||||
public static readonly string UserinfoEndpoint = EndpointPath($"{EndpointTemplate}/userinfo");
|
||||
|
||||
// NOTE: we're NOT using /api/v1.0/ here because it will clash with the Delivery API docs
|
||||
private static string EndpointPath(string relativePath) => $"/umbraco/delivery/api/v1/{relativePath}";
|
||||
}
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" />
|
||||
<PackageReference Include="OpenIddict.Abstractions" />
|
||||
<PackageReference Include="OpenIddict.AspNetCore" />
|
||||
|
||||
<!-- Both OpenIddict.AspNetCore, Npoco.SqlServer and Microsoft.EntityFrameworkCore.SqlServer bring in a vulnerable version of Microsoft.IdentityModel.JsonWebTokens -->
|
||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens"/>
|
||||
|
||||
<!-- Take top-level depedendency on OpenIddict.AspNetCore depends on a vulnerable version -->
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -8,7 +8,6 @@ using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Controllers.Content;
|
||||
|
||||
@@ -16,10 +15,11 @@ namespace Umbraco.Cms.Api.Delivery.Controllers.Content;
|
||||
[ApiVersion("2.0")]
|
||||
public class ByRouteContentApiController : ContentApiItemControllerBase
|
||||
{
|
||||
private readonly IRequestRoutingService _requestRoutingService;
|
||||
private readonly IApiContentPathResolver _apiContentPathResolver;
|
||||
private readonly IRequestRedirectService _requestRedirectService;
|
||||
private readonly IRequestPreviewService _requestPreviewService;
|
||||
private readonly IRequestMemberAccessService _requestMemberAccessService;
|
||||
private const string PreviewContentRequestPathPrefix = $"/{Constants.DeliveryApi.Routing.PreviewContentPathPrefix}";
|
||||
|
||||
[Obsolete($"Please use the constructor that does not accept {nameof(IPublicAccessService)}. Will be removed in V14.")]
|
||||
public ByRouteContentApiController(
|
||||
@@ -58,7 +58,7 @@ public class ByRouteContentApiController : ContentApiItemControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
[Obsolete($"Please use the constructor that accepts {nameof(IApiContentPathResolver)}. Will be removed in V15.")]
|
||||
public ByRouteContentApiController(
|
||||
IApiPublishedContentCache apiPublishedContentCache,
|
||||
IApiContentResponseBuilder apiContentResponseBuilder,
|
||||
@@ -66,12 +66,50 @@ public class ByRouteContentApiController : ContentApiItemControllerBase
|
||||
IRequestRedirectService requestRedirectService,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
IRequestMemberAccessService requestMemberAccessService)
|
||||
: this(
|
||||
apiPublishedContentCache,
|
||||
apiContentResponseBuilder,
|
||||
requestRedirectService,
|
||||
requestPreviewService,
|
||||
requestMemberAccessService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IApiContentPathResolver>())
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete($"Please use the non-obsolete constructor. Will be removed in V15.")]
|
||||
public ByRouteContentApiController(
|
||||
IApiPublishedContentCache apiPublishedContentCache,
|
||||
IApiContentResponseBuilder apiContentResponseBuilder,
|
||||
IPublicAccessService publicAccessService,
|
||||
IRequestRoutingService requestRoutingService,
|
||||
IRequestRedirectService requestRedirectService,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
IRequestMemberAccessService requestMemberAccessService,
|
||||
IApiContentPathResolver apiContentPathResolver)
|
||||
: this(
|
||||
apiPublishedContentCache,
|
||||
apiContentResponseBuilder,
|
||||
requestRedirectService,
|
||||
requestPreviewService,
|
||||
requestMemberAccessService,
|
||||
apiContentPathResolver)
|
||||
{
|
||||
}
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public ByRouteContentApiController(
|
||||
IApiPublishedContentCache apiPublishedContentCache,
|
||||
IApiContentResponseBuilder apiContentResponseBuilder,
|
||||
IRequestRedirectService requestRedirectService,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
IRequestMemberAccessService requestMemberAccessService,
|
||||
IApiContentPathResolver apiContentPathResolver)
|
||||
: base(apiPublishedContentCache, apiContentResponseBuilder)
|
||||
{
|
||||
_requestRoutingService = requestRoutingService;
|
||||
_requestRedirectService = requestRedirectService;
|
||||
_requestPreviewService = requestPreviewService;
|
||||
_requestMemberAccessService = requestMemberAccessService;
|
||||
_apiContentPathResolver = apiContentPathResolver;
|
||||
}
|
||||
|
||||
[HttpGet("item/{*path}")]
|
||||
@@ -105,10 +143,13 @@ public class ByRouteContentApiController : ContentApiItemControllerBase
|
||||
private async Task<IActionResult> HandleRequest(string path)
|
||||
{
|
||||
path = DecodePath(path);
|
||||
|
||||
path = path.TrimStart("/");
|
||||
path = path.Length == 0 ? "/" : path;
|
||||
|
||||
if (_apiContentPathResolver.IsResolvablePath(path) is false)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
IPublishedContent? contentItem = GetContent(path);
|
||||
if (contentItem is not null)
|
||||
{
|
||||
@@ -128,17 +169,12 @@ public class ByRouteContentApiController : ContentApiItemControllerBase
|
||||
}
|
||||
|
||||
private IPublishedContent? GetContent(string path)
|
||||
=> path.StartsWith(Constants.DeliveryApi.Routing.PreviewContentPathPrefix)
|
||||
=> path.StartsWith(PreviewContentRequestPathPrefix)
|
||||
? GetPreviewContent(path)
|
||||
: GetPublishedContent(path);
|
||||
|
||||
private IPublishedContent? GetPublishedContent(string path)
|
||||
{
|
||||
var contentRoute = _requestRoutingService.GetContentRoute(path);
|
||||
|
||||
IPublishedContent? contentItem = ApiPublishedContentCache.GetByRoute(contentRoute);
|
||||
return contentItem;
|
||||
}
|
||||
=> _apiContentPathResolver.ResolveContentPath(path);
|
||||
|
||||
private IPublishedContent? GetPreviewContent(string path)
|
||||
{
|
||||
@@ -147,7 +183,7 @@ public class ByRouteContentApiController : ContentApiItemControllerBase
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Guid.TryParse(path.AsSpan(Constants.DeliveryApi.Routing.PreviewContentPathPrefix.Length).TrimEnd("/"), out Guid contentId) is false)
|
||||
if (Guid.TryParse(path.AsSpan(PreviewContentRequestPathPrefix.Length).TrimEnd("/"), out Guid contentId) is false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Umbraco.Cms.Api.Delivery.Controllers.Content;
|
||||
[ApiExplorerSettings(GroupName = "Content")]
|
||||
[LocalizeFromAcceptLanguageHeader]
|
||||
[ValidateStartItem]
|
||||
[AddVaryHeader]
|
||||
[OutputCache(PolicyName = Constants.DeliveryApi.OutputCache.ContentCachePolicy)]
|
||||
public abstract class ContentApiControllerBase : DeliveryApiControllerBase
|
||||
{
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using OpenIddict.Server.AspNetCore;
|
||||
using Umbraco.Cms.Api.Delivery.Routing;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Controllers.Security;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedDeliveryApiRoute(Common.Security.Paths.MemberApi.EndpointTemplate)]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
[Authorize(AuthenticationSchemes = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)]
|
||||
public class CurrentMemberController : DeliveryApiControllerBase
|
||||
{
|
||||
private readonly ICurrentMemberClaimsProvider _currentMemberClaimsProvider;
|
||||
|
||||
public CurrentMemberController(ICurrentMemberClaimsProvider currentMemberClaimsProvider)
|
||||
=> _currentMemberClaimsProvider = currentMemberClaimsProvider;
|
||||
|
||||
[HttpGet("userinfo")]
|
||||
public async Task<IActionResult> Userinfo()
|
||||
{
|
||||
Dictionary<string, object> claims = await _currentMemberClaimsProvider.GetClaimsAsync();
|
||||
return Ok(claims);
|
||||
}
|
||||
}
|
||||
@@ -160,11 +160,12 @@ public class MemberController : DeliveryApiControllerBase
|
||||
claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken);
|
||||
}
|
||||
|
||||
if (request.GetScopes().Contains(OpenIddictConstants.Scopes.OfflineAccess))
|
||||
{
|
||||
// "offline_access" scope is required to use refresh tokens
|
||||
memberPrincipal.SetScopes(OpenIddictConstants.Scopes.OfflineAccess);
|
||||
}
|
||||
// "openid" and "offline_access" are the only scopes allowed for members; explicitly ensure we only add those
|
||||
// NOTE: the "offline_access" scope is required to use refresh tokens
|
||||
IEnumerable<string> allowedScopes = request
|
||||
.GetScopes()
|
||||
.Intersect(new[] { OpenIddictConstants.Scopes.OpenId, OpenIddictConstants.Scopes.OfflineAccess });
|
||||
memberPrincipal.SetScopes(allowedScopes);
|
||||
|
||||
return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, memberPrincipal);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ public static class UmbracoBuilderExtensions
|
||||
builder.Services.AddSingleton<IApiMediaQueryService, ApiMediaQueryService>();
|
||||
builder.Services.AddTransient<IMemberApplicationManager, MemberApplicationManager>();
|
||||
builder.Services.AddTransient<IRequestMemberAccessService, RequestMemberAccessService>();
|
||||
builder.Services.AddTransient<ICurrentMemberClaimsProvider, CurrentMemberClaimsProvider>();
|
||||
|
||||
builder.Services.ConfigureOptions<ConfigureUmbracoDeliveryApiSwaggerGenOptions>();
|
||||
builder.AddUmbracoApiOpenApiUI();
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Filters;
|
||||
|
||||
public sealed class AddVaryHeaderAttribute : ActionFilterAttribute
|
||||
{
|
||||
private const string Vary = "Accept-Language, Preview, Start-Item";
|
||||
|
||||
public override void OnResultExecuting(ResultExecutingContext context)
|
||||
=> context.HttpContext.Response.Headers.Vary = context.HttpContext.Response.Headers.Vary.Count > 0
|
||||
? $"{context.HttpContext.Response.Headers.Vary}, {Vary}"
|
||||
: Vary;
|
||||
}
|
||||
@@ -12,23 +12,36 @@ public class DeliveryApiJsonTypeResolver : DefaultJsonTypeInfoResolver
|
||||
{
|
||||
JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options);
|
||||
|
||||
if (jsonTypeInfo.Type == typeof(IApiContent))
|
||||
Type[] derivedTypes = GetDerivedTypes(jsonTypeInfo);
|
||||
if (derivedTypes.Length > 0)
|
||||
{
|
||||
ConfigureJsonPolymorphismOptions(jsonTypeInfo, typeof(ApiContent));
|
||||
}
|
||||
else if (jsonTypeInfo.Type == typeof(IApiContentResponse))
|
||||
{
|
||||
ConfigureJsonPolymorphismOptions(jsonTypeInfo, typeof(ApiContentResponse));
|
||||
}
|
||||
else if (jsonTypeInfo.Type == typeof(IRichTextElement))
|
||||
{
|
||||
ConfigureJsonPolymorphismOptions(jsonTypeInfo, typeof(RichTextRootElement), typeof(RichTextGenericElement), typeof(RichTextTextElement));
|
||||
ConfigureJsonPolymorphismOptions(jsonTypeInfo, derivedTypes);
|
||||
}
|
||||
|
||||
return jsonTypeInfo;
|
||||
}
|
||||
|
||||
private void ConfigureJsonPolymorphismOptions(JsonTypeInfo jsonTypeInfo, params Type[] derivedTypes)
|
||||
protected virtual Type[] GetDerivedTypes(JsonTypeInfo jsonTypeInfo)
|
||||
{
|
||||
if (jsonTypeInfo.Type == typeof(IApiContent))
|
||||
{
|
||||
return new[] { typeof(ApiContent) };
|
||||
}
|
||||
|
||||
if (jsonTypeInfo.Type == typeof(IApiContentResponse))
|
||||
{
|
||||
return new[] { typeof(ApiContentResponse) };
|
||||
}
|
||||
|
||||
if (jsonTypeInfo.Type == typeof(IRichTextElement))
|
||||
{
|
||||
return new[] { typeof(RichTextRootElement), typeof(RichTextGenericElement), typeof(RichTextTextElement) };
|
||||
}
|
||||
|
||||
return Array.Empty<Type>();
|
||||
}
|
||||
|
||||
protected void ConfigureJsonPolymorphismOptions(JsonTypeInfo jsonTypeInfo, params Type[] derivedTypes)
|
||||
{
|
||||
jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Umbraco.Cms.Api.Delivery.Indexing.Filters;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Querying.Filters;
|
||||
|
||||
@@ -15,15 +14,15 @@ public sealed class ContentTypeFilter : IFilterHandler
|
||||
/// <inheritdoc/>
|
||||
public FilterOption BuildFilterOption(string filter)
|
||||
{
|
||||
var alias = filter.Substring(ContentTypeSpecifier.Length);
|
||||
var filterValue = filter.Substring(ContentTypeSpecifier.Length);
|
||||
var negate = filterValue.StartsWith('!');
|
||||
var aliases = filterValue.TrimStart('!').Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
return new FilterOption
|
||||
{
|
||||
FieldName = ContentTypeFilterIndexer.FieldName,
|
||||
Values = alias.IsNullOrWhiteSpace() == false
|
||||
? new[] { alias.TrimStart('!') }
|
||||
: Array.Empty<string>(),
|
||||
Operator = alias.StartsWith('!')
|
||||
Values = aliases,
|
||||
Operator = negate
|
||||
? FilterOperation.IsNot
|
||||
: FilterOperation.Is
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Delivery.Indexing.Selectors;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Extensions;
|
||||
@@ -10,10 +12,21 @@ public sealed class AncestorsSelector : QueryOptionBase, ISelectorHandler
|
||||
{
|
||||
private const string AncestorsSpecifier = "ancestors:";
|
||||
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
|
||||
private readonly IRequestPreviewService _requestPreviewService;
|
||||
|
||||
public AncestorsSelector(IPublishedSnapshotAccessor publishedSnapshotAccessor, IRequestRoutingService requestRoutingService)
|
||||
: base(publishedSnapshotAccessor, requestRoutingService) =>
|
||||
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
|
||||
public AncestorsSelector(IPublishedSnapshotAccessor publishedSnapshotAccessor,
|
||||
IRequestRoutingService requestRoutingService)
|
||||
: this(publishedSnapshotAccessor, requestRoutingService, StaticServiceProvider.Instance.GetRequiredService<IRequestPreviewService>())
|
||||
{
|
||||
}
|
||||
|
||||
public AncestorsSelector(IPublishedSnapshotAccessor publishedSnapshotAccessor, IRequestRoutingService requestRoutingService, IRequestPreviewService requestPreviewService)
|
||||
: base(publishedSnapshotAccessor, requestRoutingService)
|
||||
{
|
||||
_publishedSnapshotAccessor = publishedSnapshotAccessor;
|
||||
_requestPreviewService = requestPreviewService;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanHandle(string query)
|
||||
@@ -37,10 +50,20 @@ public sealed class AncestorsSelector : QueryOptionBase, ISelectorHandler
|
||||
};
|
||||
}
|
||||
|
||||
IPublishedSnapshot publishedSnapshot = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot();
|
||||
IPublishedContentCache contentCache = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot()?.Content
|
||||
?? throw new InvalidOperationException("Could not obtain the content cache");
|
||||
|
||||
IPublishedContent contentItem = publishedSnapshot.Content?.GetById((Guid)id)
|
||||
?? throw new InvalidOperationException("Could not obtain the content cache");
|
||||
IPublishedContent? contentItem = contentCache.GetById(_requestPreviewService.IsPreview(), id.Value);
|
||||
|
||||
if (contentItem is null)
|
||||
{
|
||||
// no such content item, make sure the selector does not yield any results
|
||||
return new SelectorOption
|
||||
{
|
||||
FieldName = AncestorsSelectorIndexer.FieldName,
|
||||
Values = Array.Empty<string>()
|
||||
};
|
||||
}
|
||||
|
||||
var ancestorKeys = contentItem.Ancestors().Select(a => a.Key.ToString("D")).ToArray();
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
using OpenIddict.Abstractions;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Services;
|
||||
|
||||
// NOTE: this is public and unsealed to allow overriding the default claims with minimal effort.
|
||||
public class CurrentMemberClaimsProvider : ICurrentMemberClaimsProvider
|
||||
{
|
||||
private readonly IMemberManager _memberManager;
|
||||
|
||||
public CurrentMemberClaimsProvider(IMemberManager memberManager)
|
||||
=> _memberManager = memberManager;
|
||||
|
||||
public virtual async Task<Dictionary<string, object>> GetClaimsAsync()
|
||||
{
|
||||
MemberIdentityUser? memberIdentityUser = await _memberManager.GetCurrentMemberAsync();
|
||||
return memberIdentityUser is not null
|
||||
? await GetClaimsForMemberIdentityAsync(memberIdentityUser)
|
||||
: throw new InvalidOperationException("Could not retrieve the current member. This method should only ever be invoked when a member has been authorized.");
|
||||
}
|
||||
|
||||
protected virtual async Task<Dictionary<string, object>> GetClaimsForMemberIdentityAsync(MemberIdentityUser memberIdentityUser)
|
||||
{
|
||||
var claims = new Dictionary<string, object>
|
||||
{
|
||||
[OpenIddictConstants.Claims.Subject] = memberIdentityUser.Key
|
||||
};
|
||||
|
||||
if (memberIdentityUser.Name is not null)
|
||||
{
|
||||
claims[OpenIddictConstants.Claims.Name] = memberIdentityUser.Name;
|
||||
}
|
||||
|
||||
if (memberIdentityUser.Email is not null)
|
||||
{
|
||||
claims[OpenIddictConstants.Claims.Email] = memberIdentityUser.Email;
|
||||
}
|
||||
|
||||
claims[OpenIddictConstants.Claims.Role] = await _memberManager.GetRolesAsync(memberIdentityUser);
|
||||
|
||||
return claims;
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,5 @@ internal abstract class RequestHeaderHandler
|
||||
|
||||
protected RequestHeaderHandler(IHttpContextAccessor httpContextAccessor) => _httpContextAccessor = httpContextAccessor;
|
||||
|
||||
protected string? GetHeaderValue(string headerName)
|
||||
{
|
||||
HttpContext httpContext = _httpContextAccessor.HttpContext ??
|
||||
throw new InvalidOperationException("Could not obtain an HTTP context");
|
||||
|
||||
return httpContext.Request.Headers[headerName];
|
||||
}
|
||||
protected string? GetHeaderValue(string headerName) => _httpContextAccessor.HttpContext?.Request.Headers[headerName];
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Services;
|
||||
@@ -11,5 +11,5 @@ internal sealed class RequestPreviewService : RequestHeaderHandler, IRequestPrev
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPreview() => GetHeaderValue("Preview") == "true";
|
||||
public bool IsPreview() => string.Equals(GetHeaderValue("Preview"), "true", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
@@ -42,31 +42,42 @@ internal sealed class RequestRedirectService : RoutingServiceBase, IRequestRedir
|
||||
{
|
||||
requestedPath = requestedPath.EnsureStartsWith("/");
|
||||
|
||||
IPublishedContent? startItem = GetStartItem();
|
||||
|
||||
// must append the root content url segment if it is not hidden by config, because
|
||||
// the URL tracking is based on the actual URL, including the root content url segment
|
||||
if (_globalSettings.HideTopLevelNodeFromPath == false)
|
||||
if (_globalSettings.HideTopLevelNodeFromPath == false && startItem?.UrlSegment != null)
|
||||
{
|
||||
IPublishedContent? startItem = GetStartItem();
|
||||
if (startItem?.UrlSegment != null)
|
||||
{
|
||||
requestedPath = $"{startItem.UrlSegment.EnsureStartsWith("/")}{requestedPath}";
|
||||
}
|
||||
requestedPath = $"{startItem.UrlSegment.EnsureStartsWith("/")}{requestedPath}";
|
||||
}
|
||||
|
||||
var culture = _requestCultureService.GetRequestedCulture();
|
||||
|
||||
// append the configured domain content ID to the path if we have a domain bound request,
|
||||
// because URL tracking registers the tracked url like "{domain content ID}/{content path}"
|
||||
Uri contentRoute = GetDefaultRequestUri(requestedPath);
|
||||
DomainAndUri? domainAndUri = GetDomainAndUriForRoute(contentRoute);
|
||||
if (domainAndUri != null)
|
||||
// important: redirect URLs are always tracked without trailing slashes
|
||||
requestedPath = requestedPath.TrimEnd("/");
|
||||
IRedirectUrl? redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl(requestedPath, culture);
|
||||
|
||||
// if a redirect URL was not found, try by appending the start item ID because URL tracking might have tracked
|
||||
// a redirect with "{root content ID}/{content path}"
|
||||
if (redirectUrl is null && startItem is not null)
|
||||
{
|
||||
requestedPath = GetContentRoute(domainAndUri, contentRoute);
|
||||
culture ??= domainAndUri.Culture;
|
||||
redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl($"{startItem.Id}{requestedPath}", culture);
|
||||
}
|
||||
|
||||
// still no redirect URL found - try looking for a configured domain if we have a domain bound request,
|
||||
// because URL tracking might have tracked a redirect with "{domain content ID}/{content path}"
|
||||
if (redirectUrl is null)
|
||||
{
|
||||
Uri contentRoute = GetDefaultRequestUri(requestedPath);
|
||||
DomainAndUri? domainAndUri = GetDomainAndUriForRoute(contentRoute);
|
||||
if (domainAndUri is not null)
|
||||
{
|
||||
requestedPath = GetContentRoute(domainAndUri, contentRoute);
|
||||
culture ??= domainAndUri.Culture;
|
||||
redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl(requestedPath, culture);
|
||||
}
|
||||
}
|
||||
|
||||
// important: redirect URLs are always tracked without trailing slashes
|
||||
IRedirectUrl? redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl(requestedPath.TrimEnd("/"), culture);
|
||||
IPublishedContent? content = redirectUrl != null
|
||||
? _apiPublishedContentCache.GetById(redirectUrl.ContentKey)
|
||||
: null;
|
||||
|
||||
@@ -36,7 +36,7 @@ internal abstract class RoutingServiceBase
|
||||
}
|
||||
|
||||
protected static string GetContentRoute(DomainAndUri domainAndUri, Uri contentRoute)
|
||||
=> $"{domainAndUri.ContentId}{DomainUtilities.PathRelativeToDomain(domainAndUri.Uri, contentRoute.AbsolutePath)}";
|
||||
=> $"{domainAndUri.ContentId}{DomainUtilities.PathRelativeToDomain(domainAndUri.Uri, contentRoute.LocalPath)}"; // Use LocalPath over AbsolutePath to keep the path decoded.
|
||||
|
||||
protected DomainAndUri? GetDomainAndUriForRoute(Uri contentUrl)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Umbraco.Tests.UnitTests</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Umbraco.Tests.Integration</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>DynamicProxyGenAssembly2</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Headers;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -48,16 +49,26 @@ public sealed class ConfigureImageSharpMiddlewareOptions : IConfigureOptions<Ima
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
int width = context.Parser.ParseValue<int>(context.Commands.GetValueOrDefault(ResizeWebProcessor.Width), context.Culture);
|
||||
if (width <= 0 || width > _imagingSettings.Resize.MaxWidth)
|
||||
if (context.Commands.Contains(ResizeWebProcessor.Width))
|
||||
{
|
||||
context.Commands.Remove(ResizeWebProcessor.Width);
|
||||
if (!int.TryParse(context.Commands.GetValueOrDefault(ResizeWebProcessor.Width), NumberStyles.Integer,
|
||||
CultureInfo.InvariantCulture, out var width)
|
||||
|| width < 0
|
||||
|| width >= _imagingSettings.Resize.MaxWidth)
|
||||
{
|
||||
context.Commands.Remove(ResizeWebProcessor.Width);
|
||||
}
|
||||
}
|
||||
|
||||
int height = context.Parser.ParseValue<int>(context.Commands.GetValueOrDefault(ResizeWebProcessor.Height), context.Culture);
|
||||
if (height <= 0 || height > _imagingSettings.Resize.MaxHeight)
|
||||
if (context.Commands.Contains(ResizeWebProcessor.Height))
|
||||
{
|
||||
context.Commands.Remove(ResizeWebProcessor.Height);
|
||||
if (!int.TryParse(context.Commands.GetValueOrDefault(ResizeWebProcessor.Height), NumberStyles.Integer,
|
||||
CultureInfo.InvariantCulture, out var height)
|
||||
|| height < 0
|
||||
|| height >= _imagingSettings.Resize.MaxHeight)
|
||||
{
|
||||
context.Commands.Remove(ResizeWebProcessor.Height);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SixLabors.ImageSharp" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Web" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Web" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SixLabors.ImageSharp" VersionOverride="[2.1.6, 3)" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" VersionOverride="[2.1.10, 3)" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Web" VersionOverride="[2.0.2, 3)" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
+8
@@ -7,7 +7,15 @@
|
||||
<ItemGroup>
|
||||
<!-- Take top-level depedendency on Azure.Identity, because Microsoft.EntityFrameworkCore.SqlServer depends on a vulnerable version -->
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
<!-- Both Azure.Identity, Microsoft.EntityFrameworkCore.SqlServer,NPoco.SqlServer, and more bring in a vulnerable version of System.Text.Json -->
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
<!-- Both Microsoft.EntityFrameworkCore.SqlServer and NPoco.SqlServer bring in a vulnerable version of Microsoft.Data.SqlClient -->
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" />
|
||||
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
|
||||
|
||||
<!-- Both OpenIddict.AspNetCore, Npoco.SqlServer and Microsoft.EntityFrameworkCore.SqlServer bring in a vulnerable version of Microsoft.IdentityModel.JsonWebTokens -->
|
||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
+14
-14
@@ -16,8 +16,8 @@ namespace Umbraco.Cms.Persistence.EFCore.Locking;
|
||||
internal class SqlServerEFCoreDistributedLockingMechanism<T> : IDistributedLockingMechanism
|
||||
where T : DbContext
|
||||
{
|
||||
private readonly IOptionsMonitor<ConnectionStrings> _connectionStrings;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private ConnectionStrings _connectionStrings;
|
||||
private GlobalSettings _globalSettings;
|
||||
private readonly ILogger<SqlServerEFCoreDistributedLockingMechanism<T>> _logger;
|
||||
private readonly Lazy<IEFCoreScopeAccessor<T>> _scopeAccessor; // Hooray it's a circular dependency.
|
||||
|
||||
@@ -32,27 +32,29 @@ internal class SqlServerEFCoreDistributedLockingMechanism<T> : IDistributedLocki
|
||||
{
|
||||
_logger = logger;
|
||||
_scopeAccessor = scopeAccessor;
|
||||
_globalSettings = globalSettings;
|
||||
_connectionStrings = connectionStrings;
|
||||
_globalSettings = globalSettings.CurrentValue;
|
||||
_connectionStrings = connectionStrings.CurrentValue;
|
||||
globalSettings.OnChange(x=>_globalSettings = x);
|
||||
connectionStrings.OnChange(x=>_connectionStrings = x);
|
||||
}
|
||||
|
||||
public bool HasActiveRelatedScope => _scopeAccessor.Value.AmbientScope is not null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Enabled => _connectionStrings.CurrentValue.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.CurrentValue.ProviderName, "Microsoft.Data.SqlClient", StringComparison.InvariantCultureIgnoreCase) && _scopeAccessor.Value.AmbientScope is not null;
|
||||
public bool Enabled => _connectionStrings.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.ProviderName, "Microsoft.Data.SqlClient", StringComparison.InvariantCultureIgnoreCase) && _scopeAccessor.Value.AmbientScope is not null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDistributedLock ReadLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingReadLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingReadLockDefaultTimeout;
|
||||
return new SqlServerDistributedLock(this, lockId, DistributedLockType.ReadLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDistributedLock WriteLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingWriteLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingWriteLockDefaultTimeout;
|
||||
return new SqlServerDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
@@ -136,9 +138,7 @@ internal class SqlServerEFCoreDistributedLockingMechanism<T> : IDistributedLocki
|
||||
"A transaction with minimum ReadCommitted isolation level is required.");
|
||||
}
|
||||
|
||||
await dbContext.Database.ExecuteSqlRawAsync($"SET LOCK_TIMEOUT {(int)_timeout.TotalMilliseconds};");
|
||||
|
||||
var number = await dbContext.Database.ExecuteScalarAsync<int?>($"SELECT value FROM dbo.umbracoLock WITH (REPEATABLEREAD) WHERE id={LockId}");
|
||||
var number = await dbContext.Database.ExecuteScalarAsync<int?>($"SET LOCK_TIMEOUT {(int)_timeout.TotalMilliseconds};SELECT value FROM dbo.umbracoLock WITH (REPEATABLEREAD) WHERE id={LockId}");
|
||||
|
||||
if (number == null)
|
||||
{
|
||||
@@ -170,9 +170,9 @@ internal class SqlServerEFCoreDistributedLockingMechanism<T> : IDistributedLocki
|
||||
"A transaction with minimum ReadCommitted isolation level is required.");
|
||||
}
|
||||
|
||||
await dbContext.Database.ExecuteSqlRawAsync($"SET LOCK_TIMEOUT {(int)_timeout.TotalMilliseconds};");
|
||||
|
||||
var rowsAffected = await dbContext.Database.ExecuteSqlAsync(@$"UPDATE umbracoLock WITH (REPEATABLEREAD) SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id={LockId}");
|
||||
#pragma warning disable EF1002
|
||||
var rowsAffected = await dbContext.Database.ExecuteSqlRawAsync(@$"SET LOCK_TIMEOUT {(int)_timeout.TotalMilliseconds};UPDATE umbracoLock WITH (REPEATABLEREAD) SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id={LockId}");
|
||||
#pragma warning restore EF1002
|
||||
|
||||
if (rowsAffected == 0)
|
||||
{
|
||||
|
||||
+10
-8
@@ -16,8 +16,8 @@ namespace Umbraco.Cms.Persistence.EFCore.Locking;
|
||||
internal class SqliteEFCoreDistributedLockingMechanism<T> : IDistributedLockingMechanism
|
||||
where T : DbContext
|
||||
{
|
||||
private readonly IOptionsMonitor<ConnectionStrings> _connectionStrings;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private ConnectionStrings _connectionStrings;
|
||||
private GlobalSettings _globalSettings;
|
||||
private readonly ILogger<SqliteEFCoreDistributedLockingMechanism<T>> _logger;
|
||||
private readonly Lazy<IEFCoreScopeAccessor<T>> _efCoreScopeAccessor;
|
||||
|
||||
@@ -29,27 +29,29 @@ internal class SqliteEFCoreDistributedLockingMechanism<T> : IDistributedLockingM
|
||||
{
|
||||
_logger = logger;
|
||||
_efCoreScopeAccessor = efCoreScopeAccessor;
|
||||
_connectionStrings = connectionStrings;
|
||||
_globalSettings = globalSettings;
|
||||
_globalSettings = globalSettings.CurrentValue;
|
||||
_connectionStrings = connectionStrings.CurrentValue;
|
||||
globalSettings.OnChange(x=>_globalSettings = x);
|
||||
connectionStrings.OnChange(x=>_connectionStrings = x);
|
||||
}
|
||||
|
||||
public bool HasActiveRelatedScope => _efCoreScopeAccessor.Value.AmbientScope is not null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Enabled => _connectionStrings.CurrentValue.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.CurrentValue.ProviderName, "Microsoft.Data.Sqlite", StringComparison.InvariantCultureIgnoreCase) && _efCoreScopeAccessor.Value.AmbientScope is not null;
|
||||
public bool Enabled => _connectionStrings.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.ProviderName, "Microsoft.Data.Sqlite", StringComparison.InvariantCultureIgnoreCase) && _efCoreScopeAccessor.Value.AmbientScope is not null;
|
||||
|
||||
// With journal_mode=wal we can always read a snapshot.
|
||||
public IDistributedLock ReadLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingReadLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingReadLockDefaultTimeout;
|
||||
return new SqliteDistributedLock(this, lockId, DistributedLockType.ReadLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
// With journal_mode=wal only a single write transaction can exist at a time.
|
||||
public IDistributedLock WriteLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingWriteLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingWriteLockDefaultTimeout;
|
||||
return new SqliteDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,14 @@
|
||||
<ItemGroup>
|
||||
<!-- Take top-level depedendency on Azure.Identity, because Microsoft.EntityFrameworkCore.SqlServer depends on a vulnerable version -->
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
|
||||
<!-- Take top-level depedendency on Microsoft.Extensions.Caching.Memory, because Microsoft.EntityFrameworkCore.* depends on a vulnerable version -->
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
|
||||
<!-- Both Azure.Identity, Microsoft.EntityFrameworkCore.SqlServer,NPoco.SqlServer, and more bring in a vulnerable version of System.Text.Json -->
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
<!-- Both Microsoft.EntityFrameworkCore.SqlServer and NPoco.SqlServer bring in a vulnerable version of Microsoft.Data.SqlClient -->
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" />
|
||||
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
|
||||
<PackageReference Include="OpenIddict.EntityFrameworkCore" />
|
||||
|
||||
+11
-8
@@ -17,8 +17,8 @@ namespace Umbraco.Cms.Persistence.SqlServer.Services;
|
||||
/// </summary>
|
||||
public class SqlServerDistributedLockingMechanism : IDistributedLockingMechanism
|
||||
{
|
||||
private readonly IOptionsMonitor<ConnectionStrings> _connectionStrings;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private ConnectionStrings _connectionStrings;
|
||||
private GlobalSettings _globalSettings;
|
||||
private readonly ILogger<SqlServerDistributedLockingMechanism> _logger;
|
||||
private readonly Lazy<IScopeAccessor> _scopeAccessor; // Hooray it's a circular dependency.
|
||||
|
||||
@@ -33,25 +33,28 @@ public class SqlServerDistributedLockingMechanism : IDistributedLockingMechanism
|
||||
{
|
||||
_logger = logger;
|
||||
_scopeAccessor = scopeAccessor;
|
||||
_globalSettings = globalSettings;
|
||||
_connectionStrings = connectionStrings;
|
||||
_globalSettings = globalSettings.CurrentValue;
|
||||
_connectionStrings = connectionStrings.CurrentValue;
|
||||
globalSettings.OnChange(x => _globalSettings = x);
|
||||
connectionStrings.OnChange(x => _connectionStrings = x);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Enabled => _connectionStrings.CurrentValue.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.CurrentValue.ProviderName,Constants.ProviderName, StringComparison.InvariantCultureIgnoreCase);
|
||||
public bool Enabled => _connectionStrings.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.ProviderName,Constants.ProviderName, StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDistributedLock ReadLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingReadLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingReadLockDefaultTimeout;
|
||||
return new SqlServerDistributedLock(this, lockId, DistributedLockType.ReadLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDistributedLock WriteLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingWriteLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingWriteLockDefaultTimeout;
|
||||
return new SqlServerDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,15 @@
|
||||
<ItemGroup>
|
||||
<!-- Take top-level depedendency on Azure.Identity, because NPoco.SqlServer depends on a vulnerable version -->
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
<!-- Both Azure.Identity, Microsoft.EntityFrameworkCore.SqlServer,NPoco.SqlServer, and more bring in a vulnerable version of System.Text.Json -->
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
<!-- Both Microsoft.EntityFrameworkCore.SqlServer and NPoco.SqlServer bring in a vulnerable version of Microsoft.Data.SqlClient -->
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" />
|
||||
|
||||
<PackageReference Include="NPoco.SqlServer" />
|
||||
|
||||
<!-- Both OpenIddict.AspNetCore, Npoco.SqlServer and Microsoft.EntityFrameworkCore.SqlServer bring in a vulnerable version of Microsoft.IdentityModel.JsonWebTokens -->
|
||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace Umbraco.Cms.Persistence.Sqlite.Services;
|
||||
|
||||
public class SqliteDistributedLockingMechanism : IDistributedLockingMechanism
|
||||
{
|
||||
private readonly IOptionsMonitor<ConnectionStrings> _connectionStrings;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private ConnectionStrings _connectionStrings;
|
||||
private GlobalSettings _globalSettings;
|
||||
private readonly ILogger<SqliteDistributedLockingMechanism> _logger;
|
||||
private readonly Lazy<IScopeAccessor> _scopeAccessor;
|
||||
|
||||
@@ -29,25 +29,27 @@ public class SqliteDistributedLockingMechanism : IDistributedLockingMechanism
|
||||
{
|
||||
_logger = logger;
|
||||
_scopeAccessor = scopeAccessor;
|
||||
_connectionStrings = connectionStrings;
|
||||
_globalSettings = globalSettings;
|
||||
_connectionStrings = connectionStrings.CurrentValue;
|
||||
_globalSettings = globalSettings.CurrentValue;
|
||||
globalSettings.OnChange(x=>_globalSettings = x);
|
||||
connectionStrings.OnChange(x=>_connectionStrings = x);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Enabled => _connectionStrings.CurrentValue.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.CurrentValue.ProviderName, Constants.ProviderName, StringComparison.InvariantCultureIgnoreCase);
|
||||
public bool Enabled => _connectionStrings.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.ProviderName, Constants.ProviderName, StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
// With journal_mode=wal we can always read a snapshot.
|
||||
public IDistributedLock ReadLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingReadLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingReadLockDefaultTimeout;
|
||||
return new SqliteDistributedLock(this, lockId, DistributedLockType.ReadLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
// With journal_mode=wal only a single write transaction can exist at a time.
|
||||
public IDistributedLock WriteLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingWriteLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingWriteLockDefaultTimeout;
|
||||
return new SqliteDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,13 +30,13 @@
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildBelle">
|
||||
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Client\" Command="npm ci --no-fund --no-audit --prefer-offline" />
|
||||
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Client\" Command="npm run build:skip-tests" />
|
||||
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Client\" Command="npm ci --no-fund --no-audit --prefer-offline" Timeout="600000" />
|
||||
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Client\" Command="npm run build:skip-tests" Timeout="600000" />
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildLogin">
|
||||
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Login\" Command="npm ci --no-fund --no-audit --prefer-offline" />
|
||||
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Login\" Command="npm run build" />
|
||||
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Login\" Command="npm ci --no-fund --no-audit --prefer-offline" Timeout="600000" />
|
||||
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Login\" Command="npm run build" Timeout="600000" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanStaticAssetsPreconditions" AfterTargets="Clean" Condition="'$(UmbracoBuild)' == ''">
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<!-- Generate JSON schema on build (and before copying to project) -->
|
||||
<Target Name="GenerateAppsettingsSchema" BeforeTargets="Build;CopyUmbracoJsonSchemaFiles" Condition="!Exists('$(_UmbracoCmsJsonSchemaReference)')">
|
||||
<Message Text="Generating $(_UmbracoCmsJsonSchemaReference) because it doesn't exist" Importance="high" />
|
||||
<Exec WorkingDirectory="$(MSBuildThisFileDirectory)..\..\tools\Umbraco.JsonSchema" Command="dotnet run --configuration $(Configuration) -- --outputFile "$(MSBuildThisFileDirectory)$(_UmbracoCmsJsonSchemaReference)"" />
|
||||
<Exec WorkingDirectory="$(MSBuildThisFileDirectory)..\..\tools\Umbraco.JsonSchema" Command="dotnet run --configuration $(Configuration) -- --outputFile "$(MSBuildThisFileDirectory)$(_UmbracoCmsJsonSchemaReference)"" Timeout="600000" />
|
||||
</Target>
|
||||
|
||||
<!-- Remove generated JSON schema on clean -->
|
||||
|
||||
@@ -43,7 +43,7 @@ public static class AppCacheExtensions
|
||||
public static T? GetCacheItem<T>(this IAppCache provider, string cacheKey)
|
||||
{
|
||||
var result = provider.Get(cacheKey);
|
||||
if (result == null)
|
||||
if (IsRetrievedItemNull(result))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
@@ -54,11 +54,13 @@ public static class AppCacheExtensions
|
||||
public static T? GetCacheItem<T>(this IAppCache provider, string cacheKey, Func<T> getCacheItem)
|
||||
{
|
||||
var result = provider.Get(cacheKey, () => getCacheItem());
|
||||
if (result == null)
|
||||
if (IsRetrievedItemNull(result))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return result.TryConvertTo<T>().Result;
|
||||
}
|
||||
|
||||
private static bool IsRetrievedItemNull(object? result) => result is null or (object)Cms.Core.Constants.Cache.NullRepresentationInCache;
|
||||
}
|
||||
|
||||
@@ -22,4 +22,6 @@ public static class CacheKeys
|
||||
|
||||
public const string ContentRecycleBinCacheKey = "recycleBin_content";
|
||||
public const string MediaRecycleBinCacheKey = "recycleBin_media";
|
||||
|
||||
public const string MemberUserNameCachePrefix = "uRepo_userNameKey+";
|
||||
}
|
||||
|
||||
@@ -142,7 +142,9 @@ public static class DistributedCacheExtensions
|
||||
Id = x.Item.Id,
|
||||
Key = x.Item.Key,
|
||||
ChangeTypes = x.ChangeTypes,
|
||||
Blueprint = x.Item.Blueprint
|
||||
Blueprint = x.Item.Blueprint,
|
||||
PublishedCultures = x.PublishedCultures?.ToArray(),
|
||||
UnpublishedCultures = x.UnpublishedCultures?.ToArray()
|
||||
});
|
||||
|
||||
dc.RefreshByPayload(ContentCacheRefresher.UniqueId, payloads);
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
namespace Umbraco.Cms.Core.Cache.PartialViewCacheInvalidators;
|
||||
|
||||
/// <summary>
|
||||
/// Defines behaviours for clearing of cached partials views that are configured to be cached individually by member.
|
||||
/// </summary>
|
||||
public interface IMemberPartialViewCacheInvalidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Clears the partial view cache items for the specified member ids.
|
||||
/// </summary>
|
||||
/// <param name="memberIds">The member Ids to clear the cache for.</param>
|
||||
/// <remarks>
|
||||
/// Called from the <see cref="MemberCacheRefresher"/> when a member is saved or deleted.
|
||||
/// </remarks>
|
||||
void ClearPartialViewCacheItems(IEnumerable<int> memberIds);
|
||||
}
|
||||
@@ -182,6 +182,10 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase<ContentCac
|
||||
public TreeChangeTypes ChangeTypes { get; init; }
|
||||
|
||||
public bool Blueprint { get; init; }
|
||||
|
||||
public string[]? PublishedCultures { get; init; }
|
||||
|
||||
public string[]? UnpublishedCultures { get; init; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// using Newtonsoft.Json;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Cache.PartialViewCacheInvalidators;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
@@ -15,10 +18,37 @@ public sealed class MemberCacheRefresher : PayloadCacheRefresherBase<MemberCache
|
||||
public static readonly Guid UniqueId = Guid.Parse("E285DF34-ACDC-4226-AE32-C0CB5CF388DA");
|
||||
|
||||
private readonly IIdKeyMap _idKeyMap;
|
||||
private readonly IMemberPartialViewCacheInvalidator _memberPartialViewCacheInvalidator;
|
||||
|
||||
public MemberCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IIdKeyMap idKeyMap, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, serializer, eventAggregator, factory) =>
|
||||
[Obsolete("Use the non obsoleted constructor instead. Scheduled for removal in v17")]
|
||||
public MemberCacheRefresher(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer serializer,
|
||||
IIdKeyMap idKeyMap,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheRefresherNotificationFactory factory)
|
||||
: this(
|
||||
appCaches,
|
||||
serializer,
|
||||
idKeyMap,
|
||||
eventAggregator,
|
||||
factory,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IMemberPartialViewCacheInvalidator>())
|
||||
{
|
||||
}
|
||||
|
||||
public MemberCacheRefresher(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer serializer,
|
||||
IIdKeyMap idKeyMap,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheRefresherNotificationFactory factory,
|
||||
IMemberPartialViewCacheInvalidator memberPartialViewCacheInvalidator)
|
||||
: base(appCaches, serializer, eventAggregator, factory)
|
||||
{
|
||||
_idKeyMap = idKeyMap;
|
||||
_memberPartialViewCacheInvalidator = memberPartialViewCacheInvalidator;
|
||||
}
|
||||
|
||||
#region Indirect
|
||||
|
||||
@@ -67,17 +97,30 @@ public sealed class MemberCacheRefresher : PayloadCacheRefresherBase<MemberCache
|
||||
|
||||
private void ClearCache(params JsonPayload[] payloads)
|
||||
{
|
||||
AppCaches.ClearPartialViewCache();
|
||||
// Clear the partial views cache for all partials that are cached by member, for the updates members.
|
||||
_memberPartialViewCacheInvalidator.ClearPartialViewCacheItems(payloads.Select(p => p.Id));
|
||||
|
||||
Attempt<IAppPolicyCache?> memberCache = AppCaches.IsolatedCaches.Get<IMember>();
|
||||
|
||||
foreach (JsonPayload p in payloads)
|
||||
{
|
||||
_idKeyMap.ClearCache(p.Id);
|
||||
if (memberCache.Success)
|
||||
if (memberCache.Success is false)
|
||||
{
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, int>(p.Id));
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(p.Username));
|
||||
continue;
|
||||
}
|
||||
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, int>(p.Id));
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(p.Username));
|
||||
|
||||
// This specific cache key was introduced to fix an issue where the member username could not be the same as the member id, because the cache keys collided.
|
||||
// This is done in a bit of a hacky way, because the cache key is created internally in the repository, but we need to clear it here.
|
||||
// Ideally, we want to use a shared way of generating the key between this and the repository.
|
||||
// Additionally, the RepositoryCacheKeys actually caches the string to avoid re-allocating memory; we would like to also use this in the repository
|
||||
// See:
|
||||
// https://github.com/umbraco/Umbraco-CMS/pull/17350
|
||||
// https://github.com/umbraco/Umbraco-CMS/pull/17815
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(CacheKeys.MemberUserNameCachePrefix + p.Username));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ public class RepositoryCachePolicyOptions
|
||||
public RepositoryCachePolicyOptions(Func<int> performCount)
|
||||
{
|
||||
PerformCount = performCount;
|
||||
CacheNullValues = false;
|
||||
GetAllCacheValidateCount = true;
|
||||
GetAllCacheAllowZeroCount = false;
|
||||
}
|
||||
@@ -21,6 +22,7 @@ public class RepositoryCachePolicyOptions
|
||||
public RepositoryCachePolicyOptions()
|
||||
{
|
||||
PerformCount = null;
|
||||
CacheNullValues = false;
|
||||
GetAllCacheValidateCount = false;
|
||||
GetAllCacheAllowZeroCount = false;
|
||||
}
|
||||
@@ -30,6 +32,11 @@ public class RepositoryCachePolicyOptions
|
||||
/// </summary>
|
||||
public Func<int>? PerformCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the Get method will cache null results so that the db is not hit for repeated lookups
|
||||
/// </summary>
|
||||
public bool CacheNullValues { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True/false as to validate the total item count when all items are returned from cache, the default is true but this
|
||||
/// means that a db lookup will occur - though that lookup will probably be significantly less expensive than the
|
||||
|
||||
@@ -34,7 +34,7 @@ public class TypeFinder : ITypeFinder
|
||||
"ServiceStack.", "SqlCE4Umbraco,", "Superpower,", // used by Serilog
|
||||
"System.", "TidyNet,", "TidyNet.", "WebDriver,", "itextsharp,", "mscorlib,", "NUnit,", "NUnit.", "NUnit3.",
|
||||
"Selenium.", "ImageProcessor", "MiniProfiler.", "Owin,", "SQLite",
|
||||
"ReSharperTestRunner", "ReSharperTestRunner32", "ReSharperTestRunner64", // These are used by the Jetbrains Rider IDE and Visual Studio ReSharper Extension
|
||||
"ReSharperTestRunner", "ReSharperTestRunner32", "ReSharperTestRunner64", "ReSharperTestRunnerArm32", "ReSharperTestRunnerArm64", // These are used by the Jetbrains Rider IDE and Visual Studio ReSharper Extension
|
||||
};
|
||||
|
||||
private static readonly ConcurrentDictionary<string, Type?> TypeNamesCache = new();
|
||||
|
||||
@@ -160,6 +160,7 @@ public class ContentSettings
|
||||
internal const bool StaticDisableUnpublishWhenReferenced = false;
|
||||
internal const bool StaticAllowEditInvariantFromNonDefault = false;
|
||||
internal const bool StaticShowDomainWarnings = true;
|
||||
internal const bool StaticShowUnroutableContentWarnings = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value for the content notification settings.
|
||||
@@ -285,4 +286,10 @@ public class ContentSettings
|
||||
/// </summary>
|
||||
[DefaultValue(StaticShowDomainWarnings)]
|
||||
public bool ShowDomainWarnings { get; set; } = StaticShowDomainWarnings;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show unroutable content warnings.
|
||||
/// </summary>
|
||||
[DefaultValue(StaticShowUnroutableContentWarnings)]
|
||||
public bool ShowUnroutableContentWarnings { get; set; } = StaticShowUnroutableContentWarnings;
|
||||
}
|
||||
|
||||
@@ -129,6 +129,7 @@ public class GlobalSettings
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to install the database when it is missing.
|
||||
/// </summary>
|
||||
[Obsolete("This option will be removed in V16.")]
|
||||
[DefaultValue(StaticInstallMissingDatabase)]
|
||||
public bool InstallMissingDatabase { get; set; } = StaticInstallMissingDatabase;
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ public class ModelsBuilderSettings
|
||||
internal const string StaticModelsDirectory = "~/umbraco/models";
|
||||
internal const bool StaticAcceptUnsafeModelsDirectory = false;
|
||||
internal const int StaticDebugLevel = 0;
|
||||
internal const bool StaticIncludeVersionNumberInGeneratedModels = true;
|
||||
private bool _flagOutOfDateModels = true;
|
||||
|
||||
/// <summary>
|
||||
@@ -78,4 +79,16 @@ public class ModelsBuilderSettings
|
||||
/// <remarks>0 means minimal (safe on live site), anything else means more and more details (maybe not safe).</remarks>
|
||||
[DefaultValue(StaticDebugLevel)]
|
||||
public int DebugLevel { get; set; } = StaticDebugLevel;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the version number should be included in generated models.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// By default this is written to the <see cref="System.CodeDom.Compiler.GeneratedCodeAttribute"/> output in
|
||||
/// generated code for each property of the model. This can be useful for debugging purposes but isn't essential,
|
||||
/// and it has the causes the generated code to change every time Umbraco is upgraded. In turn, this leads
|
||||
/// to unnecessary code file changes that need to be checked into source control. Default is <c>true</c>.
|
||||
/// </remarks>
|
||||
[DefaultValue(StaticIncludeVersionNumberInGeneratedModels)]
|
||||
public bool IncludeVersionNumberInGeneratedModels { get; set; } = StaticIncludeVersionNumberInGeneratedModels;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ public class RequestHandlerSettings
|
||||
{
|
||||
internal const bool StaticAddTrailingSlash = true;
|
||||
internal const string StaticConvertUrlsToAscii = "try";
|
||||
internal const string StaticConvertFileNamesToAscii = "false";
|
||||
internal const bool StaticEnableDefaultCharReplacements = true;
|
||||
|
||||
internal static readonly CharItem[] DefaultCharCollection =
|
||||
@@ -73,6 +74,22 @@ public class RequestHandlerSettings
|
||||
/// </summary>
|
||||
public bool ShouldTryConvertUrlsToAscii => ConvertUrlsToAscii.InvariantEquals("try");
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to convert file names to ASCII (valid values: "true", "try" or "false").
|
||||
/// </summary>
|
||||
[DefaultValue(StaticConvertFileNamesToAscii)]
|
||||
public string ConvertFileNamesToAscii { get; set; } = StaticConvertFileNamesToAscii;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether URLs should be converted to ASCII.
|
||||
/// </summary>
|
||||
public bool ShouldConvertFileNamesToAscii => ConvertFileNamesToAscii.InvariantEquals("true");
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether URLs should be tried to be converted to ASCII.
|
||||
/// </summary>
|
||||
public bool ShouldTryConvertFileNamesToAscii => ConvertFileNamesToAscii.InvariantEquals("try");
|
||||
|
||||
/// <summary>
|
||||
/// Disable all default character replacements
|
||||
/// </summary>
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Umbraco.Cms.Core.Configuration.Models;
|
||||
public class RichTextEditorSettings
|
||||
{
|
||||
internal const string StaticValidElements =
|
||||
"+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-s[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],figure,figcaption,cite,video[*],audio[*],picture[*],source[*],canvas[*]";
|
||||
"+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-s[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],figure,figcaption,cite,video[*],audio[*],picture[*],source[*],canvas[*],code";
|
||||
|
||||
internal const string StaticInvalidElements = "font";
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Umbraco.Cms.Core.Configuration.Models;
|
||||
|
||||
@@ -19,12 +20,16 @@ public class SecuritySettings
|
||||
internal const bool StaticAllowEditInvariantFromNonDefault = false;
|
||||
internal const bool StaticAllowConcurrentLogins = false;
|
||||
internal const string StaticAuthCookieName = "UMB_UCONTEXT";
|
||||
internal const bool StaticUsernameIsEmail = true;
|
||||
internal const bool StaticMemberRequireUniqueEmail = true;
|
||||
|
||||
internal const string StaticAllowedUserNameCharacters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+\\";
|
||||
|
||||
internal const int StaticMemberDefaultLockoutTimeInMinutes = 30 * 24 * 60;
|
||||
internal const int StaticUserDefaultLockoutTimeInMinutes = 30 * 24 * 60;
|
||||
internal const long StaticUserDefaultFailedLoginDurationInMilliseconds = 1000;
|
||||
internal const long StaticUserMinimumFailedLoginDurationInMilliseconds = 250;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to keep the user logged in.
|
||||
@@ -58,7 +63,14 @@ public class SecuritySettings
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the user's email address is to be considered as their username.
|
||||
/// </summary>
|
||||
public bool UsernameIsEmail { get; set; } = true;
|
||||
[DefaultValue(StaticUsernameIsEmail)]
|
||||
public bool UsernameIsEmail { get; set; } = StaticUsernameIsEmail;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the member's email address must be unique.
|
||||
/// </summary>
|
||||
[DefaultValue(StaticMemberRequireUniqueEmail)]
|
||||
public bool MemberRequireUniqueEmail { get; set; } = StaticMemberRequireUniqueEmail;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the set of allowed characters for a username
|
||||
@@ -116,4 +128,28 @@ public class SecuritySettings
|
||||
/// </summary>
|
||||
[DefaultValue(StaticAllowConcurrentLogins)]
|
||||
public bool AllowConcurrentLogins { get; set; } = StaticAllowConcurrentLogins;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default duration (in milliseconds) of failed login attempts.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default duration (in milliseconds) of failed login attempts.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// The user login endpoint ensures that failed login attempts take at least as long as the average successful login.
|
||||
/// However, if no successful logins have occurred, this value is used as the default duration.
|
||||
/// </remarks>
|
||||
[Range(0, int.MaxValue)] // TODO (V17): Change property type to short and update maximum range to short.MaxValue
|
||||
[DefaultValue(StaticUserDefaultFailedLoginDurationInMilliseconds)]
|
||||
public long UserDefaultFailedLoginDurationInMilliseconds { get; set; } = StaticUserDefaultFailedLoginDurationInMilliseconds;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum duration (in milliseconds) of failed login attempts.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The minimum duration (in milliseconds) of failed login attempts.
|
||||
/// </value>
|
||||
[Range(0, int.MaxValue)] // TODO (V17): Change property type to short and update maximum range to short.MaxValue
|
||||
[DefaultValue(StaticUserMinimumFailedLoginDurationInMilliseconds)]
|
||||
public long UserMinimumFailedLoginDurationInMilliseconds { get; set; } = StaticUserMinimumFailedLoginDurationInMilliseconds;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Umbraco.Cms.Core;
|
||||
|
||||
public static partial class Constants
|
||||
{
|
||||
public static class Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the string used to represent a null value in the cache.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Used in conjunction with the option to cache null values on the repository caches, so we
|
||||
/// can distinguish a true null "not found" value and a cached null value.</remarks>
|
||||
public const string NullRepresentationInCache = "*NULL*";
|
||||
}
|
||||
}
|
||||
@@ -32,5 +32,14 @@ public static partial class Constants
|
||||
public static string BackofficeExternalLoginProviderCount = "BackofficeExternalLoginProviderCount";
|
||||
public static string DeliverApiEnabled = "DeliverApiEnabled";
|
||||
public static string DeliveryApiPublicAccess = "DeliveryApiPublicAccess";
|
||||
public static string WebhookPrefix = "WebhookCount_";
|
||||
public static string WebhookTotal = $"{WebhookPrefix}Total";
|
||||
public static string WebhookCustomHeaders = $"{WebhookPrefix}CustomHeaders";
|
||||
public static string WebhookCustomEvent = $"{WebhookPrefix}CustomEvent";
|
||||
public static string RichTextEditorCount = "RichTextEditorCount";
|
||||
public static string RichTextBlockCount = "RichTextBlockCount";
|
||||
public static string TotalPropertyCount = "TotalPropertyCount";
|
||||
public static string HighestPropertyCount = "HighestPropertyCount";
|
||||
public static string TotalCompositions = "TotalCompositions";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ public static partial class Constants
|
||||
public const string ControllerToken = "controller";
|
||||
public const string ActionToken = "action";
|
||||
public const string AreaToken = "area";
|
||||
public const string DynamicRoutePattern = "/{**umbracoSlug}";
|
||||
}
|
||||
|
||||
public static class RoutePath
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
|
||||
namespace Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
// NOTE: left unsealed on purpose so it is extendable.
|
||||
public class ApiContentPathProvider : IApiContentPathProvider
|
||||
{
|
||||
private readonly IPublishedUrlProvider _publishedUrlProvider;
|
||||
|
||||
public ApiContentPathProvider(IPublishedUrlProvider publishedUrlProvider)
|
||||
=> _publishedUrlProvider = publishedUrlProvider;
|
||||
|
||||
public virtual string? GetContentPath(IPublishedContent content, string? culture)
|
||||
=> _publishedUrlProvider.GetUrl(content, UrlMode.Relative, culture);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
// NOTE: left unsealed on purpose so it is extendable.
|
||||
public class ApiContentPathResolver : IApiContentPathResolver
|
||||
{
|
||||
private readonly IRequestRoutingService _requestRoutingService;
|
||||
private readonly IApiPublishedContentCache _apiPublishedContentCache;
|
||||
|
||||
public ApiContentPathResolver(IRequestRoutingService requestRoutingService, IApiPublishedContentCache apiPublishedContentCache)
|
||||
{
|
||||
_requestRoutingService = requestRoutingService;
|
||||
_apiPublishedContentCache = apiPublishedContentCache;
|
||||
}
|
||||
|
||||
[Obsolete("No longer used in V15. Scheduled for removal in V15.")]
|
||||
public virtual bool IsResolvablePath(string path)
|
||||
{
|
||||
// File requests will blow up with an downstream exception in GetRequiredPublishedSnapshot, which fails due to an UmbracoContext
|
||||
// not being available for what's considered a static file request.
|
||||
// See: https://github.com/umbraco/Umbraco-CMS/issues/19051
|
||||
// Given a URL segment and hence route can't contain a period, we can safely assume that if the last segment of the path contains
|
||||
// a period, it's a file request and should return null here.
|
||||
if (IsFileRequest(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsFileRequest(string path) => path
|
||||
.Split('/', StringSplitOptions.RemoveEmptyEntries)
|
||||
.LastOrDefault()?
|
||||
.Contains('.') is true;
|
||||
|
||||
public virtual IPublishedContent? ResolveContentPath(string path)
|
||||
{
|
||||
path = path.EnsureStartsWith("/");
|
||||
|
||||
var contentRoute = _requestRoutingService.GetContentRoute(path);
|
||||
IPublishedContent? contentItem = _apiPublishedContentCache.GetByRoute(contentRoute);
|
||||
return contentItem;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
using Umbraco.Cms.Core.Models.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
public sealed class ApiContentResponseBuilder : ApiContentBuilderBase<IApiContentResponse>, IApiContentResponseBuilder
|
||||
public class ApiContentResponseBuilder : ApiContentBuilderBase<IApiContentResponse>, IApiContentResponseBuilder
|
||||
{
|
||||
private readonly IApiContentRouteBuilder _apiContentRouteBuilder;
|
||||
|
||||
@@ -14,6 +13,12 @@ public sealed class ApiContentResponseBuilder : ApiContentBuilderBase<IApiConten
|
||||
=> _apiContentRouteBuilder = apiContentRouteBuilder;
|
||||
|
||||
protected override IApiContentResponse Create(IPublishedContent content, string name, IApiContentRoute route, IDictionary<string, object?> properties)
|
||||
{
|
||||
IDictionary<string, IApiContentRoute> cultures = GetCultures(content);
|
||||
return new ApiContentResponse(content.Key, name, content.ContentType.Alias, content.CreateDate, content.UpdateDate, route, properties, cultures);
|
||||
}
|
||||
|
||||
protected virtual IDictionary<string, IApiContentRoute> GetCultures(IPublishedContent content)
|
||||
{
|
||||
var routesByCulture = new Dictionary<string, IApiContentRoute>();
|
||||
|
||||
@@ -35,6 +40,6 @@ public sealed class ApiContentResponseBuilder : ApiContentBuilderBase<IApiConten
|
||||
routesByCulture[publishedCultureInfo.Culture] = cultureRoute;
|
||||
}
|
||||
|
||||
return new ApiContentResponse(content.Key, name, content.ContentType.Alias, content.CreateDate, content.UpdateDate, route, properties, routesByCulture);
|
||||
return routesByCulture;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
@@ -10,13 +12,14 @@ namespace Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder
|
||||
{
|
||||
private readonly IPublishedUrlProvider _publishedUrlProvider;
|
||||
private readonly IApiContentPathProvider _apiContentPathProvider;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IVariationContextAccessor _variationContextAccessor;
|
||||
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
|
||||
private readonly IRequestPreviewService _requestPreviewService;
|
||||
private RequestHandlerSettings _requestSettings;
|
||||
|
||||
[Obsolete($"Use the constructor that does not accept {nameof(IPublishedUrlProvider)}. Will be removed in V15.")]
|
||||
public ApiContentRouteBuilder(
|
||||
IPublishedUrlProvider publishedUrlProvider,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
@@ -24,8 +27,32 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder
|
||||
IPublishedSnapshotAccessor publishedSnapshotAccessor,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
IOptionsMonitor<RequestHandlerSettings> requestSettings)
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IApiContentPathProvider>(), globalSettings, variationContextAccessor, publishedSnapshotAccessor, requestPreviewService, requestSettings)
|
||||
{
|
||||
_publishedUrlProvider = publishedUrlProvider;
|
||||
}
|
||||
|
||||
[Obsolete($"Use the constructor that does not accept {nameof(IPublishedUrlProvider)}. Will be removed in V15.")]
|
||||
public ApiContentRouteBuilder(
|
||||
IPublishedUrlProvider publishedUrlProvider,
|
||||
IApiContentPathProvider apiContentPathProvider,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
IPublishedSnapshotAccessor publishedSnapshotAccessor,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
IOptionsMonitor<RequestHandlerSettings> requestSettings)
|
||||
: this(apiContentPathProvider, globalSettings, variationContextAccessor, publishedSnapshotAccessor, requestPreviewService, requestSettings)
|
||||
{
|
||||
}
|
||||
|
||||
public ApiContentRouteBuilder(
|
||||
IApiContentPathProvider apiContentPathProvider,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
IPublishedSnapshotAccessor publishedSnapshotAccessor,
|
||||
IRequestPreviewService requestPreviewService,
|
||||
IOptionsMonitor<RequestHandlerSettings> requestSettings)
|
||||
{
|
||||
_apiContentPathProvider = apiContentPathProvider;
|
||||
_variationContextAccessor = variationContextAccessor;
|
||||
_publishedSnapshotAccessor = publishedSnapshotAccessor;
|
||||
_requestPreviewService = requestPreviewService;
|
||||
@@ -72,7 +99,7 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder
|
||||
}
|
||||
|
||||
// grab the content path from the URL provider
|
||||
var contentPath = _publishedUrlProvider.GetUrl(content, UrlMode.Relative, culture);
|
||||
var contentPath = _apiContentPathProvider.GetContentPath(content, culture);
|
||||
|
||||
// in some scenarios the published content is actually routable, but due to the built-in handling of i.e. lacking culture setup
|
||||
// the URL provider resolves the content URL as empty string or "#". since the Delivery API handles routing explicitly,
|
||||
@@ -96,7 +123,7 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder
|
||||
|
||||
private string ContentPreviewPath(IPublishedContent content) => $"{Constants.DeliveryApi.Routing.PreviewContentPathPrefix}{content.Key:D}{(_requestSettings.AddTrailingSlash ? "/" : string.Empty)}";
|
||||
|
||||
private static bool IsInvalidContentPath(string path) => path.IsNullOrWhiteSpace() || "#".Equals(path);
|
||||
private static bool IsInvalidContentPath(string? path) => path.IsNullOrWhiteSpace() || "#".Equals(path);
|
||||
|
||||
private IPublishedContent GetRoot(IPublishedContent content, bool isPreview)
|
||||
{
|
||||
|
||||
@@ -17,6 +17,6 @@ public sealed class ApiMediaUrlProvider : IApiMediaUrlProvider
|
||||
throw new ArgumentException("Media URLs can only be generated from Media items.", nameof(media));
|
||||
}
|
||||
|
||||
return _publishedUrlProvider.GetMediaUrl(media, UrlMode.Relative);
|
||||
return _publishedUrlProvider.GetMediaUrl(media);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
public interface IApiContentPathProvider
|
||||
{
|
||||
string? GetContentPath(IPublishedContent content, string? culture);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
public interface IApiContentPathResolver
|
||||
{
|
||||
[Obsolete("No longer used in V15. Scheduled for removal in V15.")]
|
||||
bool IsResolvablePath(string path) => true;
|
||||
|
||||
IPublishedContent? ResolveContentPath(string path);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
public interface ICurrentMemberClaimsProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the claims for the currently logged in member.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used by the OIDC user info endpoint to supply "current user" info.
|
||||
/// </remarks>
|
||||
Task<Dictionary<string, object>> GetClaimsAsync();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
public class NoopCurrentMemberClaimsProvider : ICurrentMemberClaimsProvider
|
||||
{
|
||||
public Task<Dictionary<string, object>> GetClaimsAsync() => Task.FromResult(new Dictionary<string, object>());
|
||||
}
|
||||
@@ -126,7 +126,6 @@ public static partial class UmbracoBuilderExtensions
|
||||
// register OEmbed providers - no type scanning - all explicit opt-in of adding types, IEmbedProvider is not IDiscoverable
|
||||
builder.EmbedProviders()
|
||||
.Append<YouTube>()
|
||||
.Append<Twitter>()
|
||||
.Append<Vimeo>()
|
||||
.Append<DailyMotion>()
|
||||
.Append<Flickr>()
|
||||
@@ -138,7 +137,8 @@ public static partial class UmbracoBuilderExtensions
|
||||
.Append<Issuu>()
|
||||
.Append<Hulu>()
|
||||
.Append<Giphy>()
|
||||
.Append<LottieFiles>();
|
||||
.Append<LottieFiles>()
|
||||
.Append<X>();
|
||||
builder.SearchableTrees().Add(() => builder.TypeLoader.GetTypes<ISearchableTree>());
|
||||
builder.BackOfficeAssets();
|
||||
builder.SelectorHandlers().Add(() => builder.TypeLoader.GetTypes<ISelectorHandler>());
|
||||
|
||||
@@ -27,7 +27,9 @@ public class RootDynamicRootOriginFinder : IDynamicRootOriginFinder
|
||||
return null;
|
||||
}
|
||||
|
||||
var entity = _entityService.Get(query.Context.ParentKey);
|
||||
// when creating new content, CurrentKey will be null - fallback to using ParentKey
|
||||
Guid entityKey = query.Context.CurrentKey ?? query.Context.ParentKey;
|
||||
var entity = _entityService.Get(entityKey);
|
||||
|
||||
if (entity is null || _allowedObjectTypes.Contains(entity.NodeObjectType) is false)
|
||||
{
|
||||
|
||||
@@ -20,12 +20,14 @@ public class SiteDynamicRootOriginFinder : RootDynamicRootOriginFinder
|
||||
|
||||
public override Guid? FindOriginKey(DynamicRootNodeQuery query)
|
||||
{
|
||||
if (query.OriginAlias != SupportedOriginType || query.Context.CurrentKey.HasValue is false)
|
||||
if (query.OriginAlias != SupportedOriginType)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
IEntitySlim? entity = _entityService.Get(query.Context.CurrentKey.Value);
|
||||
// when creating new content, CurrentKey will be null - fallback to using ParentKey
|
||||
Guid entityKey = query.Context.CurrentKey ?? query.Context.ParentKey;
|
||||
IEntitySlim? entity = _entityService.Get(entityKey);
|
||||
if (entity is null || entity.NodeObjectType != Constants.ObjectTypes.Document)
|
||||
{
|
||||
return null;
|
||||
|
||||
@@ -459,9 +459,10 @@
|
||||
<key alias="confirmlogout">Ydych chi'n sicr?</key>
|
||||
<key alias="confirmSure">Ydych chi'n sicr?</key>
|
||||
<key alias="cut">Torri</key>
|
||||
<key alias="editdictionary">Golygu Eitem Geiriadur</key>
|
||||
<key alias="editlanguage">Golygu Iaith</key>
|
||||
<key alias="editDictionary">Golygu Eitem Geiriadur</key>
|
||||
<key alias="editLanguage">Golygu Iaith</key>
|
||||
<key alias="editSelectedMedia">Golygu cyfrwng a dewiswyd</key>
|
||||
<key alias="editWebhook">Golygu bachyn gwe</key>
|
||||
<key alias="insertAnchor">Mewnosod dolen leol</key>
|
||||
<key alias="insertCharacter">Mewnosod nod</key>
|
||||
<key alias="insertgraphicheadline">Mewnosod pennawd graffig</key>
|
||||
@@ -504,6 +505,7 @@
|
||||
<key alias="openInNewWindow">Agor y ddolen ddogfen mewn ffenestr neu tab newydd</key>
|
||||
<key alias="linkToMedia">Dolen i gyfrwng</key>
|
||||
<key alias="selectContentStartNode">Dewis nod cychwyn cynnwys</key>
|
||||
<key alias="selectEvent">Dewis digwyddiad</key>
|
||||
<key alias="selectMedia">Dewis cyfrwng</key>
|
||||
<key alias="selectMediaType">Dewis y math o gyfrwng</key>
|
||||
<key alias="selectIcon">Dewis eicon</key>
|
||||
@@ -857,6 +859,7 @@
|
||||
<key alias="umbracoInfo">Gwybodaeth Umbraco</key>
|
||||
<key alias="skipToMenu">Neidio i'r dewislen</key>
|
||||
<key alias="skipToContent">Neidio i'r cynnwys</key>
|
||||
<key alias="primary">Prif</key>
|
||||
</area>
|
||||
<area alias="colors">
|
||||
<key alias="blue">Glas</key>
|
||||
@@ -1061,6 +1064,7 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
|
||||
<key alias="instruction">Mewngofnodwch isod</key>
|
||||
<key alias="signInWith">Mewngofnodwch gyda</key>
|
||||
<key alias="timeout">Sesiwn wedi cyrraedd terfyn amser</key>
|
||||
<key alias="userFailedLogin">Wps! Mae mewngofnodi wedi methu. Gwiriwch eich manylion a thrio eto.</key>
|
||||
<key alias="bottomText"><![CDATA[<p style="text-align:right;">© 2001 - %0% <br /><a href="https://umbraco.com" style="text-decoration: none" target="_blank" rel="noopener">Umbraco.com</a></p> ]]></key>
|
||||
<key alias="forgottenPassword">Wedi anghofio eich cyfrinair?</key>
|
||||
<key alias="forgottenPasswordInstruction">Bydd ebost yn cael ei anfon i'r cyfeiriad darparwyd gyda dolen i ailosod eich cyfrinair</key>
|
||||
@@ -1424,6 +1428,44 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
|
||||
<key alias="allowedItemTypes">Gallwch ond ddewis eitemau o'r math(au): %0%</key>
|
||||
<key alias="pickedTrashedItem">Rydych wedi dewis eitem gynnwys sydd naill ai wedi'i ddileu neu yn y bin ailgylchu</key>
|
||||
<key alias="pickedTrashedItems">Rydych wedi dewis eitemau gynnwys sydd naill ai wedi'u dileu neu yn y bin ailgylchu</key>
|
||||
<key alias="specifyPickerRootTitle">Nodi gwraidd</key>
|
||||
<key alias="defineRootNode">Dewiswch nod gwraidd</key>
|
||||
<key alias="defineXPathOrigin">Nodi gwraidd efo XPath</key>
|
||||
<key alias="defineDynamicRoot">Nodi Gwraidd Dynamig</key>
|
||||
<key alias="configurationStartNodeTitle">Nod cychwyn</key>
|
||||
<key alias="configurationXPathTitle">Ymholiad XPath</key>
|
||||
</area>
|
||||
<area alias="dynamicRoot">
|
||||
<key alias="configurationTitle">Ymholiad Gwraidd Dynamig</key>
|
||||
<key alias="pickDynamicRootOriginTitle">Dewiswch darddiad</key>
|
||||
<key alias="pickDynamicRootOriginDesc">Diffiniwch darddiad eich Ymholiad Gwraidd Dynamig</key>
|
||||
<key alias="originRootTitle">Gwraidd</key>
|
||||
<key alias="originRootDesc">Nod gwraidd y sesiwn olygu hon</key>
|
||||
<key alias="originParentTitle">Rhiant</key>
|
||||
<key alias="originParentDesc">Nod rhiant y ffynhonnell yn y sesiwn olygu hon</key>
|
||||
<key alias="originCurrentTitle">Cyfredol</key>
|
||||
<key alias="originCurrentDesc">Y nod cynnwys sy'n ffynhonnell ar gyfer y sesiwn olygu hon</key>
|
||||
<key alias="originSiteTitle">Gwefan</key>
|
||||
<key alias="originSiteDesc">Canfod nod agosaf gydag enw gwesteiwr</key>
|
||||
<key alias="originByKeyTitle">Nod penodol</key>
|
||||
<key alias="originByKeyDesc">Dewiswch Nod penodol fel tarddiad yr ymholiad hwn</key>
|
||||
<key alias="pickDynamicRootQueryStepTitle">Atodwch y cam i'r ymholiad</key>
|
||||
<key alias="pickDynamicRootQueryStepDesc">Diffiniwch y cam nesaf yn eich Ymholiad Gwraidd Dynamig</key>
|
||||
<key alias="queryStepNearestAncestorOrSelfTitle">Hynafiad Agosaf Neu Hunan</key>
|
||||
<key alias="queryStepNearestAncestorOrSelfDesc">Holwch yr hynafiad agosaf neu hunan sy'n cyd-fynd ag un o'r mathau sydd wedi'u ffurfweddu</key>
|
||||
<key alias="queryStepFurthestAncestorOrSelfTitle">Hynafiad Pellaf Neu Hunan</key>
|
||||
<key alias="queryStepFurthestAncestorOrSelfDesc">Holwch yr hynafiad pellaf neu'r hunan sy'n cyd-fynd ag un o'r mathau sydd wedi'u ffurfweddu</key>
|
||||
<key alias="queryStepNearestDescendantOrSelfTitle">Disgynnydd Agosaf Neu Hunan</key>
|
||||
<key alias="queryStepNearestDescendantOrSelfDesc">Holwch y disgynnydd agosaf neu'r hunan sy'n cyd-fynd ag un o'r mathau sydd wedi'u ffurfweddu</key>
|
||||
<key alias="queryStepFurthestDescendantOrSelfTitle">Disgynnydd Pellaf Neu Hunan</key>
|
||||
<key alias="queryStepFurthestDescendantOrSelfDesc">Holwch y disgynnydd pellaf neu'r hunan sy'n cyd-fynd ag un o'r mathau sydd wedi'u ffurfweddu</key>
|
||||
<key alias="queryStepCustomTitle">Arferiad</key>
|
||||
<key alias="queryStepCustomDesc">Ymholiad gan ddefnyddio Cam Ymholiad arferiad</key>
|
||||
<key alias="addQueryStep">Ychwanegu cam ymholiad</key>
|
||||
<key alias="queryStepTypes">Sy'n cyfateb i'r mathau: </key>
|
||||
<key alias="noValidStartNodeTitle">Dim cynnwys cyfatebol</key>
|
||||
<key alias="noValidStartNodeDesc">Nid yw ffurfweddiad yr eiddo hwn yn cyfateb i unrhyw gynnwys. Creu'r cynnwys coll neu cysylltwch â'ch gweinyddwr i addasu gosodiadau Gwraidd Dynamig ar gyfer yr eiddo hwn.</key>
|
||||
<key alias="cancelAndClearQuery">Canslo a chlirio ymholiad</key>
|
||||
</area>
|
||||
<area alias="mediaPicker">
|
||||
<key alias="pickedTrashedItem">Rydych wedi dewis eitem gyfrwng sydd naill ai wedi'i ddileu neu yn y bin ailgylchu</key>
|
||||
@@ -1629,6 +1671,7 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
|
||||
<key alias="preventCleanupDisableError">Digwyddodd gwall wrth analluogi glanhau fersiwn ar gyfer %0%</key>
|
||||
<key alias="copySuccessMessage">Mae gwybodaeth eich system wedi'i chopïo'n llwyddiannus i'r clipfwrdd</key>
|
||||
<key alias="cannotCopyInformation">Methu â chopïo gwybodaeth eich system i'r clipfwrdd</key>
|
||||
<key alias="webhookSaved">Bachyn gwe wedi arbed</key>
|
||||
</area>
|
||||
<area alias="stylesheet">
|
||||
<key alias="addRule">Ychwanegu ardull</key>
|
||||
@@ -1873,6 +1916,33 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
|
||||
<key alias="historyCleanupGloballyDisabled"><strong>NODYN!</strong> Mae glanhau fersiynau cynnwys hanesyddol wedi'u hanalluogi'n fyd-eang. Ni fydd y gosodiadau hyn yn dod i rym cyn iddo gael ei alluogi.</key>
|
||||
<key alias="changeDataTypeHelpText">Mae newid math o ddata gyda gwerthoedd storio wedi'i analluogi. I ganiatáu hyn gallwch newid y gosodiad Umbraco:CMS:DataTypes:CanBeChanged yn appssettings.json.</key>
|
||||
</area>
|
||||
<area alias="webhooks">
|
||||
<key alias="addWebhook">Creu bachyn gwe</key>
|
||||
<key alias="addWebhookHeader">Ychwanegu pennyn bachyn gwe</key>
|
||||
<key alias="addDocumentType">Ychwanegu Math o Ddogfen</key>
|
||||
<key alias="addMediaType">Ychwanegu Math o Gyfrwng</key>
|
||||
<key alias="createHeader">Creu pennawd</key>
|
||||
<key alias="deliveries">Danfoniadau</key>
|
||||
<key alias="noHeaders">Nid oes penawdau bachyn gwe wedi'u hychwanegu</key>
|
||||
<key alias="noEventsFound">Ni chanfuwyd unrhyw ddigwyddiadau.</key>
|
||||
<key alias="enabled">Wedi'i alluogi</key>
|
||||
<key alias="events">Digwyddiadau</key>D
|
||||
<key alias="event">Digwyddiad</key>
|
||||
<key alias="url">Url</key>
|
||||
<key alias="types">Mathau</key>
|
||||
<key alias="webhookKey">Allwedd bachyn gwe</key>
|
||||
<key alias="retryCount">Cyfrif o Ailgeision</key>
|
||||
<key alias="toggleDebug">Togl modd dadfygio am ragor o wybodaeth.</key>
|
||||
<key alias="statusNotOk">Cod statws ddim yn OK</key>
|
||||
<key alias="urlDescription">Yr url i'w alw pan fydd y bachyn gwe yn cael ei sbarduno.</key>
|
||||
<key alias="eventDescription">Y digwyddiadau y dylid sbarduno'r bachyn gwe.</key>
|
||||
<key alias="contentTypeDescription">Sbardunwch y bachyn gwe am fath penodol o gynnwys yn unig.</key>
|
||||
<key alias="enabledDescription">A yw'r bachyn gwe wedi'i alluogi?</key>
|
||||
<key alias="headersDescription">Penawdau arferu i'w cynnwys yn y cais bachyn gwe.</key>
|
||||
<key alias="contentType">Math o Gynnwys</key>
|
||||
<key alias="headers">Penawdau</key>
|
||||
<key alias="selectEventFirst">Dewiswch ddigwyddiad yn gyntaf.</key>
|
||||
</area>
|
||||
<area alias="languages">
|
||||
<key alias="addLanguage">Ychwanegu iaith</key>
|
||||
<key alias="mandatoryLanguage">Iath gorfodol</key>
|
||||
@@ -2011,6 +2081,7 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
|
||||
<key alias="settingsGroup">Gosodiadau</key>
|
||||
<key alias="templatingGroup">Templedi</key>
|
||||
<key alias="thirdPartyGroup">Trydydd parti</key>
|
||||
<key alias="webhooks">Bachau gwe</key>
|
||||
</area>
|
||||
<area alias="update">
|
||||
<key alias="updateAvailable">Diweddariad newydd yn barod</key>
|
||||
@@ -2769,6 +2840,8 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang
|
||||
<key alias="configureArea">Ffurfweddu ardal</key>
|
||||
<key alias="deleteArea">Dileu ardal</key>
|
||||
<key alias="addColumnSpanOption">Ychwanegu opsiwn rhychwantu %0% colofn</key>
|
||||
<key alias="insertBlock">Mewnosod Bloc</key>
|
||||
<key alias="labelInlineMode">Arddangos yn fewnol â'r testun</key>
|
||||
</area>
|
||||
<area alias="contentTemplatesDashboard">
|
||||
<key alias="whatHeadline">Beth yw Templedi Gynnwys</key>
|
||||
|
||||
@@ -1469,6 +1469,7 @@ Mange hilsner fra Umbraco robotten
|
||||
<key alias="publishWithMissingDomain">Der er ikke noget domæne konfigureret for %0%, kontakt vensligst en
|
||||
administrator, se loggen for mere information
|
||||
</key>
|
||||
<key alias="publishWithNoUrl">Dokumentet har ikke nogen URL, muligvis grundet en kollision med et andet dokuments navn. Flere detaljer kan ses under Info.</key>
|
||||
<key alias="copySuccessMessage">Dit systems information er blevet kopieret til udklipsholderen</key>
|
||||
<key alias="cannotCopyInformation">Kunne desværre ikke kopiere dit systems information til udklipsholderen</key>
|
||||
<key alias="webhookSaved">Webhook gemt</key>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -167,6 +167,7 @@
|
||||
<key alias="move">Media moved</key>
|
||||
<key alias="copy">Media copied</key>
|
||||
<key alias="save">Media saved</key>
|
||||
<key alias="new">Media created</key>
|
||||
</area>
|
||||
<area alias="auditTrails">
|
||||
<key alias="atViewingFor">Viewing for</key>
|
||||
@@ -190,6 +191,7 @@
|
||||
<key alias="smallPublishVariant">Publish</key>
|
||||
<key alias="smallMove">Move</key>
|
||||
<key alias="smallSave">Save</key>
|
||||
<key alias="smallNew">New</key>
|
||||
<key alias="smallSaveVariant">Save</key>
|
||||
<key alias="smallDelete">Delete</key>
|
||||
<key alias="smallUnpublish">Unpublish</key>
|
||||
@@ -292,6 +294,7 @@
|
||||
Content.
|
||||
</key>
|
||||
<key alias="nestedContentDeleteAllItems">Are you sure you want to delete all items?</key>
|
||||
<key alias="nestedContentDeleteItem">Are you sure you want to delete this item?</key>
|
||||
<key alias="nestedContentNoContentTypes">No Content Types are configured for this property.</key>
|
||||
<key alias="nestedContentAddElementType">Add Element Type</key>
|
||||
<key alias="nestedContentSelectElementTypeModalTitle">Select Element Type</key>
|
||||
@@ -329,6 +332,7 @@
|
||||
<key alias="schedulePublishHelp">Select the date and time to publish and/or unpublish the content item.</key>
|
||||
<key alias="createEmpty">Create new</key>
|
||||
<key alias="createFromClipboard">Paste from clipboard</key>
|
||||
<key alias="removeItem">Remove item</key>
|
||||
<key alias="nodeIsInTrash">This item is in the Recycle Bin</key>
|
||||
<key alias="noProperties">No content can be added for this item</key>
|
||||
<key alias="variantSaveNotAllowed">Save is not allowed</key>
|
||||
@@ -336,6 +340,7 @@
|
||||
<key alias="variantSendForApprovalNotAllowed">Send for approval is not allowed</key>
|
||||
<key alias="variantScheduleNotAllowed">Schedule is not allowed</key>
|
||||
<key alias="variantUnpublishNotAllowed">Unpublish is not allowed</key>
|
||||
<key alias="selectAllVariants">Select all variants</key>
|
||||
</area>
|
||||
<area alias="blueprints">
|
||||
<key alias="createBlueprintFrom"><![CDATA[Create a new Content Template from <em>%0%</em>]]></key>
|
||||
@@ -1699,6 +1704,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
|
||||
<key alias="publishWithMissingDomain">There is no domain configured for %0%, please contact an administrator, see
|
||||
log for more information
|
||||
</key>
|
||||
<key alias="publishWithNoUrl">The document does not have a URL, possibly due to a naming collision with another document. More details can be found under Info.</key>
|
||||
<key alias="copySuccessMessage">Your system information has successfully been copied to the clipboard</key>
|
||||
<key alias="cannotCopyInformation">Could not copy your system information to the clipboard</key>
|
||||
<key alias="webhookSaved">Webhook saved</key>
|
||||
@@ -3076,7 +3082,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
|
||||
<key alias="detailedLevelDescription"><![CDATA[We will send:
|
||||
<ul>
|
||||
<li>Anonymized site ID, Umbraco version, and packages installed.</li>
|
||||
<li>Number of: Root nodes, Content nodes, Macros, Media, Document Types, Templates, Languages, Domains, User Group, Users, Members, Backoffice external login providers, and Property Editors in use.</li>
|
||||
<li>Number of: Root nodes, Content nodes, Macros, Media, Document Types, Property Types, Compositions, Templates, Languages, Domains, User Group, Users, Members, Backoffice external login providers, Webhooks, rich text datatypes, blocks used in rich text datatypes, and Property Editors in use.</li>
|
||||
<li>System information: Webserver, server OS, server framework, server OS language, and database provider.</li>
|
||||
<li>Configuration settings: Modelsbuilder mode, if custom Umbraco path exists, ASP environment, whether the delivery API is enabled, and allows public access, and if you are in debug mode.</li>
|
||||
</ul>
|
||||
|
||||
@@ -165,6 +165,7 @@
|
||||
<key alias="move">Media moved</key>
|
||||
<key alias="copy">Media copied</key>
|
||||
<key alias="save">Media saved</key>
|
||||
<key alias="new">Media created</key>
|
||||
</area>
|
||||
<area alias="auditTrails">
|
||||
<key alias="atViewingFor">Viewing for</key>
|
||||
@@ -189,6 +190,7 @@
|
||||
<key alias="smallPublishVariant">Publish</key>
|
||||
<key alias="smallMove">Move</key>
|
||||
<key alias="smallSave">Save</key>
|
||||
<key alias="smallNew">New</key>
|
||||
<key alias="smallSaveVariant">Save</key>
|
||||
<key alias="smallDelete">Delete</key>
|
||||
<key alias="smallUnpublish">Unpublish</key>
|
||||
@@ -288,6 +290,7 @@
|
||||
<![CDATA[<a href="https://docs.umbraco.com/umbraco-cms/fundamentals/data/scheduled-publishing#timezones" target="_blank" rel="noopener">What does this mean?</a>]]></key>
|
||||
<key alias="nestedContentDeleteItem">Are you sure you want to delete this item?</key>
|
||||
<key alias="nestedContentDeleteAllItems">Are you sure you want to delete all items?</key>
|
||||
<key alias="nestedContentDeleteItem">Are you sure you want to delete this item?</key>
|
||||
<key alias="nestedContentEditorNotSupported">Property %0% uses editor %1% which is not supported by Nested
|
||||
Content.
|
||||
</key>
|
||||
@@ -328,12 +331,14 @@
|
||||
<key alias="schedulePublishHelp">Select the date and time to publish and/or unpublish the content item.</key>
|
||||
<key alias="createEmpty">Create new</key>
|
||||
<key alias="createFromClipboard">Paste from clipboard</key>
|
||||
<key alias="removeItem">Remove item</key>
|
||||
<key alias="nodeIsInTrash">This item is in the Recycle Bin</key>
|
||||
<key alias="variantSaveNotAllowed">Save is not allowed</key>
|
||||
<key alias="variantPublishNotAllowed">Publish is not allowed</key>
|
||||
<key alias="variantSendForApprovalNotAllowed">Send for approval is not allowed</key>
|
||||
<key alias="variantScheduleNotAllowed">Schedule is not allowed</key>
|
||||
<key alias="variantUnpublishNotAllowed">Unpublish is not allowed</key>
|
||||
<key alias="selectAllVariants">Select all variants</key>
|
||||
</area>
|
||||
<area alias="blueprints">
|
||||
<key alias="createBlueprintFrom"><![CDATA[Create a new Content Template from <em>%0%</em>]]></key>
|
||||
@@ -1731,6 +1736,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
|
||||
<key alias="publishWithMissingDomain">There is no domain configured for %0%, please contact an administrator, see
|
||||
log for more information
|
||||
</key>
|
||||
<key alias="publishWithNoUrl">The document does not have a URL, possibly due to a naming collision with another document. More details can be found under Info.</key>
|
||||
<key alias="preventCleanupEnableError">An error occurred while enabling version cleanup for %0%</key>
|
||||
<key alias="preventCleanupDisableError">An error occurred while disabling version cleanup for %0%</key>
|
||||
<key alias="copySuccessMessage">Your system information has successfully been copied to the clipboard</key>
|
||||
@@ -2034,7 +2040,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
|
||||
<key alias="webhookKey">Webhook key</key>
|
||||
<key alias="retryCount">Retry count</key>
|
||||
<key alias="toggleDebug">Toggle debug mode for more information.</key>
|
||||
<key alias="statusNotOk">Not OK status code</key>
|
||||
<key alias="statusNotOk">Not OK status code</key>
|
||||
<key alias="urlDescription">The url to call when the webhook is triggered.</key>
|
||||
<key alias="eventDescription">The events for which the webhook should be triggered.</key>
|
||||
<key alias="contentTypeDescription">Only trigger the webhook for a specific content type.</key>
|
||||
@@ -3095,7 +3101,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
|
||||
We will send:
|
||||
<ul>
|
||||
<li>Anonymized site ID, Umbraco version, and packages installed.</li>
|
||||
<li>Number of: Root nodes, Content nodes, Macros, Media, Document Types, Templates, Languages, Domains, User Group, Users, Members, Backoffice external login providers, and Property Editors in use.</li>
|
||||
<li>Number of: Root nodes, Content nodes, Macros, Media, Document Types, Property Types, Compositions, Templates, Languages, Domains, User Group, Users, Members, Backoffice external login providers, Webhooks, rich text datatypes, blocks used in rich text datatypes, and Property Editors in use.</li>
|
||||
<li>System information: Webserver, server OS, server framework, server OS language, and database provider.</li>
|
||||
<li>Configuration settings: Modelsbuilder mode, if custom Umbraco path exists, ASP environment, whether the delivery API is enabled, and allows public access, and if you are in debug mode.</li>
|
||||
</ul>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,9 +4,6 @@
|
||||
<name>The Umbraco community</name>
|
||||
<link>https://docs.umbraco.com/umbraco-cms/extending/language-files</link>
|
||||
</creator>
|
||||
<area alias="apps">
|
||||
<key alias="umbContent">Innehåll</key>
|
||||
</area>
|
||||
<area alias="actions">
|
||||
<key alias="assigndomain">Hantera domännamn</key>
|
||||
<key alias="auditTrail">Hantera versioner</key>
|
||||
@@ -14,13 +11,16 @@
|
||||
<key alias="changeDocType">Ändra dokumenttyp</key>
|
||||
<key alias="copy">Kopiera</key>
|
||||
<key alias="create">Skapa</key>
|
||||
<key alias="export">Exportera</key>
|
||||
<key alias="createGroup">Skapa grupp</key>
|
||||
<key alias="createPackage">Skapa paket</key>
|
||||
<key alias="createblueprint">Skapa innehållsmall</key>
|
||||
<key alias="defaultValue">Standardvärde</key>
|
||||
<key alias="delete">Ta bort</key>
|
||||
<key alias="disable">Avaktivera</key>
|
||||
<key alias="editSettings">Ändra inställningar</key>
|
||||
<key alias="emptyrecyclebin">Töm papperskorgen</key>
|
||||
<key alias="enable">Aktivera</key>
|
||||
<key alias="exportDocumentType">Exportera dokumenttyp</key>
|
||||
<key alias="importdocumenttype">Importera dokumenttyp</key>
|
||||
<key alias="importPackage">Importera paket</key>
|
||||
@@ -40,6 +40,13 @@
|
||||
<key alias="translate">Översätt</key>
|
||||
<key alias="unpublish">Avpublicera</key>
|
||||
<key alias="update">Uppdatera</key>
|
||||
<key alias="setPermissions">Ändra rättigheter</key>
|
||||
<key alias="unlock">Lås upp</key>
|
||||
<key alias="createblueprint">Skapa innehållsmall</key>
|
||||
<key alias="resendInvite">Skicka inbjudan igen</key>
|
||||
<key alias="toggleHideUnavailable">Dölj otillgängliga alternativ</key>
|
||||
<key alias="changeDataType">Ändra datatyp</key>
|
||||
<key alias="editContent">Redigera innehåll</key>
|
||||
</area>
|
||||
<area alias="actionCategories">
|
||||
<key alias="administration">Administration</key>
|
||||
@@ -47,6 +54,10 @@
|
||||
<key alias="other">Övrigt</key>
|
||||
<key alias="content">Innehåll</key>
|
||||
</area>
|
||||
<area alias="apps">
|
||||
<key alias="umbContent">Innehåll</key>
|
||||
<key alias="umbInfo">Info</key>
|
||||
</area>
|
||||
<area alias="assignDomain">
|
||||
<key alias="addNew">Lägg till nytt domännamn</key>
|
||||
<key alias="domain">Domännamn</key>
|
||||
@@ -113,8 +124,21 @@
|
||||
<key alias="areaValidationEntriesExceed"><![CDATA[<strong>%0%</strong> måste maximalt finnas <strong>%3%</strong> time(s).]]></key>
|
||||
</area>
|
||||
<area alias="validation">
|
||||
<key alias="invalidEmail">Ogiltig e-postadress</key>
|
||||
<key alias="invalidNull">Värde får inte vara 'null'</key>
|
||||
<key alias="invalidEmpty">Värde får inte vara tomt</key>
|
||||
<key alias="invalidPattern">Värdet är ogiltigt och matchar inte det korrekta formatet</key>
|
||||
<key alias="entriesShort"><![CDATA[Minsta %0% poster, kräver <strong>%1%</strong> mer.]]></key>
|
||||
<key alias="entriesExceed"><![CDATA[Max %0% poster, <strong>%1%</strong> för många.]]></key>
|
||||
<key alias="entriesAreasMismatch">Innehållskraven har ej godkänts på ett eller flera områden.</key>
|
||||
<key alias="invalidMemberGroupName">Ogiltigt namn för medlemsgrupp</key>
|
||||
<key alias="invalidUserGroupName">Ogiltigt namn för användargrupp</key>
|
||||
<key alias="invalidToken">Ogiltig token</key>
|
||||
<key alias="invalidUsername">Ogiltigt användarnamn</key>
|
||||
<key alias="duplicateEmail">E-postadressen '%0%' är redan tagen</key>
|
||||
<key alias="duplicateUserGroupName">Namnet '%0%' är redan taget</key>
|
||||
<key alias="duplicateMemberGroupName">Namnet '%0%' är redan taget</key>
|
||||
<key alias="duplicateUsername">Användarnamnet '%0%' är redan taget</key>
|
||||
</area>
|
||||
<area alias="blueprints">
|
||||
<key alias="createBlueprintFrom"><![CDATA[Skapa en ny innehållsmall för <em>%0%</em>]]></key>
|
||||
@@ -176,6 +200,7 @@
|
||||
<key alias="clickToEdit">Klicka för att redigera detta objekt</key>
|
||||
<key alias="createBy">Skapad av</key>
|
||||
<key alias="createByDesc">Ursprunglig författare</key>
|
||||
<key alias="updatedBy" version="7.0">Uppdaterad av</key>
|
||||
<key alias="createDate">Skapad</key>
|
||||
<key alias="createDateDesc">Datum/tid som dokumentet skapades</key>
|
||||
<key alias="documentType">Dokumenttyp</key>
|
||||
@@ -559,6 +584,29 @@
|
||||
<key alias="retrieve">Hämta</key>
|
||||
<key alias="selected">valda</key>
|
||||
</area>
|
||||
<area alias="colors">
|
||||
<key alias="blue">Blå</key>
|
||||
</area>
|
||||
<area alias="shortcuts">
|
||||
<key alias="addGroup">Lägg till grupp</key>
|
||||
<key alias="addProperty">Lägg till egenskap</key>
|
||||
<key alias="addEditor">Lägg till redigerare</key>
|
||||
<key alias="addTemplate">Lägg till mall</key>
|
||||
<key alias="editDataType">Ändra datatyp</key>
|
||||
<key alias="shortcut">Genvägar</key>
|
||||
<key alias="showShortcuts">visa genvägar</key>
|
||||
<key alias="toggleListView">Toggle list view</key>
|
||||
<key alias="toggleAllowAsRoot">Toggle allow as root</key>
|
||||
<key alias="commentLine">Kommentera bort/återställ rader</key>
|
||||
<key alias="removeLine">Radera rad</key>
|
||||
<key alias="copyLineUp">Kopiera upp rader</key>
|
||||
<key alias="copyLineDown">Kopiera ned rader</key>
|
||||
<key alias="moveLineUp">Flytta upp rader</key>
|
||||
<key alias="moveLineDown">Flytta ned rader</key>
|
||||
<key alias="generalHeader">Allmänt</key>
|
||||
<key alias="editorHeader">Redigerare</key>
|
||||
<key alias="addTab">Lägg till tabb</key>
|
||||
</area>
|
||||
<area alias="graphicheadline">
|
||||
<key alias="backgroundcolor">Bakgrundsfärg</key>
|
||||
<key alias="bold">Fetstil</key>
|
||||
@@ -661,6 +709,11 @@
|
||||
<key alias="createNewMember">Skapa en ny medlem</key>
|
||||
<key alias="allMembers">Alla medlemmar</key>
|
||||
<key alias="memberGroupNoProperties">Medlemsgrupper har inga extra egenskaper för redigering.</key>
|
||||
<key alias="duplicateMemberLogin">En medlem med detta inlogg existerar redan</key>
|
||||
<key alias="memberHasGroup">Medlem finns redan i gruppen '%0%'</key>
|
||||
<key alias="memberHasPassword">Medlem har redan ett lösenord</key>
|
||||
<key alias="memberLockoutNotEnabled">Avstängning är inte aktiverat för denna medlem</key>
|
||||
<key alias="memberNotInGroup">Medlem är inte med i gruppen '%0%'</key>
|
||||
</area>
|
||||
<area alias="moveOrCopy">
|
||||
<key alias="choose">Välj sida ovan...</key>
|
||||
@@ -1087,9 +1140,45 @@
|
||||
<key alias="sortCreateDateAscending">Äldst</key>
|
||||
<key alias="sortCreateDateDescending">Nyast</key>
|
||||
<key alias="sortLastLoginDateDescending">Senaste login</key>
|
||||
<key alias="duplicateLogin">En användare med detta inlogg existerar redan</key>
|
||||
<key alias="passwordRequiresDigit">Lösenordet måste innehålla minst en siffra ('0'-'9')</key>
|
||||
<key alias="passwordRequiresLower">Lösenordet måste innehålla minst en liten bokstav ('a'-'z')</key>
|
||||
<key alias="passwordRequiresNonAlphanumeric">Lösenordet måste innehålla minst ett specialtecken</key>
|
||||
<key alias="passwordRequiresUniqueChars">Lösenordet måste innehålla minst %0% unika tecken</key>
|
||||
<key alias="passwordRequiresUpper">Lösenordet måste innehålla minst en stor bokstav ('A'-'Z')</key>
|
||||
<key alias="passwordTooShort">Lösenordet måste vara minst %0% tecken</key>
|
||||
<key alias="userHasPassword">Användaren har redan ett lösenord</key>
|
||||
<key alias="userHasGroup">Användaren finns redan i gruppen '%0%'</key>
|
||||
<key alias="userLockoutNotEnabled">Avstängning är inte aktiverat för denna användaren</key>
|
||||
<key alias="userNotInGroup">Användaren finns inte i gruppen '%0%'</key>
|
||||
</area>
|
||||
<area alias="logViewer">
|
||||
<key alias="selectAllLogLevelFilters">Välj alla</key>
|
||||
<key alias="deselectAllLogLevelFilters">Avmarkera alla</key>
|
||||
</area>
|
||||
<area alias="preview">
|
||||
<key alias="endLabel">Avsluta</key>
|
||||
<key alias="endTitle">Avsluta förhandsvisning</key>
|
||||
<key alias="openWebsiteLabel">Förhandsvisa webbplats</key>
|
||||
<key alias="openWebsiteTitle">Öppna webbplats i förhandsvisningsläge</key>
|
||||
<key alias="returnToPreviewHeadline">Förhandsvisa webbplats?</key>
|
||||
<key alias="returnToPreviewDescription">Du har avslutat förhandsvisningsläge, vill du aktivera det igen för att se senast sparade version av webbplatsen?</key>
|
||||
<key alias="returnToPreviewAcceptButton">Förhandsvisa senaste version</key>
|
||||
<key alias="returnToPreviewDeclineButton">Visa publicerad version</key>
|
||||
<key alias="viewPublishedContentHeadline">Visa publicerad version?</key>
|
||||
<key alias="viewPublishedContentDescription">Du visar webbplats i förhandsvisningsläge, vill du avsluta förhandsvisning och visa senaste publicerad version av webbplatsen?
|
||||
</key>
|
||||
<key alias="viewPublishedContentAcceptButton">Visa publicerad version</key>
|
||||
<key alias="viewPublishedContentDeclineButton">Fortsätt förhandsvisning</key>
|
||||
</area>
|
||||
<area alias="permissions">
|
||||
<key alias="FolderCreation">Skapa kataloger</key>
|
||||
<key alias="FileWritingForPackages">Skriva filer till paket</key>
|
||||
<key alias="FileWriting">Skriva filer</key>
|
||||
<key alias="MediaFolderCreation">Skapa media-kataloger</key>
|
||||
</area>
|
||||
<area alias="analytics">
|
||||
<key alias="minimalLevelDescription">Vi kommer endast att skicka ett anonymt Id så att vi informeras om att webbplatsens finns.</key>
|
||||
<key alias="basicLevelDescription">Vi kommer att skicka ett anonymt Id, Umbraco-version och installerade paket</key>
|
||||
</area>
|
||||
</language>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
@using Umbraco.Extensions
|
||||
|
||||
@{
|
||||
var isLoggedIn = Context.User?.Identity?.IsAuthenticated ?? false;
|
||||
var isLoggedIn = Context.User.GetMemberIdentity()?.IsAuthenticated ?? false;
|
||||
var logoutModel = new PostRedirectModel();
|
||||
// You can modify this to redirect to a different URL instead of the current one
|
||||
logoutModel.RedirectUrl = null;
|
||||
@@ -15,7 +15,7 @@
|
||||
{
|
||||
<div class="login-status">
|
||||
|
||||
<p>Welcome back <strong>@Context?.User?.Identity?.Name</strong>!</p>
|
||||
<p>Welcome back <strong>@Context.User?.GetMemberIdentity()?.Name</strong>!</p>
|
||||
|
||||
@using (Html.BeginUmbracoForm<UmbLoginStatusController>("HandleLogout", new { RedirectUrl = logoutModel.RedirectUrl }))
|
||||
{
|
||||
|
||||
@@ -330,7 +330,10 @@ public static class ClaimsIdentityExtensions
|
||||
var firstValue = identity.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (firstValue is not null)
|
||||
{
|
||||
return int.Parse(firstValue, CultureInfo.InvariantCulture);
|
||||
if (int.TryParse(firstValue, CultureInfo.InvariantCulture, out var id))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
public static class IntExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Does something 'x' amount of times
|
||||
/// Does something 'x' amount of times.
|
||||
/// </summary>
|
||||
/// <param name="n"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="n">Number of times to execute the action.</param>
|
||||
/// <param name="action">The action to execute.</param>
|
||||
public static void Times(this int n, Action<int> action)
|
||||
{
|
||||
for (var i = 0; i < n; i++)
|
||||
@@ -19,11 +21,11 @@ public static class IntExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Guid based on an integer value
|
||||
/// Creates a Guid based on an integer value.
|
||||
/// </summary>
|
||||
/// <param name="value"><see cref="int" /> value to convert</param>
|
||||
/// <param name="value">The <see cref="int" /> value to convert.</param>
|
||||
/// <returns>
|
||||
/// <see cref="Guid" />
|
||||
/// The converted <see cref="Guid" />.
|
||||
/// </returns>
|
||||
public static Guid ToGuid(this int value)
|
||||
{
|
||||
@@ -31,4 +33,28 @@ public static class IntExtensions
|
||||
BitConverter.GetBytes(value).CopyTo(bytes, 0);
|
||||
return new Guid(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores a GUID previously created from an integer value using <see cref="ToGuid" />.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Guid" /> value to convert.</param>
|
||||
/// <param name="result">The converted <see cref="int" />.</param>
|
||||
/// <returns>
|
||||
/// True if the <see cref="int" /> value could be created, otherwise false.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// This is used with Umbraco entities that only have integer references in the database (e.g. users).
|
||||
/// </remarks>
|
||||
public static bool TryParseFromGuid(Guid value, [NotNullWhen(true)] out int? result)
|
||||
{
|
||||
if (value.ToString().EndsWith("-0000-0000-0000-000000000000") is false)
|
||||
{
|
||||
// We have a proper GUID, not one converted from an integer.
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
result = BitConverter.ToInt32(value.ToByteArray());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public class ExcessiveHeadersCheck : HealthCheck
|
||||
var url = _hostingEnvironment.ApplicationMainUrl?.GetLeftPart(UriPartial.Authority);
|
||||
|
||||
// Access the site home page and check for the headers
|
||||
var request = new HttpRequestMessage(HttpMethod.Head, url);
|
||||
using var request = new HttpRequestMessage(HttpMethod.Head, url);
|
||||
try
|
||||
{
|
||||
using HttpResponseMessage response = await HttpClient.SendAsync(request);
|
||||
|
||||
@@ -80,7 +80,7 @@ public class HttpsCheck : HealthCheck
|
||||
var urlBuilder = new UriBuilder(_hostingEnvironment.ApplicationMainUrl) { Scheme = Uri.UriSchemeHttps };
|
||||
Uri url = urlBuilder.Uri;
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Head, url);
|
||||
using var request = new HttpRequestMessage(HttpMethod.Head, url);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -358,7 +358,7 @@ namespace Umbraco.Cms.Core.IO
|
||||
|
||||
// nothing prevents us to reach the file, security-wise, yet it is outside
|
||||
// this filesystem's root - throw
|
||||
throw new UnauthorizedAccessException($"File original: [{originalPath}] full: [{path}] is outside this filesystem's root.");
|
||||
throw new UnauthorizedAccessException($"Requested path {originalPath} is outside this filesystem's root.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -27,4 +27,9 @@ public interface IProfiler
|
||||
/// authenticated or you want to clear the results, based upon some other mechanism.
|
||||
/// </remarks>
|
||||
void Stop(bool discardResults = false);
|
||||
|
||||
/// <summary>
|
||||
/// Whether the profiler is enabled.
|
||||
/// </summary>
|
||||
bool IsEnabled => true;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ public class LogProfiler : IProfiler
|
||||
// the log never stops
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnabled => _logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug);
|
||||
|
||||
// a lightweight disposable timer
|
||||
private class LightDisposableTimer : DisposableObjectSlim
|
||||
{
|
||||
|
||||
@@ -14,6 +14,9 @@ public class NoopProfiler : IProfiler
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsEnabled => false;
|
||||
|
||||
private class VoidDisposable : DisposableObjectSlim
|
||||
{
|
||||
protected override void DisposeResources()
|
||||
|
||||
@@ -60,7 +60,7 @@ public abstract class OEmbedProviderBase : IEmbedProvider
|
||||
|
||||
using (var request = new HttpRequestMessage(HttpMethod.Get, url))
|
||||
{
|
||||
HttpResponseMessage response = _httpClient.SendAsync(request).GetAwaiter().GetResult();
|
||||
using HttpResponseMessage response = _httpClient.SendAsync(request).GetAwaiter().GetResult();
|
||||
return response.Content.ReadAsStringAsync().Result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Umbraco.Cms.Core.Media.EmbedProviders;
|
||||
/// <summary>
|
||||
/// Embed Provider for Twitter the popular online service for microblogging and social networking.
|
||||
/// </summary>
|
||||
[Obsolete("Please use X instead, scheduled for removal in v16")]
|
||||
public class Twitter : OEmbedProviderBase
|
||||
{
|
||||
public Twitter(IJsonSerializer jsonSerializer)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Core.Media.EmbedProviders;
|
||||
|
||||
/// <summary>
|
||||
/// Embed Provider for X the popular online service for microblogging and social networking.
|
||||
/// </summary>
|
||||
public class X : OEmbedProviderBase
|
||||
{
|
||||
public X(IJsonSerializer jsonSerializer)
|
||||
: base(jsonSerializer)
|
||||
{
|
||||
}
|
||||
|
||||
public override string ApiEndpoint => "http://publish.twitter.com/oembed";
|
||||
|
||||
public override string[] UrlSchemeRegex => new[] { @"(https?:\/\/(www\.)?)(twitter|x)\.com\/.*\/status\/.*" };
|
||||
|
||||
public override Dictionary<string, string> RequestParams => new();
|
||||
|
||||
public override string? GetMarkup(string url, int maxWidth = 0, int maxHeight = 0)
|
||||
{
|
||||
var requestUrl = base.GetEmbedProviderUrl(url, maxWidth, maxHeight);
|
||||
OEmbedResponse? oembed = base.GetJsonResponse<OEmbedResponse>(requestUrl);
|
||||
|
||||
return oembed?.GetHtml();
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,21 @@ public sealed class AuditItem : EntityBase, IAuditItem
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuditItem" /> class.
|
||||
/// </summary>
|
||||
public AuditItem(int objectId, AuditType type, int userId, string? entityType, DateTime createDate, string? comment = null, string? parameters = null)
|
||||
{
|
||||
DisableChangeTracking();
|
||||
|
||||
Id = objectId;
|
||||
Comment = comment;
|
||||
AuditType = type;
|
||||
UserId = userId;
|
||||
EntityType = entityType;
|
||||
Parameters = parameters;
|
||||
CreateDate = createDate;
|
||||
|
||||
EnableChangeTracking();
|
||||
}
|
||||
|
||||
public AuditItem(int objectId, AuditType type, int userId, string? entityType, string? comment = null, string? parameters = null)
|
||||
{
|
||||
DisableChangeTracking();
|
||||
|
||||
@@ -194,6 +194,7 @@ public class ContentItemDisplay<TVariant> :
|
||||
/// This is not used for outgoing model information.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
[Obsolete("No longer used. Will be removed in V15.")]
|
||||
public ContentPropertyCollectionDto? ContentDto { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
namespace Umbraco.Cms.Core.Models.ContentEditing;
|
||||
|
||||
/// <summary>
|
||||
/// The action associated with saving a content item
|
||||
/// The action associated with saving a content item.
|
||||
/// </summary>
|
||||
public enum ContentSaveAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Saves the content item, no publish
|
||||
/// Saves the content item, no publish.
|
||||
/// </summary>
|
||||
Save = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new content item
|
||||
/// Creates a new content item.
|
||||
/// </summary>
|
||||
SaveNew = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Saves and publishes the content item
|
||||
/// Saves and publishes the content item.
|
||||
/// </summary>
|
||||
Publish = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Creates and publishes a new content item
|
||||
/// Creates and publishes a new content item.
|
||||
/// </summary>
|
||||
PublishNew = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Saves and sends publish notification
|
||||
/// Saves and sends publish notification.
|
||||
/// </summary>
|
||||
SendPublish = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Creates and sends publish notification
|
||||
/// Creates and sends publish notification.
|
||||
/// </summary>
|
||||
SendPublishNew = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Saves and schedules publishing
|
||||
/// Saves and schedules publishing.
|
||||
/// </summary>
|
||||
Schedule = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Creates and schedules publishing
|
||||
/// Creates and schedules publishing.
|
||||
/// </summary>
|
||||
ScheduleNew = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Saves and publishes the content item including all descendants that have a published version
|
||||
/// Saves and publishes the content item including all descendants that have a published version.
|
||||
/// </summary>
|
||||
PublishWithDescendants = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Creates and publishes the content item including all descendants that have a published version
|
||||
/// Creates and publishes the new content item including all descendants that have a published version.
|
||||
/// </summary>
|
||||
PublishWithDescendantsNew = 9,
|
||||
|
||||
/// <summary>
|
||||
/// Saves and publishes the content item including all descendants regardless of whether they have a published version
|
||||
/// or not
|
||||
/// or not.
|
||||
/// </summary>
|
||||
PublishWithDescendantsForce = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Creates and publishes the content item including all descendants regardless of whether they have a published
|
||||
/// version or not
|
||||
/// Creates and publishes the new content item including all descendants regardless of whether they have a published
|
||||
/// version or not.
|
||||
/// </summary>
|
||||
PublishWithDescendantsForceNew = 11,
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ public class LinkDisplay
|
||||
[DataMember(Name = "name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[DataMember(Name = "nodeName")]
|
||||
public string? NodeName { get; set; }
|
||||
|
||||
[DataMember(Name = "published")]
|
||||
public bool Published { get; set; }
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ public class PropertyTypeBasic
|
||||
|
||||
[Required]
|
||||
[RegularExpression(@"^([a-zA-Z]\w.*)$", ErrorMessage = "Invalid alias")]
|
||||
[MaxLength(255, ErrorMessage = "Alias is too long")]
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; } = null!;
|
||||
|
||||
|
||||
@@ -174,15 +174,17 @@ public static class ContentRepositoryExtensions
|
||||
foreach (IProperty property in content.Properties)
|
||||
{
|
||||
// each property type may or may not support the variation
|
||||
if (!property.PropertyType?.SupportsVariation(culture, "*", true) ?? false)
|
||||
if ((!property.PropertyType?.SupportsVariation(culture, "*", true) ?? false) &&
|
||||
!(property.PropertyType?.Variations == ContentVariation.Nothing))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (IPropertyValue pvalue in property.Values)
|
||||
{
|
||||
if ((property.PropertyType?.SupportsVariation(pvalue.Culture, pvalue.Segment, true) ?? false) &&
|
||||
(culture == "*" || (pvalue.Culture?.InvariantEquals(culture) ?? false)))
|
||||
if (((property.PropertyType?.SupportsVariation(pvalue.Culture, pvalue.Segment, true) ?? false) &&
|
||||
(culture == "*" || (pvalue.Culture?.InvariantEquals(culture) ?? false))) ||
|
||||
property.PropertyType?.Variations == ContentVariation.Nothing)
|
||||
{
|
||||
property.SetValue(null, pvalue.Culture, pvalue.Segment);
|
||||
}
|
||||
@@ -193,7 +195,8 @@ public static class ContentRepositoryExtensions
|
||||
IPropertyCollection otherProperties = other.Properties;
|
||||
foreach (IProperty otherProperty in otherProperties)
|
||||
{
|
||||
if (!otherProperty?.PropertyType?.SupportsVariation(culture, "*", true) ?? true)
|
||||
if ((!otherProperty?.PropertyType?.SupportsVariation(culture, "*", true) ?? true) &&
|
||||
!(otherProperty?.PropertyType?.Variations == ContentVariation.Nothing))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -203,8 +206,9 @@ public static class ContentRepositoryExtensions
|
||||
{
|
||||
foreach (IPropertyValue pvalue in otherProperty.Values)
|
||||
{
|
||||
if (otherProperty.PropertyType.SupportsVariation(pvalue.Culture, pvalue.Segment, true) &&
|
||||
(culture == "*" || (pvalue.Culture?.InvariantEquals(culture) ?? false)))
|
||||
if (((otherProperty?.PropertyType.SupportsVariation(pvalue.Culture, pvalue.Segment, true) ?? false) &&
|
||||
(culture == "*" ||(pvalue.Culture?.InvariantEquals(culture) ?? false))) ||
|
||||
otherProperty?.PropertyType?.Variations == ContentVariation.Nothing)
|
||||
{
|
||||
var value = published ? pvalue.PublishedValue : pvalue.EditedValue;
|
||||
content.SetValue(alias, value, pvalue.Culture, pvalue.Segment);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models;
|
||||
|
||||
public class Link
|
||||
@@ -10,5 +12,7 @@ public class Link
|
||||
|
||||
public Udi? Udi { get; set; }
|
||||
|
||||
public IPublishedContent? Content { get; set; }
|
||||
|
||||
public string? Url { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace Umbraco.Cms.Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Describes the options available with publishing a content branch for force publishing.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum PublishBranchFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// The default behavior is to publish only the published content that has changed.
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// For publishing a branch, publish all changed content, including content that is not published.
|
||||
/// </summary>
|
||||
IncludeUnpublished = 1,
|
||||
|
||||
/// <summary>
|
||||
/// For publishing a branch, force republishing of all published content, including content that has not changed.
|
||||
/// </summary>
|
||||
ForceRepublish = 2,
|
||||
|
||||
/// <summary>
|
||||
/// For publishing a branch, publish all content, including content that is not published and content that has not changed.
|
||||
/// </summary>
|
||||
All = IncludeUnpublished | ForceRepublish,
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace Umbraco.Cms.Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies options for publishing notifcations when saving.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum PublishNotificationSaveOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Do not publish any notifications.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Only publish the saving notification.
|
||||
/// </summary>
|
||||
Saving = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Only publish the saved notification.
|
||||
/// </summary>
|
||||
Saved = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Publish all the notifications.
|
||||
/// </summary>
|
||||
All = Saving | Saved,
|
||||
}
|
||||
@@ -27,4 +27,6 @@ public class WebhookLog
|
||||
public string ResponseBody { get; set; } = string.Empty;
|
||||
|
||||
public bool ExceptionOccured { get; set; }
|
||||
|
||||
public bool IsSuccessStatusCode { get; set; }
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Umbraco.Cms.Core;
|
||||
/// Provides an equivalent to the c# lock statement, to be used in a using block.
|
||||
/// </summary>
|
||||
/// <remarks>Ie replace <c>lock (o) {...}</c> by <c>using (new MonitorLock(o)) { ... }</c></remarks>
|
||||
[Obsolete("Use System.Threading.Lock instead. This will be removed in a future version.")]
|
||||
public class MonitorLock : IDisposable
|
||||
{
|
||||
private readonly bool _entered;
|
||||
|
||||
@@ -32,4 +32,14 @@ public class ContentTreeChangeNotification : TreeChangeNotification<IContent>
|
||||
: base(new TreeChange<IContent>(target, changeTypes), messages)
|
||||
{
|
||||
}
|
||||
|
||||
public ContentTreeChangeNotification(
|
||||
IContent target,
|
||||
TreeChangeTypes changeTypes,
|
||||
IEnumerable<string>? publishedCultures,
|
||||
IEnumerable<string>? unpublishedCultures,
|
||||
EventMessages messages)
|
||||
: base(new TreeChange<IContent>(target, changeTypes, publishedCultures, unpublishedCultures), messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,23 +3,29 @@ using Umbraco.Cms.Core.Security;
|
||||
namespace Umbraco.Cms.Core.Persistence.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Repository for external logins with Guid as key, so it can be shared for members and users
|
||||
/// Repository for external logins with Guid as key, so it can be shared for members and users.
|
||||
/// </summary>
|
||||
public interface IExternalLoginWithKeyRepository : IReadWriteQueryRepository<int, IIdentityUserLogin>,
|
||||
IQueryRepository<IIdentityUserToken>
|
||||
{
|
||||
/// <summary>
|
||||
/// Replaces all external login providers for the user/member key
|
||||
/// Replaces all external login providers for the user/member key.
|
||||
/// </summary>
|
||||
void Save(Guid userOrMemberKey, IEnumerable<IExternalLogin> logins);
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all external login provider tokens for the providers specified for the user/member key
|
||||
/// Replaces all external login provider tokens for the providers specified for the user/member key.
|
||||
/// </summary>
|
||||
void Save(Guid userOrMemberKey, IEnumerable<IExternalLoginToken> tokens);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all external logins for the specified the user/member key
|
||||
/// Deletes all external logins for the specified the user/member key.
|
||||
/// </summary>
|
||||
void DeleteUserLogins(Guid userOrMemberKey);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes external logins that aren't associated with the current collection of providers.
|
||||
/// </summary>
|
||||
/// <param name="currentLoginProviders">The names of the currently configured providers.</param>
|
||||
void DeleteUserLoginsForRemovedProviders(IEnumerable<string> currentLoginProviders) { }
|
||||
}
|
||||
|
||||
@@ -38,4 +38,11 @@ public interface IMemberRepository : IContentRepository<int, IMember>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
int GetCountByQuery(IQuery<IMember>? query);
|
||||
|
||||
/// <summary>
|
||||
/// Saves only the properties related to login for the member, using an optimized, non-locking update.
|
||||
/// </summary>
|
||||
/// <param name="member">The member to update.</param>
|
||||
/// <returns>Used to avoid the full save of the member object after a login operation.</returns>
|
||||
Task UpdateLoginPropertiesAsync(IMember member) => Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Linq.Expressions;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Persistence.Querying;
|
||||
|
||||
@@ -109,5 +109,26 @@ public interface IUserRepository : IReadWriteQueryRepository<int, IUser>
|
||||
|
||||
void ClearLoginSession(Guid sessionId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a page of users, ordered by Id and starting from the provided Id.
|
||||
/// </summary>
|
||||
/// <param name="id">The user Id to start retrieving users from.</param>
|
||||
/// <param name="count">The number of users to return.</param>
|
||||
/// <returns>A page of <see cref="IUser"/> instances.</returns>
|
||||
[Obsolete("No longer used in Umbraco. Scheduled for removal in Umbraco 18.")]
|
||||
IEnumerable<IUser> GetNextUsers(int id, int count);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a page of approved users, ordered by Id and starting from the provided Id.
|
||||
/// </summary>
|
||||
/// <param name="id">The user Id to start retrieving users from.</param>
|
||||
/// <param name="count">The number of users to return.</param>
|
||||
/// <returns>A page of <see cref="IUser"/> instances.</returns>
|
||||
IEnumerable<IUser> GetNextApprovedUsers(int id, int count) => Enumerable.Empty<IUser>();
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates sessions for users that aren't associated with the current collection of providers.
|
||||
/// </summary>
|
||||
/// <param name="currentProviderKeys">The keys for the currently configured providers.</param>
|
||||
void InvalidateSessionsForRemovedProviders(IEnumerable<string> currentProviderKeys) { }
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class InstallationRepository : IInstallationRepository
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
var content = new StringContent(_jsonSerializer.Serialize(installLog), Encoding.UTF8, "application/json");
|
||||
using var content = new StringContent(_jsonSerializer.Serialize(installLog), Encoding.UTF8, "application/json");
|
||||
|
||||
await _httpClient.PostAsync(RestApiInstallUrl, content);
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ public class UpgradeCheckRepository : IUpgradeCheckRepository
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
var content = new StringContent(_jsonSerializer.Serialize(new CheckUpgradeDto(version)), Encoding.UTF8, "application/json");
|
||||
using var content = new StringContent(_jsonSerializer.Serialize(new CheckUpgradeDto(version)), Encoding.UTF8, "application/json");
|
||||
|
||||
_httpClient.Timeout = TimeSpan.FromSeconds(1);
|
||||
HttpResponseMessage task = await _httpClient.PostAsync(RestApiUpgradeChecklUrl, content);
|
||||
using HttpResponseMessage task = await _httpClient.PostAsync(RestApiUpgradeChecklUrl, content);
|
||||
var json = await task.Content.ReadAsStringAsync();
|
||||
UpgradeResult? result = _jsonSerializer.Deserialize<UpgradeResult>(json);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.Validators;
|
||||
/// <summary>
|
||||
/// A validator that validates that the value is not null or empty (if it is a string)
|
||||
/// </summary>
|
||||
public sealed class RequiredValidator : IValueRequiredValidator, IManifestValueValidator
|
||||
public class RequiredValidator : IValueRequiredValidator, IManifestValueValidator
|
||||
{
|
||||
private const string ValueCannotBeNull = "Value cannot be null";
|
||||
private const string ValueCannotBeEmpty = "Value cannot be empty";
|
||||
@@ -23,7 +23,7 @@ public sealed class RequiredValidator : IValueRequiredValidator, IManifestValueV
|
||||
ValidateRequired(value, valueType);
|
||||
|
||||
/// <inheritdoc cref="IValueRequiredValidator.ValidateRequired" />
|
||||
public IEnumerable<ValidationResult> ValidateRequired(object? value, string? valueType)
|
||||
public virtual IEnumerable<ValidationResult> ValidateRequired(object? value, string? valueType)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user