update Kirby
All checks were successful
Deploy Production / Deploy to Production (push) Successful in 44s

This commit is contained in:
isUnknown 2026-06-09 14:40:05 +02:00
parent fd3522c1f9
commit df535a6ecb
201 changed files with 10330 additions and 1976 deletions

View file

@ -22,7 +22,7 @@
},
"require": {
"php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
"getkirby/cms": "^5.2",
"getkirby/cms": "^5.4",
"sylvainjule/code-editor": "^1.1",
"tobimori/kirby-seo": "^2.0.0-beta.2"
},

110
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f0e4fb17cfdab02969f2b43fc6538f97",
"content-hash": "a41ec1d3d2e36e61f0cb8f8e4520d8b2",
"packages": [
{
"name": "christian-riesen/base32",
@ -268,16 +268,16 @@
},
{
"name": "getkirby/cms",
"version": "5.2.2",
"version": "5.4.3",
"source": {
"type": "git",
"url": "https://github.com/getkirby/kirby.git",
"reference": "55563aa1597d9e9fd2b9cb3f8ad53ef99916ae82"
"reference": "649acf7f2b055238778772bfd2fb19b0121be6d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/getkirby/kirby/zipball/55563aa1597d9e9fd2b9cb3f8ad53ef99916ae82",
"reference": "55563aa1597d9e9fd2b9cb3f8ad53ef99916ae82",
"url": "https://api.github.com/repos/getkirby/kirby/zipball/649acf7f2b055238778772bfd2fb19b0121be6d3",
"reference": "649acf7f2b055238778772bfd2fb19b0121be6d3",
"shasum": ""
},
"require": {
@ -300,10 +300,10 @@
"laminas/laminas-escaper": "2.18.0",
"michelf/php-smartypants": "1.8.1",
"php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
"phpmailer/phpmailer": "7.0.1",
"symfony/polyfill-intl-idn": "1.33.0",
"symfony/polyfill-mbstring": "1.33.0",
"symfony/yaml": "7.4.1"
"phpmailer/phpmailer": "7.1.1",
"symfony/polyfill-intl-idn": "1.38.1",
"symfony/polyfill-mbstring": "1.38.1",
"symfony/yaml": "7.4.13"
},
"replace": {
"symfony/polyfill-php72": "*"
@ -364,7 +364,7 @@
"type": "custom"
}
],
"time": "2026-01-08T10:44:15+00:00"
"time": "2026-05-27T11:20:31+00:00"
},
{
"name": "getkirby/composer-installer",
@ -591,16 +591,16 @@
},
{
"name": "phpmailer/phpmailer",
"version": "v7.0.1",
"version": "v7.1.1",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "360ae911ce62e25e11249f6140fa58939f556ebe"
"reference": "1bc1716a507a65e039d4ac9d9adebbbd0d346e15"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/360ae911ce62e25e11249f6140fa58939f556ebe",
"reference": "360ae911ce62e25e11249f6140fa58939f556ebe",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/1bc1716a507a65e039d4ac9d9adebbbd0d346e15",
"reference": "1bc1716a507a65e039d4ac9d9adebbbd0d346e15",
"shasum": ""
},
"require": {
@ -661,7 +661,7 @@
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v7.0.1"
"source": "https://github.com/PHPMailer/PHPMailer/tree/v7.1.1"
},
"funding": [
{
@ -669,7 +669,7 @@
"type": "github"
}
],
"time": "2025-11-25T07:18:09+00:00"
"time": "2026-05-18T08:06:14+00:00"
},
{
"name": "psr/log",
@ -761,16 +761,16 @@
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.6.0",
"version": "v3.7.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
"reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b",
"reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b",
"shasum": ""
},
"require": {
@ -783,7 +783,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.6-dev"
"dev-main": "3.7-dev"
}
},
"autoload": {
@ -808,7 +808,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0"
},
"funding": [
{
@ -819,25 +819,29 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-25T14:21:43+00:00"
"time": "2026-04-13T15:52:40+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.33.0",
"version": "v1.37.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
"reference": "141046a8f9477948ff284fa65be2095baafb94f2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2",
"reference": "141046a8f9477948ff284fa65be2095baafb94f2",
"shasum": ""
},
"require": {
@ -887,7 +891,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0"
},
"funding": [
{
@ -907,20 +911,20 @@
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
"time": "2026-04-10T16:19:22+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.33.0",
"version": "v1.38.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3"
"reference": "dc21118016c039a66235cf93d96b435ffb282412"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3",
"reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412",
"reference": "dc21118016c039a66235cf93d96b435ffb282412",
"shasum": ""
},
"require": {
@ -974,7 +978,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0"
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1"
},
"funding": [
{
@ -994,20 +998,20 @@
"type": "tidelift"
}
],
"time": "2024-09-10T14:38:51+00:00"
"time": "2026-05-25T15:22:23+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.33.0",
"version": "v1.38.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "3833d7255cc303546435cb650316bff708a1c75c"
"reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
"reference": "3833d7255cc303546435cb650316bff708a1c75c",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b",
"reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b",
"shasum": ""
},
"require": {
@ -1059,7 +1063,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0"
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0"
},
"funding": [
{
@ -1079,20 +1083,20 @@
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
"time": "2026-05-25T13:48:31+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.33.0",
"version": "v1.38.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
"reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/14c5439eec4ccff081ac14eca2dc57feb2a66d92",
"reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92",
"shasum": ""
},
"require": {
@ -1144,7 +1148,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.1"
},
"funding": [
{
@ -1164,20 +1168,20 @@
"type": "tidelift"
}
],
"time": "2024-12-23T08:48:59+00:00"
"time": "2026-05-26T12:51:13+00:00"
},
{
"name": "symfony/yaml",
"version": "v7.4.1",
"version": "v7.4.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "24dd4de28d2e3988b311751ac49e684d783e2345"
"reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/24dd4de28d2e3988b311751ac49e684d783e2345",
"reference": "24dd4de28d2e3988b311751ac49e684d783e2345",
"url": "https://api.github.com/repos/symfony/yaml/zipball/a7ec3b1156faf8815db7683ec7c1e7338e6f977c",
"reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c",
"shasum": ""
},
"require": {
@ -1220,7 +1224,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/yaml/tree/v7.4.1"
"source": "https://github.com/symfony/yaml/tree/v7.4.13"
},
"funding": [
{
@ -1240,7 +1244,7 @@
"type": "tidelift"
}
],
"time": "2025-12-04T18:11:45+00:00"
"time": "2026-05-25T06:06:12+00:00"
},
{
"name": "tobimori/kirby-seo",

View file

@ -1,7 +1,7 @@
# Kirby License Agreement
Published: November 29, 2025
Source: https://getkirby.com/license/2025-11-29
Published: March 26, 2026
Source: https://getkirby.com/license/2026-03-26
## About this Agreement
@ -17,12 +17,13 @@ This section summarizes the most important conditions of this Agreement to give
- With your purchase you obtain a license. A license allows you to use Kirby according to this Agreement.
- Each project (defined by its URL) needs its own license. You need to purchase the right license for your project and/or client. You can find our license variants on <https://getkirby.com/buy>.
- Each purchased license needs to be activated to the project URL as soon as the project is published.
- In some explicitly listed cases, you can use Kirby without having to purchase a license. In these cases, this Agreement grants you the license directly. There are also cases where you can request a free or discounted license from us.
- Each license includes any Kirby version that gets released within three years from the date when you first activated your license. We also provide free security updates for older versions that may protect your project beyond three years.
- After those three years, you can continue to use Kirby for your project with any of these versions as long as you want.
- To use any newer version released after this time, you will need to upgrade your license.
- Upgrading your license extends the timeframe for an additional three years during which you can use new releases. You can perform the upgrade at any time.
- You have the right to transfer or reassign a license to another person or project if needed.
- You have the right to transfer a license to another person or reassign it to another project of the same client if needed.
- There are some restrictions for use of Kirby that you can find below.
For the full license details, please read the Agreement in full. Only the following sections are legally binding.
@ -55,15 +56,17 @@ Every time you see one of these capitalized terms in the following text, it has
## Usage for a Public Site
Installing Kirby on or using it for a Public Site requires a [paid license](https://getkirby.com/buy). Once a paid license is needed, the license must be immediately activated to the Public Sites domain name and root directory via our [license hub](https://hub.getkirby.com) or the activation feature in the Kirby Panel.
Installing Kirby on or using it for a Public Site requires a [paid license](https://getkirby.com/buy).
As Kirby is software and software is intangible, We don't sell it as such. Instead, this Agreement grants a license for each purchase to install and use a single instance of Kirby on a **specific Website**. Additional Kirby licenses must be purchased in order to install and use Kirby on **additional Websites**.
Once a paid license is needed, the license must be immediately activated to the Public Sites domain name and root directory via our [license hub](https://hub.getkirby.com) or the activation feature in the Kirby Panel. Failure to activate the license within one (1) week after the project's launch constitutes a violation of this Agreement and equals unlicensed use of Kirby.
The license is **non-exclusive** (meaning that You are not the only one to whom We will issue a license) and **generally non-transferable** (meaning that the one who purchases the license is the licensee).
On request, We will **transfer** a license to anyone who would be allowed and Qualified to purchase the license by law and this Agreement. The new licensee will take over all rights and obligations of this Agreement from You at the moment We confirm the license transfer.
We will also **reassign** a license to another Qualified Website domain and root directory, if You confirm that the previous Website is no longer in operation and will not be operated with the same license in the future.
We will also **reassign** a license to another Qualified Website domain and root directory of the same Client or of You, if You confirm that the previous Website is no longer in operation and will not be operated with the same license in the future. Licenses may *not* be reassigned to or reused for Websites that belong to a different Client.
If the new licensee, Website or Client in a transfer or reassignment is not Qualified for the existing license, You or the new licensee need to **upgrade the license to the qualifying terms and conditions** before the transfer or reassignment can be performed.
@ -284,7 +287,15 @@ YOU EXPRESSLY UNDERSTAND AND AGREE THAT **WE SHALL NOT BE LIABLE** FOR ANY DIREC
Bastian Allgeier **owns all rights**, title and interest to Kirby (including all intellectual property rights) and **reserves all rights to Kirby** that are not expressly granted in this Agreement.
In the event that Kirby will no longer be actively maintained, Bastian Allgeier will provide the Source Code under the terms of a free and open source software (FOSS) license as far as legally and contractually possible.
In the event that We or Our legal successor will not publish a stable Kirby release for the time span of one year (365 days), Kirby is **considered to be no longer actively maintained**. In this case, all parts of the Source Code with copyright notice to Bastian Allgeier previously licensed under the terms of this Agreement will, in their last published form, **automatically be available under the following terms of the free and open source MIT license**:
```
Permission is hereby granted, free of charge, to any person obtaining a copy of the Kirby Source Code, to deal in the Source Code without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Source Code, and to permit persons to whom the Source Code is furnished to do so, subject to the following conditions:
The copyright notice to Bastian Allgeier and this permission notice shall be included in all copies or substantial portions of the Source Code.
THE SOURCE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOURCE CODE OR THE USE OR OTHER DEALINGS IN THE SOURCE CODE.
```
## Applicable Law & Place of Jurisdiction

View file

@ -1,7 +1,7 @@
##
## Bundle of CA Root Certificates
##
## Certificate data from Mozilla as of: Tue Dec 2 04:12:02 2025 GMT
## Certificate data from Mozilla as of: Thu May 14 03:12:02 2026 GMT
##
## Find updated versions here: https://curl.se/docs/caextract.html
##
@ -15,8 +15,8 @@
## an Apache+mod_ssl webserver for SSL client authentication.
## Just configure this file as the SSLCACertificateFile.
##
## Conversion done with mk-ca-bundle.pl version 1.30.
## SHA256: a903b3cd05231e39332515ef7ebe37e697262f39515a52015c23c62805b73cd0
## Conversion done with mk-ca-bundle.pl version 1.33.
## SHA256: 77130ef91213772844561fbd3aa31d413b25c2ac7f576fea3bc3bbff7ef93489
##
@ -46,237 +46,6 @@ W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
-----END CERTIFICATE-----
QuoVadis Root CA 2
==================
-----BEGIN CERTIFICATE-----
MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx
ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6
XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk
lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB
lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy
lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt
66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn
wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh
D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy
BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie
J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud
DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU
a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv
Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3
UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm
VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK
+JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW
IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1
WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X
f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II
4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8
VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
-----END CERTIFICATE-----
QuoVadis Root CA 3
==================
-----BEGIN CERTIFICATE-----
MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx
OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg
DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij
KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K
DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv
BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp
p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8
nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX
MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM
Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz
uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT
BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj
YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB
BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD
VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4
ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE
AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV
qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s
hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z
POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2
Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp
8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC
bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu
g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p
vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr
qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto=
-----END CERTIFICATE-----
DigiCert Assured ID Root CA
===========================
-----BEGIN CERTIFICATE-----
MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw
IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx
MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO
9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy
UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW
/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy
oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf
GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF
66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq
hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc
EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn
SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i
8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
-----END CERTIFICATE-----
DigiCert Global Root CA
=======================
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw
MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn
TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5
BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H
4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y
7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB
o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm
8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF
BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr
EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt
tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886
UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
DigiCert High Assurance EV Root CA
==================================
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw
KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw
MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu
Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t
Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS
OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3
MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ
NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe
h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB
Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY
JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ
V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp
myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK
mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K
-----END CERTIFICATE-----
SwissSign Gold CA - G2
======================
-----BEGIN CERTIFICATE-----
MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw
EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN
MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp
c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B
AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq
t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C
jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg
vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF
ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR
AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend
jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO
peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR
7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi
GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64
OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm
5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr
44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf
Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m
Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp
mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk
vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf
KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br
NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj
viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
-----END CERTIFICATE-----
SecureTrust CA
==============
-----BEGIN CERTIFICATE-----
MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG
EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy
dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe
BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX
OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t
DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH
GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b
01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH
ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/
BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj
aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ
KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu
SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf
mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ
nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
-----END CERTIFICATE-----
Secure Global CA
================
-----BEGIN CERTIFICATE-----
MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG
EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH
bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg
MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg
Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx
YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ
bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g
8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV
HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi
0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn
oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA
MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+
OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn
CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5
3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
-----END CERTIFICATE-----
COMODO Certification Authority
==============================
-----BEGIN CERTIFICATE-----
MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE
BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG
A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb
MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD
T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH
+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww
xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV
4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA
1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI
rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E
BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k
b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC
AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP
OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc
IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN
+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ==
-----END CERTIFICATE-----
COMODO ECC Certification Authority
==================================
-----BEGIN CERTIFICATE-----
@ -294,28 +63,6 @@ FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA
U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
-----END CERTIFICATE-----
Certigna
========
-----BEGIN CERTIFICATE-----
MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw
EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3
MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI
Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q
XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH
GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p
ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg
DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf
Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ
tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ
BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J
SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA
hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+
ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu
PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY
1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
-----END CERTIFICATE-----
ePKI Root Certification Authority
=================================
-----BEGIN CERTIFICATE-----
@ -347,26 +94,6 @@ sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD
BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw=
-----END CERTIFICATE-----
certSIGN ROOT CA
================
-----BEGIN CERTIFICATE-----
MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD
VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa
Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE
CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I
JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH
rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2
ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD
0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943
AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB
AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8
SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0
x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt
vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz
TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD
-----END CERTIFICATE-----
NetLock Arany (Class Gold) Főtanúsítvány
========================================
-----BEGIN CERTIFICATE-----
@ -536,90 +263,6 @@ iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza
YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6
-----END CERTIFICATE-----
AffirmTrust Commercial
======================
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS
BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw
MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb
DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV
C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6
BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww
MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV
HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG
hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi
qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv
0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh
sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
-----END CERTIFICATE-----
AffirmTrust Networking
======================
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS
BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw
MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE
Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI
dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24
/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb
h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV
HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu
UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6
12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23
WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9
/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
-----END CERTIFICATE-----
AffirmTrust Premium
===================
-----BEGIN CERTIFICATE-----
MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS
BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy
OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy
dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn
BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV
5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs
+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd
GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R
p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI
S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04
6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5
/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo
+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB
/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv
MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC
6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S
L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK
+4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV
BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg
IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60
g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb
zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw==
-----END CERTIFICATE-----
AffirmTrust Premium ECC
=======================
-----BEGIN CERTIFICATE-----
MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV
BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx
MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U
cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA
IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ
N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW
BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK
BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X
57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM
eQ==
-----END CERTIFICATE-----
Certum Trusted Network CA
=========================
-----BEGIN CERTIFICATE-----
@ -946,35 +589,6 @@ EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3
zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0=
-----END CERTIFICATE-----
TeliaSonera Root CA v1
======================
-----BEGIN CERTIFICATE-----
MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE
CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4
MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW
VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+
6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA
3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k
B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn
Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH
oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3
F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ
oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7
gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc
TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB
AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW
DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm
zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx
0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW
pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV
G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc
c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT
JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2
qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6
Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems
WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
-----END CERTIFICATE-----
T-TeleSec GlobalRoot Class 2
============================
-----BEGIN CERTIFICATE-----
@ -1371,50 +985,6 @@ ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ
3Wl9af0AVqW3rLatt8o+Ae+c
-----END CERTIFICATE-----
Entrust Root Certification Authority - G2
=========================================
-----BEGIN CERTIFICATE-----
MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV
BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy
bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug
b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw
HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT
DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx
OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s
eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP
/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz
HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU
s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y
TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx
AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6
0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z
iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ
Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi
nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+
vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO
e4pIb4tF9g==
-----END CERTIFICATE-----
Entrust Root Certification Authority - EC1
==========================================
-----BEGIN CERTIFICATE-----
MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx
FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn
YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl
ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw
FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs
LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg
dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt
IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy
AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef
9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h
vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8
kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G
-----END CERTIFICATE-----
CFCA EV ROOT
============
-----BEGIN CERTIFICATE-----
@ -2197,71 +1767,6 @@ NMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE1LlSVHJ7liXMvGnjSG4N
0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MXQRBdJ3NghVdJIgc=
-----END CERTIFICATE-----
Trustwave Global Certification Authority
========================================
-----BEGIN CERTIFICATE-----
MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV
UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2
ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u
IEF1dGhvcml0eTAeFw0xNzA4MjMxOTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJV
UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2
ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u
IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALldUShLPDeS0YLOvR29
zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0XznswuvCAAJWX/NKSqIk4cXGIDtiLK0thAf
LdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4Bq
stTnoApTAbqOl5F2brz81Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9o
WN0EACyW80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotPJqX+
OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1lRtzuzWniTY+HKE40
Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfwhI0Vcnyh78zyiGG69Gm7DIwLdVcE
uE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm
+9jaJXLE9gCxInm943xZYkqcBW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqj
ifLJS3tBEW1ntwiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud
EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1UdDwEB/wQEAwIB
BjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W0OhUKDtkLSGm+J1WE2pIPU/H
PinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfeuyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0H
ZJDmHvUqoai7PF35owgLEQzxPy0QlG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla
4gt5kNdXElE1GYhBaCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5R
vbbEsLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPTMaCm/zjd
zyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qequ5AvzSxnI9O4fKSTx+O
856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxhVicGaeVyQYHTtgGJoC86cnn+OjC/QezH
Yj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu
3R3y4G5OBVixwJAWKqQ9EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP
29FpHOTKyeC2nOnOcXHebD8WpHk=
-----END CERTIFICATE-----
Trustwave Global ECC P256 Certification Authority
=================================================
-----BEGIN CERTIFICATE-----
MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYDVQQGEwJVUzER
MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI
b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZp
Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYD
VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy
dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1
NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH77bOYj
43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoNFWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqm
P62jQzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt
0UrrdaVKEJmzsaGLSvcwCgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjz
RM4q3wghDDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7
-----END CERTIFICATE-----
Trustwave Global ECC P384 Certification Authority
=================================================
-----BEGIN CERTIFICATE-----
MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYDVQQGEwJVUzER
MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI
b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZp
Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYD
VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy
dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4
NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuBBAAiA2IABGvaDXU1CDFH
Ba5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJj9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr
/TklZvFe/oyujUF5nQlgziip04pt89ZF1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNV
HQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNn
ADBkAjA3AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsCMGcl
CrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVuSw==
-----END CERTIFICATE-----
NAVER Global Root Certification Authority
=========================================
-----BEGIN CERTIFICATE-----
@ -2354,36 +1859,6 @@ vLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+
CAezNIm8BZ/3Hobui3A=
-----END CERTIFICATE-----
GLOBALTRUST 2020
================
-----BEGIN CERTIFICATE-----
MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQVQx
IzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVT
VCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYxMDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAh
BgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAy
MDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWi
D59bRatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9ZYybNpyrO
VPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3QWPKzv9pj2gOlTblzLmM
CcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPwyJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCm
fecqQjuCgGOlYx8ZzHyyZqjC0203b+J+BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKA
A1GqtH6qRNdDYfOiaxaJSaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9OR
JitHHmkHr96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj04KlG
DfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9MedKZssCz3AwyIDMvU
clOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIwq7ejMZdnrY8XD2zHc+0klGvIg5rQ
mjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUw
AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1Ud
IwQYMBaAFNwuH9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA
VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJCXtzoRlgHNQIw
4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd6IwPS3BD0IL/qMy/pJTAvoe9
iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS
8cE54+X1+NZK3TTN+2/BT+MAi1bikvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2
HcqtbepBEX4tdJP7wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxS
vTOBTI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6CMUO+1918
oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn4rnvyOL2NSl6dPrFf4IF
YqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+IaFvowdlxfv1k7/9nR4hYJS8+hge9+6jl
gqispdNpQ80xiEmEU5LAsTkbOYMBMMTyqfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg==
-----END CERTIFICATE-----
ANF Secure Server Root CA
=========================
-----BEGIN CERTIFICATE-----
@ -2708,36 +2183,6 @@ FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bbbP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3
gm3c
-----END CERTIFICATE-----
GTS Root R2
===========
-----BEGIN CERTIFICATE-----
MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQGEwJV
UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg
UjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE
ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0G
CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv
CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo7JUl
e3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWIm8Wb
a96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS
+LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7M
kogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJG
r61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RWIr9q
S34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73VululycslaVNV
J1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy5okL
dWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T
AQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQAD
ggIBAB/Kzt3HvqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8
0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyCB19m3H0Q/gxh
swWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2uNmSRXbBoGOqKYcl3qJfEycel
/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMgyALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVn
jWQye+mew4K6Ki3pHrTgSAai/GevHyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y5
9PYjJbigapordwj6xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M
7YNRTOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924SgJPFI/2R8
0L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV7LXTWtiBmelDGDfrs7vR
WGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjW
HYbL
-----END CERTIFICATE-----
GTS Root R3
===========
-----BEGIN CERTIFICATE-----
@ -3214,23 +2659,6 @@ HVlNjM7IDiPCtyaaEBRx/pOyiriA8A4QntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0
o82bNSQ3+pCTE4FCxpgmdTdmQRCsu/WU48IxK63nI1bMNSWSs1A=
-----END CERTIFICATE-----
FIRMAPROFESIONAL CA ROOT-A WEB
==============================
-----BEGIN CERTIFICATE-----
MIICejCCAgCgAwIBAgIQMZch7a+JQn81QYehZ1ZMbTAKBggqhkjOPQQDAzBuMQswCQYDVQQGEwJF
UzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UEYQwPVkFURVMtQTYyNjM0MDY4
MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENBIFJPT1QtQSBXRUIwHhcNMjIwNDA2MDkwMTM2
WhcNNDcwMzMxMDkwMTM2WjBuMQswCQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25h
bCBTQTEYMBYGA1UEYQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFM
IENBIFJPT1QtQSBXRUIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARHU+osEaR3xyrq89Zfe9MEkVz6
iMYiuYMQYneEMy3pA4jU4DP37XcsSmDq5G+tbbT4TIqk5B/K6k84Si6CcyvHZpsKjECcfIr28jlg
st7L7Ljkb+qbXbdTkBgyVcUgt5SjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUk+FD
Y1w8ndYn81LsF7Kpryz3dvgwHQYDVR0OBBYEFJPhQ2NcPJ3WJ/NS7Beyqa8s93b4MA4GA1UdDwEB
/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjAdfKR7w4l1M+E7qUW/Runpod3JIha3RxEL2Jq68cgL
cFBTApFwhVmpHqTm6iMxoAACMQD94vizrxa5HnPEluPBMBnYfubDl94cT7iJLzPrSA8Z94dGXSaQ
pYXFuXqUPoeovQA=
-----END CERTIFICATE-----
TWCA CYBER Root CA
==================
-----BEGIN CERTIFICATE-----
@ -3480,8 +2908,8 @@ SM49BAMDA2kAMGYCMQCpKjAd0MKfkFFRQD6VVCHNFmb3U2wIFjnQEnx/Yxvf4zgAOdktUyBFCxxg
ZzFDJe0CMQCSia7pXGKDYmH5LVerVrkR3SW+ak5KGoJr3M/TvEqzPNcum9v4KGm8ay3sMaE641c=
-----END CERTIFICATE-----
OISTE Server Root RSA G1
=========================
OISTE Server Root RSA G1
========================
-----BEGIN CERTIFICATE-----
MIIFgzCCA2ugAwIBAgIQVaXZZ5Qoxu0M+ifdWwFNGDANBgkqhkiG9w0BAQwFADBLMQswCQYDVQQG
EwJDSDEZMBcGA1UECgwQT0lTVEUgRm91bmRhdGlvbjEhMB8GA1UEAwwYT0lTVEUgU2VydmVyIFJv
@ -3509,3 +2937,21 @@ msuY33OhkKCgxeDoAaijFJzIwZqsFVAzje18KotzlUBDJvyBpCpfOZC3J8tRd/iWkx7P8nd9H0aT
olkelUTFLXVksNb54Dxp6gS1HAviRkRNQzuXSXERvSS2wq1yVAb+axj5d9spLFKebXd7Yv0PTY6Y
MjAwcRLWJTXjn/hvnLXrahut6hDTlhZyBiElxky8j3C7DOReIoMt0r7+hVu05L0=
-----END CERTIFICATE-----
e-Szigno TLS Root CA 2023
=========================
-----BEGIN CERTIFICATE-----
MIICzzCCAjGgAwIBAgINAOhvGHvWOWuYSkmYCjAKBggqhkjOPQQDBDB1MQswCQYDVQQGEwJIVTER
MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xFzAVBgNVBGEMDlZBVEhV
LTIzNTg0NDk3MSIwIAYDVQQDDBllLVN6aWdubyBUTFMgUm9vdCBDQSAyMDIzMB4XDTIzMDcxNzE0
MDAwMFoXDTM4MDcxNzE0MDAwMFowdTELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRYw
FAYDVQQKDA1NaWNyb3NlYyBMdGQuMRcwFQYDVQRhDA5WQVRIVS0yMzU4NDQ5NzEiMCAGA1UEAwwZ
ZS1Temlnbm8gVExTIFJvb3QgQ0EgMjAyMzCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEAGgP36J8
PKp0iGEKjcJMpQEiFNT3YHdCnAo4YKGMZz6zY+n6kbCLS+Y53wLCMAFSAL/fjO1ZrTJlqwlZULUZ
wmgcAOAFX9pQJhzDrAQixTpN7+lXWDajwRlTEArRzT/vSzUaQ49CE0y5LBqcvjC2xN7cS53kpDzL
Ltmt3999Cd8ukv+ho2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E
FgQUWYQCYlpGePVd3I8KECgj3NXW+0UwHwYDVR0jBBgwFoAUWYQCYlpGePVd3I8KECgj3NXW+0Uw
CgYIKoZIzj0EAwQDgYsAMIGHAkIBLdqu9S54tma4n7Zwf2Z0z+yOfP7AAXmazlIC58PRDHpty7Ve
7hekm9sEdu4pKeiv+62sUvTXK9Z3hBC9xdIoaDQCQTV2WnXzkoYI9bIeCvZlC9p2x1L/Cx6AcCIw
wzPbGO2E14vs7dOoY4G1VnxHx1YwlGhza9IuqbnZLBwpvQy6uWWL
-----END CERTIFICATE-----

View file

@ -3,7 +3,7 @@
"description": "The Kirby core",
"license": "proprietary",
"type": "kirby-cms",
"version": "5.2.2",
"version": "5.4.3",
"keywords": [
"kirby",
"cms",
@ -43,10 +43,10 @@
"getkirby/composer-installer": "^1.2.1",
"laminas/laminas-escaper": "2.18.0",
"michelf/php-smartypants": "1.8.1",
"phpmailer/phpmailer": "7.0.1",
"symfony/polyfill-intl-idn": "1.33.0",
"symfony/polyfill-mbstring": "1.33.0",
"symfony/yaml": "7.4.1"
"phpmailer/phpmailer": "7.1.1",
"symfony/polyfill-intl-idn": "1.38.1",
"symfony/polyfill-mbstring": "1.38.1",
"symfony/yaml": "7.4.13"
},
"replace": {
"symfony/polyfill-php72": "*"

92
kirby/composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ec1a0c2c8ea87efef288a791c674ac53",
"content-hash": "4b4b85db0273b5616f4f99359b7bf084",
"packages": [
{
"name": "christian-riesen/base32",
@ -491,16 +491,16 @@
},
{
"name": "phpmailer/phpmailer",
"version": "v7.0.1",
"version": "v7.1.1",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "360ae911ce62e25e11249f6140fa58939f556ebe"
"reference": "1bc1716a507a65e039d4ac9d9adebbbd0d346e15"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/360ae911ce62e25e11249f6140fa58939f556ebe",
"reference": "360ae911ce62e25e11249f6140fa58939f556ebe",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/1bc1716a507a65e039d4ac9d9adebbbd0d346e15",
"reference": "1bc1716a507a65e039d4ac9d9adebbbd0d346e15",
"shasum": ""
},
"require": {
@ -561,7 +561,7 @@
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v7.0.1"
"source": "https://github.com/PHPMailer/PHPMailer/tree/v7.1.1"
},
"funding": [
{
@ -569,7 +569,7 @@
"type": "github"
}
],
"time": "2025-11-25T07:18:09+00:00"
"time": "2026-05-18T08:06:14+00:00"
},
{
"name": "psr/log",
@ -623,16 +623,16 @@
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.6.0",
"version": "v3.7.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
"reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b",
"reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b",
"shasum": ""
},
"require": {
@ -645,7 +645,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.6-dev"
"dev-main": "3.7-dev"
}
},
"autoload": {
@ -670,7 +670,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0"
},
"funding": [
{
@ -681,25 +681,29 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-25T14:21:43+00:00"
"time": "2026-04-13T15:52:40+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.33.0",
"version": "v1.37.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
"reference": "141046a8f9477948ff284fa65be2095baafb94f2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2",
"reference": "141046a8f9477948ff284fa65be2095baafb94f2",
"shasum": ""
},
"require": {
@ -749,7 +753,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0"
},
"funding": [
{
@ -769,20 +773,20 @@
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
"time": "2026-04-10T16:19:22+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.33.0",
"version": "v1.38.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3"
"reference": "dc21118016c039a66235cf93d96b435ffb282412"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3",
"reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412",
"reference": "dc21118016c039a66235cf93d96b435ffb282412",
"shasum": ""
},
"require": {
@ -836,7 +840,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0"
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1"
},
"funding": [
{
@ -856,20 +860,20 @@
"type": "tidelift"
}
],
"time": "2024-09-10T14:38:51+00:00"
"time": "2026-05-25T15:22:23+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.33.0",
"version": "v1.38.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "3833d7255cc303546435cb650316bff708a1c75c"
"reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
"reference": "3833d7255cc303546435cb650316bff708a1c75c",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b",
"reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b",
"shasum": ""
},
"require": {
@ -921,7 +925,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0"
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0"
},
"funding": [
{
@ -941,20 +945,20 @@
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
"time": "2026-05-25T13:48:31+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.33.0",
"version": "v1.38.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
"reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/14c5439eec4ccff081ac14eca2dc57feb2a66d92",
"reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92",
"shasum": ""
},
"require": {
@ -1006,7 +1010,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.1"
},
"funding": [
{
@ -1026,20 +1030,20 @@
"type": "tidelift"
}
],
"time": "2024-12-23T08:48:59+00:00"
"time": "2026-05-26T12:51:13+00:00"
},
{
"name": "symfony/yaml",
"version": "v7.4.1",
"version": "v7.4.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "24dd4de28d2e3988b311751ac49e684d783e2345"
"reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/24dd4de28d2e3988b311751ac49e684d783e2345",
"reference": "24dd4de28d2e3988b311751ac49e684d783e2345",
"url": "https://api.github.com/repos/symfony/yaml/zipball/a7ec3b1156faf8815db7683ec7c1e7338e6f977c",
"reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c",
"shasum": ""
},
"require": {
@ -1082,7 +1086,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/yaml/tree/v7.4.1"
"source": "https://github.com/symfony/yaml/tree/v7.4.13"
},
"funding": [
{
@ -1102,7 +1106,7 @@
"type": "tidelift"
}
],
"time": "2025-12-04T18:11:45+00:00"
"time": "2026-05-25T06:06:12+00:00"
}
],
"packages-dev": [],

View file

@ -9,7 +9,7 @@ use Kirby\Form\Form;
return [
'fields' => [
'blueprint' => fn (File $file) => $file->blueprint(),
'content' => fn (File $file) => Form::for($file)->values(),
'content' => fn (File $file) => Form::for($file)->toFormValues(),
'dimensions' => fn (File $file) => $file->dimensions()->toArray(),
'dragText' => fn (File $file) => $file->panel()->dragText(),
'exists' => fn (File $file) => $file->exists(),
@ -20,9 +20,17 @@ return [
'mime' => fn (File $file) => $file->mime(),
'modified' => fn (File $file) => $file->modified('c'),
'name' => fn (File $file) => $file->name(),
'next' => fn (File $file) => $file->next(),
'next' => function (File $file) {
$next = $file->next();
if ($next === null || $next->isListable() === false) {
return null;
}
return $next;
},
'nextWithTemplate' => function (File $file) {
$files = $file->templateSiblings()->sorted();
$files = $file->templateSiblings()->sorted()->filter('isListable', true);
$index = $files->indexOf($file);
return $files->nth($index + 1);
@ -31,15 +39,31 @@ return [
'options' => fn (File $file) => $file->panel()->options(),
'panelImage' => fn (File $file) => $file->panel()->image(),
'panelUrl' => fn (File $file) => $file->panel()->url(true),
'prev' => fn (File $file) => $file->prev(),
'prev' => function (File $file) {
$prev = $file->prev();
if ($prev === null || $prev->isListable() === false) {
return null;
}
return $prev;
},
'prevWithTemplate' => function (File $file) {
$files = $file->templateSiblings()->sorted();
$files = $file->templateSiblings()->sorted()->filter('isListable', true);
$index = $files->indexOf($file);
return $files->nth($index - 1);
},
'parent' => fn (File $file) => $file->parent(),
'parents' => fn (File $file) => $file->parents()->flip(),
'parent' => function (File $file) {
$parent = $file->parent();
if ($parent === null || $parent->isListable() === false) {
return null;
}
return $parent;
},
'parents' => fn (File $file) => $file->parents()->flip()->filter('isListable', true),
'size' => fn (File $file) => $file->size(),
'template' => fn (File $file) => $file->template(),
'thumbs' => function ($file) {

View file

@ -1,17 +1,37 @@
<?php
use Kirby\Cms\License;
use Kirby\Exception\PermissionException;
/**
* Page
*/
return [
'fields' => [
'status' => fn (License $license) => $license->status()->value(),
'code' => function (License $license) {
return $this->kirby()->user()->isAdmin() ? $license->code() : $license->code(true);
'status' => function (License $license) {
try {
$this->validateAreaAccess('system');
return $license->status()->value();
} catch (PermissionException) {
return null;
}
},
'type' => fn (License $license) => $license->type()->label(),
'code' => function (License $license) {
try {
$this->validateAreaAccess('system');
return $this->kirby()->user()->isAdmin() ? $license->code() : $license->code(true);
} catch (PermissionException) {
return null;
}
},
'type' => function (License $license) {
try {
$this->validateAreaAccess('system');
return $license->type()->label();
} catch (PermissionException) {
return null;
}
}
],
'type' => License::class,
];

View file

@ -10,11 +10,11 @@ return [
'fields' => [
'blueprint' => fn (Page $page) => $page->blueprint(),
'blueprints' => fn (Page $page) => $page->blueprints(),
'children' => fn (Page $page) => $page->children(),
'content' => fn (Page $page) => Form::for($page)->values(),
'drafts' => fn (Page $page) => $page->drafts(),
'children' => fn (Page $page) => $page->children()->filter('isListable', true),
'content' => fn (Page $page) => Form::for($page)->toFormValues(),
'drafts' => fn (Page $page) => $page->drafts()->filter('isListable', true),
'errors' => fn (Page $page) => $page->errors(),
'files' => fn (Page $page) => $page->files()->sorted(),
'files' => fn (Page $page) => $page->files()->sorted()->filter('isListable', true),
'hasChildren' => fn (Page $page) => $page->hasChildren(),
'hasDrafts' => fn (Page $page) => $page->hasDrafts(),
'hasFiles' => fn (Page $page) => $page->hasFiles(),
@ -23,15 +23,25 @@ return [
'num' => fn (Page $page) => $page->num(),
'options' => fn (Page $page) => $page->panel()->options(['preview']),
'panelImage' => fn (Page $page) => $page->panel()->image(),
'parent' => fn (Page $page) => $page->parent(),
'parents' => fn (Page $page) => $page->parents()->flip(),
'parent' => function (Page $page) {
$parent = $page->parent();
if ($parent === null || $parent->isListable() === false) {
return null;
}
return $parent;
},
'parents' => fn (Page $page) => $page->parents()->flip()->filter('isListable', true),
'previewUrl' => fn (Page $page) => $page->previewUrl(),
'siblings' => function (Page $page) {
if ($page->isDraft() === true) {
return $page->parentModel()->children()->not($page);
$siblings = $page->parentModel()->children()->not($page);
} else {
$siblings = $page->siblings();
}
return $page->siblings();
return $siblings->filter('isListable', true);
},
'slug' => fn (Page $page) => $page->slug(),
'status' => fn (Page $page) => $page->status(),
@ -68,9 +78,7 @@ return [
'content',
'status',
'options',
'next' => ['id', 'slug', 'title'],
'parents' => ['id', 'slug', 'title'],
'prev' => ['id', 'slug', 'title'],
'previewUrl',
'slug',
'title',

View file

@ -10,10 +10,10 @@ return [
'default' => fn () => $this->site(),
'fields' => [
'blueprint' => fn (Site $site) => $site->blueprint(),
'children' => fn (Site $site) => $site->children(),
'content' => fn (Site $site) => Form::for($site)->values(),
'drafts' => fn (Site $site) => $site->drafts(),
'files' => fn (Site $site) => $site->files()->sorted(),
'children' => fn (Site $site) => $site->children()->filter('isListable', true),
'content' => fn (Site $site) => Form::for($site)->toFormValues(),
'drafts' => fn (Site $site) => $site->drafts()->filter('isListable', true),
'files' => fn (Site $site) => $site->files()->sorted()->filter('isListable', true),
'options' => fn (Site $site) => $site->permissions()->toArray(),
'previewUrl' => fn (Site $site) => $site->previewUrl(),
'title' => fn (Site $site) => $site->title()->value(),

View file

@ -1,6 +1,7 @@
<?php
use Kirby\Cms\System;
use Kirby\Exception\PermissionException;
use Kirby\Toolkit\Str;
/**
@ -30,23 +31,26 @@ return [
'requirements' => fn (System $system) => $system->toArray(),
'site' => fn (System $system) => $system->title(),
'slugs' => fn () => Str::$language,
'title' => fn () => $this->site()->title()->value(),
// provide the value even if `site.access` permission is disabled
'title' => fn () => $this->kirby()->site()->title()->value(),
'translation' => function () {
$code = $this->user()?->language() ??
$this->kirby()->panelLanguage();
$kirby = $this->kirby();
$code = $kirby->user()?->language() ??
$kirby->panelLanguage();
return
$this->kirby()->translation($code) ??
$this->kirby()->translation('en');
$kirby->translation($code) ??
$kirby->translation('en');
},
'kirbytext' => fn () => $this->kirby()->option('panel.kirbytext') ?? true,
'user' => fn () => $this->user(),
'version' => function () {
if ($this->user()?->role()->permissions()->for('access', 'system') === true) {
'user' => fn () => $this->user(),
'version' => function () {
try {
$this->validateAreaAccess('system');
return $this->kirby()->version();
} catch (PermissionException) {
return null;
}
return null;
}
],
'type' => System::class,

View file

@ -11,18 +11,34 @@ return [
'fields' => [
'avatar' => fn (User $user) => $user->avatar()?->crop(512),
'blueprint' => fn (User $user) => $user->blueprint(),
'content' => fn (User $user) => Form::for($user)->values(),
'content' => fn (User $user) => Form::for($user)->toFormValues(),
'email' => fn (User $user) => $user->email(),
'files' => fn (User $user) => $user->files()->sorted(),
'files' => fn (User $user) => $user->files()->sorted()->filter('isListable', true),
'id' => fn (User $user) => $user->id(),
'language' => fn (User $user) => $user->language(),
'name' => fn (User $user) => $user->name()->value(),
'next' => fn (User $user) => $user->next(),
'next' => fn (User $user) => $user->next($user->siblings()->filter('isListable', true)),
'options' => fn (User $user) => $user->panel()->options(),
'panelImage' => fn (User $user) => $user->panel()->image(),
'permissions' => fn (User $user) => $user->role()->permissions()->toArray(),
'prev' => fn (User $user) => $user->prev(),
'role' => fn (User $user) => $user->role(),
'permissions' => function (User $user) {
$role = $user->role();
if ($role->isAccessible() === false) {
return null;
}
return $role->permissions()->toArray();
},
'prev' => fn (User $user) => $user->prev($user->siblings()->filter('isListable', true)),
'role' => function (User $user) {
$role = $user->role();
if ($role->isAccessible() === false) {
return null;
}
return $role;
},
'roles' => fn (User $user) => $user->roles(),
'username' => fn (User $user) => $user->username(),
'uuid' => fn (User $user) => $user->uuid()?->toString()

View file

@ -70,7 +70,12 @@ return [
return $files->search($this->requestQuery('q'));
}
return $files->query($this->requestBody());
return $files->query(array_filter([
'limit' => $this->requestBody('limit'),
'offset' => $this->requestBody('offset'),
'paginate' => $this->requestBody('paginate'),
'search' => $this->requestBody('search'),
], fn ($value) => $value !== null));
}
],
[
@ -140,7 +145,12 @@ return [
return $files->search($this->requestQuery('q'));
}
return $files->query($this->requestBody());
return $files->query(array_filter([
'limit' => $this->requestBody('limit'),
'offset' => $this->requestBody('offset'),
'paginate' => $this->requestBody('paginate'),
'search' => $this->requestBody('search'),
], fn ($value) => $value !== null));
}
],
];

View file

@ -1,5 +1,7 @@
<?php
use Kirby\Cms\Find;
/**
* Roles Routes
*/
@ -8,12 +10,10 @@ return [
'pattern' => 'roles',
'method' => 'GET',
'action' => function () {
$kirby = $this->kirby();
return match ($kirby->request()->get('canBe')) {
'changed' => $kirby->roles()->canBeChanged(),
'created' => $kirby->roles()->canBeCreated(),
default => $kirby->roles()
return match ($this->requestQuery('canBe')) {
'changed' => Find::roles()->canBeChanged(),
'created' => Find::roles()->canBeCreated(),
default => Find::roles()
};
}
],
@ -21,7 +21,7 @@ return [
'pattern' => 'roles/(:any)',
'method' => 'GET',
'action' => function (string $name) {
return $this->kirby()->roles()->find($name);
return Find::role($name);
}
]
];

View file

@ -81,7 +81,12 @@ return [
return $pages->search($this->requestQuery('q'));
}
return $pages->query($this->requestBody());
return $pages->query(array_filter([
'limit' => $this->requestBody('limit'),
'offset' => $this->requestBody('offset'),
'paginate' => $this->requestBody('paginate'),
'search' => $this->requestBody('search'),
], fn ($value) => $value !== null));
}
],
[

View file

@ -14,21 +14,16 @@ return [
'auth' => false,
'action' => function () {
$system = $this->kirby()->system();
$model = $this->resolve($system);
if ($this->kirby()->user()) {
return $system;
return $model->view('panel');
}
$info = match ($system->isOk()) {
true => $this->resolve($system)->view('login')->toArray(),
false => $this->resolve($system)->view('troubleshooting')->toArray()
return match ($system->isOk()) {
true => $model->view('login'),
false => $model->view('troubleshooting')
};
return [
'status' => 'ok',
'data' => $info,
'type' => 'model'
];
}
],
[

View file

@ -1,8 +1,7 @@
<?php
use Kirby\Exception\Exception;
use Kirby\Cms\Find;
use Kirby\Filesystem\F;
use Kirby\Toolkit\Str;
/**
* User Routes
@ -13,14 +12,14 @@ return [
'pattern' => 'users',
'method' => 'GET',
'action' => function () {
return $this->users()->sort('username', 'asc', 'email', 'asc');
return Find::users()->sort('username', 'asc', 'email', 'asc');
}
],
[
'pattern' => 'users',
'method' => 'POST',
'action' => function () {
return $this->users()->create($this->requestBody());
return Find::users()->create($this->requestBody());
}
],
[
@ -28,10 +27,15 @@ return [
'method' => 'GET|POST',
'action' => function () {
if ($this->requestMethod() === 'GET') {
return $this->users()->search($this->requestQuery('q'));
return Find::users()->search($this->requestQuery('q'));
}
return $this->users()->query($this->requestBody());
return Find::users()->query(array_filter([
'limit' => $this->requestBody('limit'),
'offset' => $this->requestBody('offset'),
'paginate' => $this->requestBody('paginate'),
'search' => $this->requestBody('search'),
], fn ($value) => $value !== null));
}
],
[
@ -41,7 +45,7 @@ return [
],
'method' => 'GET',
'action' => function (string $id) {
return $this->user($id);
return Find::user($id);
}
],
[
@ -51,7 +55,7 @@ return [
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->update($this->requestBody(), $this->language(), true);
return Find::user($id)->update($this->requestBody(), $this->language(), true);
}
],
[
@ -61,7 +65,7 @@ return [
],
'method' => 'DELETE',
'action' => function (string $id) {
return $this->user($id)->delete();
return Find::user($id)->delete();
}
],
[
@ -71,7 +75,7 @@ return [
],
'method' => 'GET',
'action' => function (string $id) {
return $this->user($id)->avatar();
return Find::user($id)->avatar();
}
],
// @codeCoverageIgnoreStart
@ -84,33 +88,14 @@ return [
'action' => function (string $id) {
return $this->upload(
function ($source, $filename) use ($id) {
$type = F::type($filename);
if ($type !== 'image') {
throw new Exception(
key: 'file.type.invalid',
data: compact('type')
);
}
$user = Find::user($id);
$method = $user->avatar() === null ? 'createAvatar' : 'replaceAvatar';
$mime = F::mime($source);
if (Str::startsWith($mime, 'image/') !== true) {
throw new Exception(
key: 'file.mime.invalid',
data: compact('mime')
);
}
// delete the old avatar
$this->user($id)->avatar()?->delete();
$props = [
'filename' => 'profile.' . F::extension($filename),
'template' => 'avatar',
'source' => $source
];
// move the source file from the temp dir
return $this->user($id)->createFile($props, true);
return $user->$method(
source: $source,
extension: F::extension($filename),
move: true
)->avatar();
},
single: true
);
@ -124,7 +109,7 @@ return [
],
'method' => 'DELETE',
'action' => function (string $id) {
return $this->user($id)->avatar()->delete();
return Find::user($id)->deleteAvatar();
}
],
[
@ -134,7 +119,7 @@ return [
],
'method' => 'GET',
'action' => function (string $id) {
return $this->user($id)->blueprint();
return Find::user($id)->blueprint();
}
],
[
@ -144,7 +129,7 @@ return [
],
'method' => 'GET',
'action' => function (string $id) {
return $this->user($id)->blueprints($this->requestQuery('section'));
return Find::user($id)->blueprints($this->requestQuery('section'));
}
],
[
@ -154,7 +139,7 @@ return [
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changeEmail($this->requestBody('email'));
return Find::user($id)->changeEmail($this->requestBody('email'));
}
],
[
@ -164,7 +149,7 @@ return [
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changeLanguage($this->requestBody('language'));
return Find::user($id)->changeLanguage($this->requestBody('language'));
}
],
[
@ -174,7 +159,7 @@ return [
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changeName($this->requestBody('name'));
return Find::user($id)->changeName($this->requestBody('name'));
}
],
[
@ -184,12 +169,13 @@ return [
],
'method' => 'PATCH',
'action' => function (string $id) {
$user = $this->user($id);
$user = Find::user($id);
$currentUser = $this->kirby()->user();
// validate password of acting user unless they have logged in to reset it;
// always validate password of acting user when changing password of other users
if ($this->session()->get('kirby.resetPassword') !== true || $this->user()->is($user) !== true) {
$this->user()->validatePassword($this->requestBody('currentPassword'));
if ($this->session()->get('kirby.resetPassword') !== true || $user->is($currentUser) !== true) {
$currentUser->validatePassword($this->requestBody('currentPassword'));
}
$result = $user->changePassword($this->requestBody('password'));
@ -210,7 +196,7 @@ return [
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changeRole($this->requestBody('role'));
return Find::user($id)->changeRole($this->requestBody('role'));
}
],
[
@ -219,9 +205,7 @@ return [
'users/(:any)/roles',
],
'action' => function (string $id) {
$kirby = $this->kirby();
$purpose = $kirby->request()->get('purpose');
return $this->user($id)->roles($purpose);
return Find::user($id)->roles();
}
],
[
@ -231,7 +215,7 @@ return [
],
'method' => 'ALL',
'action' => function (string $id, string $fieldName, string|null $path = null) {
return $this->fieldApi($this->user($id), $fieldName, $path);
return $this->fieldApi(Find::user($id), $fieldName, $path);
}
],
[
@ -241,7 +225,7 @@ return [
],
'method' => 'GET',
'action' => function (string $id, string $sectionName) {
if ($section = $this->user($id)->blueprint()->section($sectionName)) {
if ($section = Find::user($id)->blueprint()->section($sectionName)) {
return $section->toResponse();
}
}
@ -253,7 +237,7 @@ return [
],
'method' => 'ALL',
'action' => function (string $id, string $sectionName, string|null $path = null) {
return $this->sectionApi($this->user($id), $sectionName, $path);
return $this->sectionApi(Find::user($id), $sectionName, $path);
}
],
// @codeCoverageIgnoreEnd

View file

@ -59,7 +59,7 @@ return [
'submit' => fn () => (new UserTotpEnableDialog())->submit()
],
'account.totp.disable' => [
'pattern' => '(account)/totp/disable',
...$dialogs['user.totp.disable'],
'pattern' => '(account)/totp/disable',
],
];

View file

@ -9,7 +9,7 @@ return [
'pattern' => 'account',
'action' => fn () => [
'component' => 'k-account-view',
'props' => App::instance()->user()->panel()->props(),
'props' => Find::user()->panel()->props(),
],
],
'account.file' => [

View file

@ -518,7 +518,7 @@ return [
'submit' => function (string $id) {
$kirby = App::instance();
$parentId = $kirby->request()->get('parent');
$parent = (empty($parentId) === true || $parentId === '/' || $parentId === 'site://') ? $kirby->site() : Find::page($parentId);
$parent = (empty($parentId) === true || $parentId === '/' || $parentId === 'site://') ? Find::site() : Find::page($parentId);
$oldPage = Find::page($id);
$newPage = $oldPage->move($parent);
@ -532,6 +532,8 @@ return [
'site.changeTitle' => [
'pattern' => 'site/changeTitle',
'load' => function () {
$site = Find::site();
return [
'component' => 'k-form-dialog',
'props' => [
@ -543,14 +545,14 @@ return [
],
'submitButton' => I18n::translate('rename'),
'value' => [
'title' => App::instance()->site()->title()->value()
'title' => $site->title()->value()
]
]
];
},
'submit' => function () {
$kirby = App::instance();
$kirby->site()->changeTitle($kirby->request()->get('title'));
Find::site()->changeTitle($kirby->request()->get('title'));
return [
'event' => 'site.changeTitle',

View file

@ -1,6 +1,5 @@
<?php
use Kirby\Cms\App;
use Kirby\Cms\Find;
use Kirby\Panel\Ui\Buttons\LanguagesDropdown;
@ -31,7 +30,7 @@ return [
'site.languages' => [
'pattern' => 'site/languages',
'options' => function () {
$site = App::instance()->site();
$site = Find::site();
return (new LanguagesDropdown($site))->options();
}
],

View file

@ -1,6 +1,5 @@
<?php
use Kirby\Cms\App;
use Kirby\Cms\Find;
use Kirby\Exception\PermissionException;
use Kirby\Panel\Ui\Buttons\ViewButtons;
@ -53,7 +52,7 @@ return [
],
'site' => [
'pattern' => 'site',
'action' => fn () => App::instance()->site()->panel()->view()
'action' => fn () => Find::site()->panel()->view()
],
'site.file' => [
'pattern' => 'site/files/(:any)',
@ -64,7 +63,7 @@ return [
'site.preview' => [
'pattern' => 'site/preview/(changes|latest|compare)',
'action' => function (string $versionId) {
$site = App::instance()->site();
$site = Find::site();
$view = $site->panel()->view();
$src = [
'latest' => $site->previewUrl('latest'),

View file

@ -180,44 +180,57 @@ return [
$kirby = App::instance();
$user = Find::user($id);
$fields = [
'currentPassword' => Field::password([
'label' => I18n::translate('user.changePassword.' . ($kirby->user()->is($user) ? 'current' : 'own')),
'autocomplete' => 'current-password',
'help' => I18n::translate('account') . ': ' . App::instance()->user()->email(),
]),
'line' => [
'type' => 'line',
],
'password' => Field::password([
'label' => I18n::translate('user.changePassword.new'),
'autocomplete' => 'new-password',
'help' => I18n::translate('account') . ': ' . $user->email(),
]),
'passwordConfirmation' => Field::password([
'label' => I18n::translate('user.changePassword.new.confirm'),
'autocomplete' => 'new-password'
])
];
// if the currently logged in user tries to change their own password
// and has no password so far, password confirmation can be skipped
if ($user->isLoggedIn() === true && $user->hasPassword() === false) {
unset($fields['currentPassword'], $fields['line']);
}
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'currentPassword' => Field::password([
'label' => I18n::translate('user.changePassword.' . ($kirby->user()->is($user) ? 'current' : 'own')),
'autocomplete' => 'current-password',
'help' => I18n::translate('account') . ': ' . App::instance()->user()->email(),
]),
'line' => [
'type' => 'line',
],
'password' => Field::password([
'label' => I18n::translate('user.changePassword.new'),
'autocomplete' => 'new-password',
'help' => I18n::translate('account') . ': ' . $user->email(),
]),
'passwordConfirmation' => Field::password([
'label' => I18n::translate('user.changePassword.new.confirm'),
'autocomplete' => 'new-password'
])
],
'fields' => $fields,
'submitButton' => I18n::translate('change'),
]
];
},
'submit' => function (string $id) {
$kirby = App::instance();
$request = $kirby->request();
$kirby = App::instance();
$request = $kirby->request();
$user = Find::user($id);
$currentPassword = $request->get('currentPassword');
$password = $request->get('password');
$passwordConfirmation = $request->get('passwordConfirmation');
// if the currently logged in user tries to change their own password
// and has no password so far, password confirmation can be skipped
$canSkipConfirmation = $user->isLoggedIn() === true && $user->hasPassword() === false;
// validate the current password of the acting user
try {
$kirby->user()->validatePassword($currentPassword);
if ($canSkipConfirmation === false) {
$kirby->user()->validatePassword($currentPassword);
}
} catch (Exception) {
// catching and re-throwing exception to avoid automatic
// sign-out of current user from the Panel

View file

@ -12,7 +12,7 @@ return [
'action' => function () {
$kirby = App::instance();
$role = $kirby->request()->get('role');
$roles = $kirby->roles()->toArray(fn ($role) => [
$roles = Find::roles()->toArray(fn ($role) => [
'id' => $role->id(),
'title' => $role->title(),
]);

View file

@ -80,12 +80,14 @@ return [
$template = $mediaRoot . '/{{ name }}{{ attributes }}.{{ extension }}';
$thumbRoot = (new Filename($file->root(), $template, $options))->toString();
$thumbName = basename($thumbRoot);
$job = $mediaRoot . '/.jobs/' . $thumbName . '.json';
// check if the thumb already exists
if (file_exists($thumbRoot) === false) {
// check if the thumb or job file already exists
if (
file_exists($thumbRoot) === false &&
file_exists($job) === false
) {
// if not, create job file
$job = $mediaRoot . '/.jobs/' . $thumbName . '.json';
try {
Data::write(
$job,
@ -356,11 +358,31 @@ return [
$kirby->option('thumbs.driver', 'gd'),
$kirby->option('thumbs', [])
);
$options = $darkroom->preprocess($src, $options);
$root = (new Filename($src, $dst, $options))->toString();
$options = $darkroom->preprocess($src, $options);
$root = (new Filename($src, $dst, $options))->toString();
$extension = F::extension($root);
F::copy($src, $root, true);
$darkroom->process($root, $options);
// generate the thumb in a temp file so broken output never appears at the final path
$tempRoot = F::dirname($root) . '/' . F::name($root) . '.tmp-' . uniqid();
// keep the original extension so the darkroom can infer the output format.
if ($extension !== '') {
$tempRoot .= '.' . $extension;
}
F::copy($src, $tempRoot, true);
try {
$darkroom->process($tempRoot, $options);
// move the finished file into place so the final path
// only appears once generation succeeded
F::move($tempRoot, $root, true);
} catch (Throwable $e) {
// clean up the temp file so failed jobs don't leave broken output behind
F::remove($tempRoot);
throw $e;
}
return $root;
},
@ -397,6 +419,9 @@ return [
if ($parts[0] ?? null) {
$page = $kirby->site()->find($parts[0]);
// get the extension of the url, if there is one, to add it back later
$extension = F::extension($parts[0]);
} else {
$page = $kirby->site()->page();
}
@ -404,6 +429,11 @@ return [
if ($page) {
$path = $page->url($language);
// add the extension back to the url, if there was one
if (isset($extension) === true && $extension !== '') {
$path .= '.' . $extension;
}
if (isset($parts[1]) === true) {
$path .= '#' . $parts[1];
}

View file

@ -1,5 +1,7 @@
<?php
use Kirby\Sane\Sane;
return [
'props' => [
/**
@ -17,7 +19,9 @@ return [
],
'computed' => [
'value' => function () {
return trim($this->value ?? '');
$value = trim($this->value ?? '');
$value = Sane::sanitizeProseMirrorFields($value);
return $value;
}
]
];

View file

@ -59,12 +59,13 @@ return [
);
}
$parent = $this->uploadParent($params['parent'] ?? null);
$parent = $this->uploadParent($params['parent'] ?? null);
$template = $params['template'] ?? null;
return $api->upload(function ($source, $filename) use ($parent, $params, $map) {
return $api->upload(function ($source, $filename, $template) use ($parent, $map) {
$props = [
'source' => $source,
'template' => $params['template'] ?? null,
'template' => $template,
'filename' => $filename,
];
@ -78,7 +79,7 @@ return [
}
return $map($file, $parent);
});
}, template: $template);
},
'uploadParent' => function (string|null $parentQuery = null) {
$parent = $this->model();

View file

@ -107,7 +107,7 @@ return [
key: 'validation.time.between',
data: [
'min' => $min->format($format),
'max' => $min->format($format)
'max' => $max->format($format)
]
);
}

View file

@ -63,13 +63,7 @@ return [
'computed' => [
'value' => function () {
$value = trim($this->value ?? '');
$value = Sane::sanitize($value, 'html');
// convert non-breaking spaces to HTML entity
// as that's how ProseMirror handles it internally;
// will allow comparing saved and current content
$value = str_replace(' ', '&nbsp;', $value);
$value = Sane::sanitizeProseMirrorFields($value);
return $value;
}
],

View file

@ -202,7 +202,7 @@ if (Helpers::hasOverride('go') === false) { // @codeCoverageIgnore
*/
function go(string $url = '/', int $code = 302): never
{
Response::go($url, $code);
Response::go($url, $code); // @codeCoverageIgnore
}
}

View file

@ -251,6 +251,9 @@ return function (App $app) {
Field $field,
string $separator = 'yaml'
) use ($app): Pages {
// always pass at least two arguments even if the
// data is empty so that `$site->find()` always
// returns a collection, not a single page
return $app->site()->find(
false,
false,
@ -320,6 +323,9 @@ return function (App $app) {
Field $field,
string $separator = 'yaml'
) use ($app): Users {
// always pass at least two arguments even if the
// data is empty so that `$users->find()` always
// returns a collection, not a single user
return $app->users()->find(
false,
false,

View file

@ -204,6 +204,8 @@
"error.page.undefined": "\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430\u0442\u0430 \u043d\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0431\u044a\u0434\u0435 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0430",
"error.page.update.permission": "Не можете да обновите \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Не можете да добавяте повече от {max} файлa в секция \"{section}\"",
"error.section.files.max.singular": "Не можете да добавяте повече от един файл в секция \"{section}\"",
"error.section.files.min.plural": "The \"{section}\" section requires at least {min} files",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Заглавието е задължително",
"error.site.changeTitle.permission": "Не може да променяте заглавието на сайта",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Нямате права за да обновите сайта",
"error.structure.validation": "There's an error on the \"{field}\" field in row {index}",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Previous",
"preview": "Preview",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Published",
@ -786,6 +791,7 @@
"user.create": "Добавете нов потребител",
"user.delete": "Изтрийте потребителя",
"user.delete.confirm": "Сигурни ли сте, че искате да изтриете <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Потребители",

View file

@ -1,5 +1,5 @@
{
"account": "Account",
"account": "Račun",
"account.changeName": "Promijeni svoje ime",
"account.delete": "Obriši svoj račun",
"account.delete.confirm": "Da li stvarno želite obrisati svoj račun? Odmah ćete biti odjavljeni. Vaš račun neće biti moguće oporaviti.",
@ -16,7 +16,7 @@
"changes": "Promjene",
"confirm": "Ok",
"collapse": "Skupi",
"collapse.all": "Collapse all",
"collapse.all": "Skupi sve",
"color": "Boja",
"coordinates": "Koordinate",
"copy": "Kopiraj",
@ -44,8 +44,8 @@
"delete": "Obriši",
"delete.all": "Obriši sve",
"deselect": "Deselect",
"deselect.all": "Deselect all",
"deselect": "Poništi odabir",
"deselect.all": "Poništi sve",
"dialog.fields.empty": "Ovaj dijalog nema polja",
"dialog.files.empty": "Nema datoteka za odabir",
@ -204,6 +204,8 @@
"error.page.undefined": "Stranica nije pronađena",
"error.page.update.permission": "Nemate dozvolu da uredite \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Nije moguće dodati više od {max} datoteka u \"{section}\" sekciju",
"error.section.files.max.singular": "Nije moguće dodati više od jedne datoteke u \"{section}\" sekciju",
"error.section.files.min.plural": "Sekcija \"{section}\" zahtjeva najmanje {min} datoteka",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "The title must not be empty",
"error.site.changeTitle.permission": "Nemate dozvolu da promijenite naslov stranice",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Nemate dozvolu da uredite stranicu",
"error.structure.validation": "Postoji greška na \"{field}\" polju u redu {index}",
@ -298,12 +301,12 @@
"error.validation.uuid": "Unesite važeći UUID",
"error.validation.url": "Unesi važeći URL",
"error.version.discard.permission": "You are not allowed to discard this version",
"error.version.publish.permission": "You are not allowed to publish this version",
"error.version.save.permission": "You are not allowed to change this version",
"error.version.discard.permission": "Nemate dozvolu da odbacite ovu verziju",
"error.version.publish.permission": "Nemate dozvolu da objavite ovu verziju",
"error.version.save.permission": "Nemate dozvolu da promenite ovu verziju",
"expand": "Proširi",
"expand.all": "Expand all",
"expand.all": "Proširi sve",
"field.invalid": "Polje je nevažeće",
"field.required": "Polje je obavezno",
@ -384,7 +387,7 @@
"field.structure.delete.confirm": "Da li stvarno želite obrisati ovaj red?",
"field.structure.delete.confirm.all": "Da li stvarno želite obrisati sve unose?",
"field.structure.delete.confirm.selected": "Do you really want to delete the selected entries?",
"field.structure.delete.confirm.selected": "Da li stvarno želite obrisati izabrane unose?",
"field.structure.empty": "Još nema unosa",
"field.users.empty": "Niti jedan korisnik još nije odabran",
@ -458,12 +461,12 @@
"language.variables.empty": "Još nema prijevoda",
"language.variable.delete.confirm": "Da li stvarno želite da obrišete varijablu za {key}?",
"language.variable.entries": "Values",
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
"language.variable.entries": "Vrijednosti",
"language.variable.entries.help": "Svaki string će se koristiti za odgovarajući brojač, npr. tri stringa će se podudarati da bi se izračunao brojač <code>0</code>, <code>1</code>, <code>2 i više</code>. Koristite <code>{count}</code> zamensko mjesto za umetanje stvarnog broja.",
"language.variable.key": "Ključ",
"language.variable.multiple": "Countable?",
"language.variable.multiple.text": "Use different translation strings",
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
"language.variable.multiple": "Brojivo?",
"language.variable.multiple.text": "Koristite različite stringove za prevođenje",
"language.variable.multiple.help": "Možete koristiti različite vrijednosti ovisno o broju koji prosljeđujete zajedno s varijablom jezika, što vam omogućava kreiranje dinamičkih prijevoda, npr. jednine i množine.",
"language.variable.notFound": "Varijablu nije moguće pronaći",
"language.variable.value": "Vrijednost",
@ -483,7 +486,7 @@
"license.code": "Kod",
"license.code.help": "Primili ste vašu licencu nakon kupovine putem e-maila. Molimo da je kopirate i zalijepite ovdje.",
"license.code.label": "Unesite vašu licencu",
"license.remove.text": "<p>Removing the license will irreversibly delete the license file from this site. You can then activate this site with a different license key or re-register the same license key if the domain remains the same.</p><p>To change the domain associated with the license, please contact the Kirby team. <a href='https://getkirby.com/license'>Read more &rarr;</a></p>",
"license.remove.text": "<p>Uklanjanje licence će nepovratno izbrisati datoteku licence sa ove stranice. Zatim možete aktivirati ovu stranicu s drugim licencnim ključem ili ponovo registrirati isti licencni ključ ako domena ostane ista.</p><p>Za promjenu domene povezane s licencom, obratite se Kirby timu. <a href='https://getkirby.com/license'>Pročitajte više &rarr;</a></p>",
"license.status.active.info": "Uključuje nove glavne verzije do {date}",
"license.status.active.label": "Važeća licenca",
"license.status.demo.info": "Ovo je demo instalacija",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugini",
"prev": "Previous",
"preview": "Pregled",
"preview.browser.scroll": "Sinhronizacija skrolanja",
"publish": "Objavi",
"published": "Javno",
@ -671,7 +676,7 @@
"security": "Sigurnost",
"select": "Odaberi",
"select.all": "Select all",
"select.all": "Odaberi sve",
"server": "Server",
"settings": "Postavke",
"show": "Prikaži",
@ -780,12 +785,13 @@
"user.changePassword.current": "Trenutna šifra",
"user.changePassword.new": "Nova šifra",
"user.changePassword.new.confirm": "Potvrdi novu šifru...",
"user.changePassword.own": "Your own password",
"user.changePassword.own": "Vaša vlastita šifra",
"user.changeRole": "Promijeni ulogu",
"user.changeRole.select": "Odaberi novu ulogu",
"user.create": "Dodaj novog korisnika",
"user.delete": "Obriši ovog korisnika",
"user.delete.confirm": "Da li stvarno želite obrisati <br><strong>{email}</strong>?",
"user.setPassword": "Postavi šifru",
"users": "Korisnici",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "La p\u00e0gina no s'ha trobat",
"error.page.update.permission": "No tens permís per actualitzar \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "No has d'afegir més de {max} fitxers a la secció \"{section}\"",
"error.section.files.max.singular": "No podeu afegir més d'un fitxer a la secció \"{section}\"",
"error.section.files.min.plural": "La secció \"{section}\" requereix almenys {min} fitxer",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "El títol no pot estar buit",
"error.site.changeTitle.permission": "No tens permís per canviar el títol del lloc web",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "No tens permís per actualitzar el lloc web",
"error.structure.validation": "There's an error on the \"{field}\" field in row {index}",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Anterior",
"preview": "Preview",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Publicat",
@ -786,6 +791,7 @@
"user.create": "Afegir un nou usuari",
"user.delete": "Eliminar aquest usuari",
"user.delete.confirm": "Segur que voleu eliminar <br> <strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Usuaris",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "Str\u00e1nku se nepoda\u0159ilo nal\u00e9zt.",
"error.page.update.permission": "Nemáte dovoleno upravit \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Sekce \"{section}\" nesmí obsahovat více jak {max} souborů",
"error.section.files.max.singular": "Sekce \"{section}\" může obsahovat nejvýše jeden soubor",
"error.section.files.min.plural": "Sekce \"{section}\" vyžaduje nejméně {min} souborů",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Titulek nesmí být prázdný",
"error.site.changeTitle.permission": "Nemáte dovoleno změnit titulek stránky",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Nemáte dovoleno upravit stránku",
"error.structure.validation": "Chyba v poli \"{field}\" na řádku {index}",
@ -635,7 +638,9 @@
"plugin": "Doplněk",
"plugins": "Doplňky",
"prev": "Předchozí",
"preview": "Náhled",
"preview.browser.scroll": "Sync scroll",
"publish": "Zveřejnit",
"published": "Zveřejněno",
@ -786,6 +791,7 @@
"user.create": "Přidat nového uživatele",
"user.delete": "Smazat tohoto uživatele",
"user.delete.confirm": "Opravdu chcete smazat tohoto u\u017eivatele?",
"user.setPassword": "Set password",
"users": "Uživatelé",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "Siden kunne ikke findes",
"error.page.update.permission": "Du har ikke tilladelse til at opdatere \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Du kan ikk tilføje mere end {max} filer til \"{section}\" sektionen",
"error.section.files.max.singular": "Du kan ikke tilføje mere end een fil til \"{section}\" sektionen",
"error.section.files.min.plural": "Sektionen \"{section}\" kræver mindst {min} filer",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Titlen kan ikke være tom",
"error.site.changeTitle.permission": "Du har ikke tilladelse til at ændre titlen på sitet",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Du har ikke tilladelse til at opdatere sitet",
"error.structure.validation": "There's an error on the \"{field}\" field in row {index}",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Forrige",
"preview": "Forhåndsvisning",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Udgivede",
@ -786,6 +791,7 @@
"user.create": "Tilføj en ny bruger",
"user.delete": "Slet denne bruger",
"user.delete.confirm": "\u00d8nsker du virkelig at slette denne bruger?",
"user.setPassword": "Set password",
"users": "Brugere",

View file

@ -109,7 +109,7 @@
"error.entries.supports": "Der Feldtyp \"{type}\" wird im Entries Feld nicht unterstützt.",
"error.entries.validation": "Fehler im Feld \"{field}\" in Zeile {index}",
"error.email.preset.notFound": "Die E-Mailvorlage \"{name}\" wurde nicht gefunden",
"error.email.preset.notFound": "Die E-Mail-Vorlage \"{name}\" wurde nicht gefunden",
"error.field.converter.invalid": "Ungültiger Konverter: \"{converter}\"",
"error.field.link.options": "Ungültige Optionen: {options}",
@ -204,6 +204,8 @@
"error.page.undefined": "Die Seite konnte nicht gefunden werden",
"error.page.update.permission": "Du kannst die Seite \"{slug}\" nicht editieren",
"error.role.notFound": "Die Rolle \"{name}\" wurde nicht gefunden",
"error.section.files.max.plural": "Bitte füge nicht mehr als {max} Dateien zum Bereich \"{section}\" hinzu",
"error.section.files.max.singular": "Bitte füge nicht mehr als eine Datei zum Bereich \"{section}\" hinzu",
"error.section.files.min.plural": "Der Bereich \"{section}\" benötigt mindestens {min} Dateien",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Bitte gib einen Titel an",
"error.site.changeTitle.permission": "Du kannst den Titel der Seite nicht ändern",
"error.site.notAccessible": "Die Seite ist nicht zugänglich",
"error.site.update.permission": "Du darfst die Seite nicht bearbeiten",
"error.structure.validation": "Fehler im Feld \"{field}\" in Zeile {index}",
@ -477,12 +480,12 @@
"license.activate": "Aktiviere Kirby jetzt",
"license.activate.label": "Bitte aktiviere deine Lizenz",
"license.activate.domain": "Deine Lizenz wird für die Domain <strong>{host}</strong> aktiviert.",
"license.activate.local": "Du bist dabei, deine Kirby Lizenz für die lokale Domain <strong>{host}</strong> zu aktivieren. Falls diese Seite später unter einer anderen Domain veröffentlicht wird, solltest du sie erst dort aktivieren. Falls {host} die Domain ist, die du für deine Lizenz nutzen möchtest, fahre bitte fort. ",
"license.activate.local": "Du bist dabei, deine Kirby-Lizenz für die lokale Domain <strong>{host}</strong> zu aktivieren. Falls diese Seite später unter einer anderen Domain veröffentlicht wird, solltest du sie erst dort aktivieren. Falls {host} die Domain ist, die du für deine Lizenz nutzen möchtest, fahre bitte fort. ",
"license.activated": "Aktiviert",
"license.buy": "Kaufe eine Lizenz",
"license.code": "Code",
"license.code.help": "Du hast deinen Lizenz Code nach dem Kauf per Email bekommen. Bitte kopiere sie aus der Email und füge sie hier ein. ",
"license.code.label": "Bitte gib deinen Lizenzcode ein",
"license.code.help": "Du hast deinen Lizenzschlüssel nach dem Kauf per E-Mail bekommen. Bitte kopiere ihn aus der E-Mail und füge ihn hier ein. ",
"license.code.label": "Bitte gib deinen Lizenzschlüssel ein",
"license.remove.text": "<p>Durch das Entfernen der Lizenz wird die Lizenzdatei unwiderruflich von dieser Website gelöscht. Du kannst diese Website dann mit einem anderen Lizenzschlüssel aktivieren oder denselben Lizenzschlüssel erneut registrieren, wenn die Domain unverändert bleibt.</p> <p>Um die mit der Lizenz verknüpfte Domain zu ändern, wende dich bitte an das Kirby-Team. <a href='https://getkirby.com/license'>Weiterlesen &rarr;</a></p>",
"license.status.active.info": "Beinhaltet neue Major Versionen bis {date}",
"license.status.active.label": "Gültige Lizenz",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Vorheriger Eintrag",
"preview": "Vorschau",
"preview.browser.scroll": "Scrollen synchronisieren",
"publish": "Veröffentlichen",
"published": "Veröffentlicht",
@ -786,6 +791,7 @@
"user.create": "Neuen Account anlegen",
"user.delete": "Account löschen",
"user.delete.confirm": "Willst du den Account <br><strong>{email}</strong> wirklich löschen?",
"user.setPassword": "Passwort anlegen",
"users": "Accounts",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "Δεν ήταν δυνατή η εύρεση της σελίδας",
"error.page.update.permission": "Δεν επιτρέπεται η ενημέρωση της σελίδας \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Δεν πρέπει να προσθέσετε περισσότερα από {max} αρχεία στην ενότητα \"{section}\"",
"error.section.files.max.singular": "Δεν πρέπει να προσθέσετε περισσότερα από ένα αρχεία στην ενότητα \"{section}\"",
"error.section.files.min.plural": "The \"{section}\" section requires at least {min} files",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Ο τίτλος δεν μπορεί να είναι κενός",
"error.site.changeTitle.permission": "Δεν επιτρέπεται να αλλάξετε τον τίτλο του ιστότοπου",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Δεν επιτρέπεται η ενημέρωση του ιστότοπου",
"error.structure.validation": "There's an error on the \"{field}\" field in row {index}",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Προηγούμενο",
"preview": "Preview",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Δημοσιευμένο",
@ -786,6 +791,7 @@
"user.create": "Προσθήκη νέου χρήστη",
"user.delete": "Διαγραφή χρήστη",
"user.delete.confirm": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03c3\u03af\u03b3\u03bf\u03c5\u03c1\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03b3\u03c1\u03ac\u03c8\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bd \u03c4\u03bf\u03bd \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7;",
"user.setPassword": "Set password",
"users": "Χρήστες",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "The page cannot be found",
"error.page.update.permission": "You are not allowed to update \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "You must not add more than {max} files to the \"{section}\" section",
"error.section.files.max.singular": "You must not add more than one file to the \"{section}\" section",
"error.section.files.min.plural": "The \"{section}\" section requires at least {min} files",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "The title must not be empty",
"error.site.changeTitle.permission": "You are not allowed to change the title of the site",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "You are not allowed to update the site",
"error.structure.validation": "There's an error on the \"{field}\" field in row {index}",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Previous",
"preview": "Preview",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Published",
@ -786,6 +791,7 @@
"user.create": "Add a new user",
"user.delete": "Delete this user",
"user.delete.confirm": "Do you really want to delete <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Users",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "La paĝo ne estas trovebla",
"error.page.update.permission": "Vi ne rajtas ĝisdatigi \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Vi devas aldoni maksimume {max} dosierojn al sekcio \"{section}\"",
"error.section.files.max.singular": "Vi devas aldoni maksimume unu dosieron al sekcio \"{section}\"",
"error.section.files.min.plural": "La sekcio \"{section}\" bezonas almenaŭ {min} dosierojn",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "La titolo ne rajtas esti malplena",
"error.site.changeTitle.permission": "Vi ne rajtas ŝanĝi la titolon de la retejo",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Vi ne rajtas ĝisdatigi la retejon",
"error.structure.validation": "There's an error on the \"{field}\" field in row {index}",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Kromprogramoj",
"prev": "Antaŭe",
"preview": "Antaŭrigardi",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Publikigita",
@ -786,6 +791,7 @@
"user.create": "Aldoni novan uzanton",
"user.delete": "Forigi ĉi tiun uzanton",
"user.delete.confirm": "Ĉu vi certe volas forigi <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Uzantoj",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "La p\u00e1gina no fue encontrada",
"error.page.update.permission": "No tienes permiso para actualizar \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "No debes agregar más de {max} archivos a la sección \"{section}\"",
"error.section.files.max.singular": "No debes agregar más de un archivo a la sección \"{section}\"",
"error.section.files.min.plural": "La sección \"{section}\" requiere al menos {min} archivos",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "El título no debe estar vacío.",
"error.site.changeTitle.permission": "No tienes permiso para cambiar el título del sitio",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "No tienes permiso de actualizar el sitio",
"error.structure.validation": "There's an error on the \"{field}\" field in row {index}",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Anterior",
"preview": "Previsualizar",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Publicado",
@ -786,6 +791,7 @@
"user.create": "Agregar un nuevo usuario",
"user.delete": "Eliminar este usuario",
"user.delete.confirm": "¿Estás seguro que deseas eliminar <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Usuarios",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "No se puede encontrar la página",
"error.page.update.permission": "No tienes permiso para actualizar \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "No debes agregar más de {max} archivos a la sección \"{section}\"",
"error.section.files.max.singular": "No debes agregar más de 1 archivo a la sección \"{section}\"",
"error.section.files.min.plural": "La sección \"{section}\" requiere al menos {min} archivos",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "El título no debe estar vacío.",
"error.site.changeTitle.permission": "No tienes permiso para cambiar el título del sitio",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "No tienes permiso para actualizar el sitio",
"error.structure.validation": "There's an error on the \"{field}\" field in row {index}",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Anterior",
"preview": "Previsualizar",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Publicadas",
@ -786,6 +791,7 @@
"user.create": "Añadir un nuevo usuario",
"user.delete": "Eliminar este usuario",
"user.delete.confirm": "¿Realmente quieres eliminar <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Usuarios",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "صفحه مورد نظر پیدا نشد",
"error.page.update.permission": "شما اجازه بروزرسانی «{slug}» را ندارید",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "نباید بیش از {max} فایل به بخش «{section}» اضافه کنید",
"error.section.files.max.singular": "نباید بیش از یک فایل به بخش «{section}» اضافه کنید",
"error.section.files.min.plural": "The \"{section}\" section requires at least {min} files",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "عنوان اجباری است",
"error.site.changeTitle.permission": "شما اجازه تغییر عنوان سایت را ندارید",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "شما اجازه بروزرسانی سایت را ندارید",
"error.structure.validation": "There's an error on the \"{field}\" field in row {index}",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "قبلی",
"preview": "Preview",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "منتشر شده",
@ -786,6 +791,7 @@
"user.create": "افزودن کاربر جدید",
"user.delete": "حذف کاربر جاری",
"user.delete.confirm": "آیا واقعا میخواهید<strong> {email}</strong> را حذف کنید؟",
"user.setPassword": "Set password",
"users": "کاربران",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "Sivua ei l\u00f6ytynyt",
"error.page.update.permission": "Sinulla ei ole oikeutta päivittää sivua \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Et voi lisätä enemmän kuin {max} tiedostoa osioon \"{section}\"",
"error.section.files.max.singular": "Et voi lisätä enempää kuin yhden tiedoston osioon \"{section}\"",
"error.section.files.min.plural": "Osio \"{section}\" vaatii ainakin {min} tiedostoa",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Nimi ei voi olla tyhjä",
"error.site.changeTitle.permission": "Sinulla ei ole oikeutta päivittää sivuston nimeä",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Sinulla ei ole oikeutta päivittää sivuston tietoja",
"error.structure.validation": "There's an error on the \"{field}\" field in row {index}",
@ -635,7 +638,9 @@
"plugin": "Liitännäinen",
"plugins": "Liitännäiset",
"prev": "Edellinen",
"preview": "Esikatselu",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Julkaistut",
@ -786,6 +791,7 @@
"user.create": "Lisää uusi käyttäjä",
"user.delete": "Poista tämä käyttäjä",
"user.delete.confirm": "Haluatko varmsti poistaa käyttäjän <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Käyttäjät",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "La page na pu être trouvée",
"error.page.update.permission": "Vous nêtes pas autorisé à modifier « {slug} »",
"error.role.notFound": "Le rôle « {name} » na pu être trouvé",
"error.section.files.max.plural": "Vous ne pouvez ajouter plus de {max} fichier(s) à la section « {section} »",
"error.section.files.max.singular": "Vous ne pouvez ajouter plus dun fichier à la section « {section} »",
"error.section.files.min.plural": "La section « {section} » requiert au moins {min} fichiers",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Le titre ne peut être vide",
"error.site.changeTitle.permission": "Vous nêtes pas autorisé à modifier le titre du site",
"error.site.notAccessible": "Le site nest pas accessible",
"error.site.update.permission": "Vous nêtes pas autorisé à modifier le contenu global du site",
"error.structure.validation": "Il y a une erreur dans le champ « {field} » de la rangée {index}",
@ -298,9 +301,9 @@
"error.validation.uuid": "Veuillez saisir un UUID valide",
"error.validation.url": "Veuillez saisir une URL valide",
"error.version.discard.permission": "You are not allowed to discard this version",
"error.version.publish.permission": "You are not allowed to publish this version",
"error.version.save.permission": "You are not allowed to change this version",
"error.version.discard.permission": "Vous nêtes pas autorisé à supprimer cette version",
"error.version.publish.permission": "Vous nêtes pas autorisé à publier cette version",
"error.version.save.permission": "Vous nêtes pas autorisé à modifier cette version",
"expand": "Déplier",
"expand.all": "Tout déplier",
@ -315,7 +318,7 @@
"field.blocks.delete.confirm.all": "Voulez-vous vraiment supprimer tous les blocs ?",
"field.blocks.delete.confirm.selected": "Voulez-vous vraiment supprimer les blocs sélectionnés ?",
"field.blocks.empty": "Pas encore de blocs",
"field.blocks.fieldsets.empty": "Pas encore densembles de champs",
"field.blocks.fieldsets.empty": "Pas encore densembles de champs",
"field.blocks.fieldsets.label": "Veuillez sélectionner un type de bloc…",
"field.blocks.fieldsets.paste": "Pressez <kbd>{{ shortcut }}</kbd> pour importer des dispositions ou blocs depuis votre presse-papier <small>Seuls ceux autorisés dans le champ actuel seront insérés.</small>",
"field.blocks.gallery.name": "Galerie",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Précédent",
"preview": "Prévisualiser",
"preview.browser.scroll": "Synchroniser le défilement",
"publish": "Publier",
"published": "Publié",
@ -786,6 +791,7 @@
"user.create": "Ajouter un nouvel utilisateur",
"user.delete": "Supprimer cet utilisateur",
"user.delete.confirm": "Voulez-vous vraiment supprimer <br><strong>{email}</strong> ?",
"user.setPassword": "Définir un mot de passe",
"users": "Utilisateurs",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "Az oldal nem tal\u00e1lhat\u00f3",
"error.page.update.permission": "Nincs jogosultságod a(z) \"{slug}\" oldal frissítéséhez",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Maximum {max} fájlt adhatsz hozzá a(z) \"{section}\" szekcióhoz",
"error.section.files.max.singular": "Nem adhatsz hozzá egynél több fájlt a(z) \"{section}\" szekcióhoz",
"error.section.files.min.plural": "A \"{section}\" szakasz legalább {min} fájlt igényel",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "A cím nem lehet üres",
"error.site.changeTitle.permission": "Nincs jogosultságod megváltoztatni az honlap címét",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Nincs jogosultságod frissíteni a honlapot",
"error.structure.validation": "There's an error on the \"{field}\" field in row {index}",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Pluginek",
"prev": "Előző",
"preview": "Előnézet",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Publikálva",
@ -786,6 +791,7 @@
"user.create": "Új felhasználó hozzáadása",
"user.delete": "Felhasználó törlése",
"user.delete.confirm": "Biztos törlöd ezt a felhasználót: <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Felhasználók",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "Halaman tidak dapat ditemukan",
"error.page.update.permission": "Anda tidak diizinkan memperbaharui \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Anda hanya boleh menambahkan maksimal {max} berkas ke bagian \"{section}\"",
"error.section.files.max.singular": "Anda hanya boleh menambahkan satu berkas ke bagian \"{section}\"",
"error.section.files.min.plural": "Bagian \"{section}\" setidaknya memiliki {min} berkas",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Judul harus diisi",
"error.site.changeTitle.permission": "Anda tidak diizinkan mengubah judul situs",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Anda tidak diizinkan memperbaharui situs",
"error.structure.validation": "Ada kesalahan pada bidang \"{field}\" di baris {index}",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Sebelumnya",
"preview": "Pratinjau",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Dipublikasikan",
@ -786,6 +791,7 @@
"user.create": "Tambah pengguna baru",
"user.delete": "Hapus pengguna ini",
"user.delete.confirm": "Anda yakin menghapus <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Pengguna",

View file

@ -1,5 +1,5 @@
{
"account": "Account",
"account": "Reikningur",
"account.changeName": "Breyta nafninu þínu",
"account.delete": "Eyða notandareikning þínum",
"account.delete.confirm": "Ertu alveg viss um að þú viljir endanlega eyða reikningnum þínum? Þú munt verða útskráð/ur án tafar. Ómögulegt verður að endurheimta reikninginn þinn.",
@ -16,7 +16,7 @@
"changes": "Breytingar",
"confirm": "OK",
"collapse": "Fella",
"collapse.all": "Collapse all",
"collapse.all": "Fella allt",
"color": "Litur",
"coordinates": "Hnit",
"copy": "Afrita",
@ -44,8 +44,8 @@
"delete": "Eyða",
"delete.all": "Eyða hreint öllu",
"deselect": "Deselect",
"deselect.all": "Deselect all",
"deselect": "Afvelja",
"deselect.all": "Afvelja allt",
"dialog.fields.empty": "Þessi valmynd hefur engin svið",
"dialog.files.empty": "Engar skrár til að velja úr",
@ -204,6 +204,8 @@
"error.page.undefined": "Síðan fannst ekkert",
"error.page.update.permission": "Þú mátt ekkert uppfæra síðuna \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Ekki fleiri en {max} skrár í \"{section}\" svæðið",
"error.section.files.max.singular": "Aðeins ein skrá í \"{section}\" svæðið",
"error.section.files.min.plural": "\"{section}\" svæðið krefst a.m.k. {min} skrá sem innihalds",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Ekki skilja titilinn eftir tóman",
"error.site.changeTitle.permission": "Þú mátt ekkert breyta titil vefsvæðisins",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Þú mátt ekkert uppfæra vefsvæðið",
"error.structure.validation": "Það er villa í \"{field}\" sviðinu í röð {index}",
@ -298,12 +301,12 @@
"error.validation.uuid": "Vinsamlegast gillt UUID (Notandakenni)",
"error.validation.url": "Ákjósanleg vefslóð",
"error.version.discard.permission": "You are not allowed to discard this version",
"error.version.publish.permission": "You are not allowed to publish this version",
"error.version.save.permission": "You are not allowed to change this version",
"error.version.discard.permission": "Þú mátt ekki hunsa þessa útgáfu",
"error.version.publish.permission": "Þú mátt ekki gefa út þessa útgáfu",
"error.version.save.permission": "Þú mátt ekki breyta þessari útgáfu",
"expand": "Þenja út",
"expand.all": "Expand all",
"expand.all": "Sýna allt",
"field.invalid": "Þetta svið er bara ógillt sem stendur.",
"field.required": "Þetta svið er nauðsynlegt",
@ -384,7 +387,7 @@
"field.structure.delete.confirm": "Viltu virkilega eyða þessari röð?",
"field.structure.delete.confirm.all": "Ætlar þú virkilega að eyða öllum færslum?",
"field.structure.delete.confirm.selected": "Do you really want to delete the selected entries?",
"field.structure.delete.confirm.selected": "Viltu virkilega eyða völdum færslum?",
"field.structure.empty": "Engar færslur enn",
"field.users.empty": "Engir notendur valdir enn",
@ -483,7 +486,7 @@
"license.code": "Kóðasnið",
"license.code.help": "Þú fékkst leyfiskóðan sendan í tölvupósti eftir að þú borgaðir fyrir leyfið. Vinsamlegast afritaðu hann hingað.",
"license.code.label": "Vinsamlegast settu inn leyfiskóðan",
"license.remove.text": "<p>Removing the license will irreversibly delete the license file from this site. You can then activate this site with a different license key or re-register the same license key if the domain remains the same.</p><p>To change the domain associated with the license, please contact the Kirby team. <a href='https://getkirby.com/license'>Read more &rarr;</a></p>",
"license.remove.text": "<p>Að fjarlægja leyfið mun eyða leyfisskránni varanlega af þessu vefsvæði. Þú getur síðan virkjað vefsvæðið með öðrum leyfislykli eða skráð sama leyfislykil aftur, svo framarlega sem lénið helst óbreytt. </p><p> Til að breyta léninu sem leyfið er tengt við, vinsamlegast hafðu samband við Kirby-teymið.<a href='https://getkirby.com/license'>Lesa nánar &rarr;</a></p>",
"license.status.active.info": "Felur í sér allar útgáfur þar til {date}",
"license.status.active.label": "Gilt skráningarleyfi",
"license.status.demo.info": "Þessi uppsetning er til prófunar.",
@ -635,7 +638,9 @@
"plugin": "Viðbót",
"plugins": "Viðbætur",
"prev": "Fyrri",
"preview": "Forskoða",
"preview.browser.scroll": "Skruna í takt",
"publish": "Útgefa",
"published": "Útgefnar og listaðar",
@ -671,7 +676,7 @@
"security": "Öryggi",
"select": "Velja",
"select.all": "Select all",
"select.all": "Velja allt",
"server": "Vefþjónn",
"settings": "Stillingar",
"show": "Sýna",
@ -780,12 +785,13 @@
"user.changePassword.current": "Þitt núverandi lykilorð",
"user.changePassword.new": "Nýtt lykilorð",
"user.changePassword.new.confirm": "Staðfestu nýtt lykilorð…",
"user.changePassword.own": "Your own password",
"user.changePassword.own": "Þitt eigið lykilorð",
"user.changeRole": "Breyta hlutverki",
"user.changeRole.select": "Veldu nýtt hlutverk",
"user.create": "Bæta við nýjum notenda",
"user.delete": "Farga þessum notenda",
"user.delete.confirm": "Viltu virkilega eyða <br><strong>{email}</strong>?",
"user.setPassword": "Setja lykilorð",
"users": "Notendur",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "La pagina non \u00e8 stata trovata",
"error.page.update.permission": "Non ti è permesso modificare \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Non puoi aggiungere più di {max} file alla sezione \"{section}\"",
"error.section.files.max.singular": "Non puoi aggiungere più di un file alla sezione \"{section}\"",
"error.section.files.min.plural": "La sezione \"{section}\" richiede almeno {min} file",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Il titolo non può essere vuoto",
"error.site.changeTitle.permission": "Non ti è permesso modificare il titolo del sito",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Non ti è permesso modificare i contenuti globali del sito",
"error.structure.validation": "C'è un errore nel campo \"{field}\" nella riga {index}",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Precedente",
"preview": "Anteprima",
"preview.browser.scroll": "Sync scroll",
"publish": "Pubblica",
"published": "Pubblicato",
@ -786,6 +791,7 @@
"user.create": "Aggiungi nuovo utente",
"user.delete": "Elimina questo utente",
"user.delete.confirm": "Sei sicuro di voler eliminare l'utente <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Utenti",

View file

@ -1,5 +1,5 @@
{
"account": "Account",
"account": "계정",
"account.changeName": "이름 변경",
"account.delete": "계정 삭제",
"account.delete.confirm": "계정을 삭제할까요? 계정을 삭제한 뒤에는 즉시 로그아웃되며, 삭제된 계정은 복구할 수 없습니다.",
@ -16,7 +16,7 @@
"changes": "변경",
"confirm": "확인",
"collapse": "접기",
"collapse.all": "Collapse all",
"collapse.all": "모두 접기",
"color": "색",
"coordinates": "좌표",
"copy": "복사",
@ -44,8 +44,8 @@
"delete": "\uc0ad\uc81c",
"delete.all": "모두 삭제",
"deselect": "Deselect",
"deselect.all": "Deselect all",
"deselect": "선택 해제",
"deselect.all": "모두 선택 해제",
"dialog.fields.empty": "필드가 없습니다.",
"dialog.files.empty": "선택할 파일이 없습니다.",
@ -204,6 +204,8 @@
"error.page.undefined": "\ud398\uc774\uc9c0\uac00 \uc5c6\uc2b5\ub2c8\ub2e4.",
"error.page.update.permission": "페이지({slug})를 변경할 권한이 없습니다.",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "이 섹션({section})에는 파일을 {max}개 이상 추가할 수 없습니다.",
"error.section.files.max.singular": "이 섹션({section})에는 파일을 하나 이상 추가할 수 없습니다.",
"error.section.files.min.plural": "이 섹션({section})에는 파일이 {min}개 이상 필요합니다.",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "제목을 입력하세요.",
"error.site.changeTitle.permission": "사이트명을 변경할 권한이 없습니다.",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "사이트 정보를 변경할 권한이 없습니다.",
"error.structure.validation": "{index}번째 필드({field})에 오류가 있습니다.",
@ -298,12 +301,12 @@
"error.validation.uuid": "올바른 UUID를 입력하세요.",
"error.validation.url": "올바른 URL을 입력하세요.",
"error.version.discard.permission": "You are not allowed to discard this version",
"error.version.publish.permission": "You are not allowed to publish this version",
"error.version.save.permission": "You are not allowed to change this version",
"error.version.discard.permission": "이 버전을 무시할 권한이 없습니다.",
"error.version.publish.permission": "이 버전을 공개할 권한이 없습니다.",
"error.version.save.permission": "이 버전을 변경할 권한이 없습니다.",
"expand": "열기",
"expand.all": "Expand all",
"expand.all": "모두 열기",
"field.invalid": "필드가 올바르지 않습니다.",
"field.required": "필드를 채우세요.",
@ -384,7 +387,7 @@
"field.structure.delete.confirm": "이 항목을 삭제할까요?",
"field.structure.delete.confirm.all": "모든 항목을 삭제할까요?",
"field.structure.delete.confirm.selected": "Do you really want to delete the selected entries?",
"field.structure.delete.confirm.selected": "선택한 항목을 삭제할까요?",
"field.structure.empty": "항목이 없습니다.",
"field.users.empty": "선택한 사용자가 없습니다.",
@ -408,8 +411,8 @@
"filter": "필터",
"form.discard": "저장되지 않은 항목이 있습니다.",
"form.discard.confirm": "<strong>저장되지 않은 내용</strong>을 삭제할까요?",
"form.discard": "변경 사항 무시",
"form.discard.confirm": "<strong>저장되지 않은 내용</strong>을 무시할까요?",
"form.locked": "다른 사용자가 편집 중입니다.",
"form.unsaved": "변경 사항이 저장되지 않았습니다.",
"form.preview": "변경 사항 미리 보기",
@ -483,7 +486,7 @@
"license.code": "언어 코드",
"license.code.help": "이메일로 전송된 라이선스 코드를 복사해 붙여넣으세요.",
"license.code.label": "라이선스 코드를 입력하세요.",
"license.remove.text": "<p>Removing the license will irreversibly delete the license file from this site. You can then activate this site with a different license key or re-register the same license key if the domain remains the same.</p><p>To change the domain associated with the license, please contact the Kirby team. <a href='https://getkirby.com/license'>Read more &rarr;</a></p>",
"license.remove.text": "<p>라이선스를 제거하면 이 사이트에서 라이선스 파일이 삭제됩니다. 도메인이 동일한 경우 다른 라이선스 키로 이 사이트를 활성화하거나 동일한 라이선스 키를 다시 등록할 수 있습니다.</p><p>라이선스와 연결된 도메인을 변경하려면 Kirby 팀에 문의하십시오.<a href='https://getkirby.com/license'>자세히 보기 &rarr;</a></p>",
"license.status.active.info": "새로운 메이저 버전은 {date}까지 포함됩니다.",
"license.status.active.label": "유효한 라이선스",
"license.status.demo.info": "데모를 설치합니다.",
@ -635,7 +638,9 @@
"plugin": "플러그인",
"plugins": "플러그인",
"prev": "이전",
"preview": "미리 보기",
"preview.browser.scroll": "스크롤 동기화",
"publish": "발행",
"published": "발행",
@ -671,7 +676,7 @@
"security": "보안",
"select": "선택",
"select.all": "Select all",
"select.all": "모두 선택",
"server": "서버",
"settings": "설정",
"show": "보기",
@ -780,12 +785,13 @@
"user.changePassword.current": "현재 암호",
"user.changePassword.new": "새 암호",
"user.changePassword.new.confirm": "새 암호 확인",
"user.changePassword.own": "Your own password",
"user.changePassword.own": "현재 암호",
"user.changeRole": "역할 변경",
"user.changeRole.select": "새 역할 선택",
"user.create": "사용자 추가",
"user.delete": "사용자 삭제",
"user.delete.confirm": "사용자(<strong>{email}</strong>)를 삭제할까요?",
"user.setPassword": "암호 설정",
"users": "사용자",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "Puslapis nerastas",
"error.page.update.permission": "Neturite leidimo atnaujinti \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Į sekciją \"{section}\" negalima pridėti daugiau nei {max} failų",
"error.section.files.max.singular": "Į sekciją \"{section}\" negalima pridėti daugiau nei vieną failą",
"error.section.files.min.plural": "Sekcija \"{section}\" reikalauja bent {min} failų",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Pavadinimas negali būti tuščias",
"error.site.changeTitle.permission": "Neturite leidimo keisti svetainės pavadinimo",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Neturite leidimo atnaujinti svetainės",
"error.structure.validation": "There's an error on the \"{field}\" field in row {index}",
@ -635,7 +638,9 @@
"plugin": "Įskiepas",
"plugins": "Įskiepai",
"prev": "Ankstesnis",
"preview": "Peržiūra",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Paskelbti",
@ -786,6 +791,7 @@
"user.create": "Pridėti naują vartotoją",
"user.delete": "Pašalinti šį vartotoją",
"user.delete.confirm": "Ar tikrai norite pašalinti vartotoją <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Vartotojai",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "Siden kunne ikke bli funnet",
"error.page.update.permission": "Du har ikke tillatelse til å oppdatere \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Det er ikke mulig å legge til mer enn {max} filer i seksjonen \"{section}\"",
"error.section.files.max.singular": "Det er ikke mulig å legge til mer enn én fil i seksjonen \"{section}\"",
"error.section.files.min.plural": "Seksjonen \"{section}\" krever minst {min} filer",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Tittelen kan ikke være tom",
"error.site.changeTitle.permission": "Du har ikke tillatelse til å endre tittel på siden",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Du har ikke tillatelse til å oppdatere denne siden",
"error.structure.validation": "Det er en feilmelding på felt \"{field}\" i rad {index}",
@ -635,7 +638,9 @@
"plugin": "Utvidelse",
"plugins": "Plugins",
"prev": "Forrige",
"preview": "Forhåndsvisning",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Publisert",
@ -786,6 +791,7 @@
"user.create": "Legg til ny bruker",
"user.delete": "Slett denne brukeren",
"user.delete.confirm": "Vil du virkelig slette denne konten?",
"user.setPassword": "Set password",
"users": "Brukere",

View file

@ -16,7 +16,7 @@
"changes": "Wijzigingen",
"confirm": "Oke",
"collapse": "Sluit",
"collapse.all": "Collapse all",
"collapse.all": "Sluit alles",
"color": "Kleur",
"coordinates": "Coördinaten ",
"copy": "Kopiëren",
@ -44,8 +44,8 @@
"delete": "Verwijderen",
"delete.all": "Verwijder alles",
"deselect": "Deselect",
"deselect.all": "Deselect all",
"deselect": "Deselecteren",
"deselect.all": "Deselecteer alles",
"dialog.fields.empty": "Dit venster heeft geen velden",
"dialog.files.empty": "Geen bestanden om te selecteren",
@ -204,6 +204,8 @@
"error.page.undefined": "De pagina kan niet worden gevonden",
"error.page.update.permission": "Je hebt geen rechten om \"{slug}\" te updaten",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Voeg niet meer dan {max} bestanden toe aan de zone \"{section}\"",
"error.section.files.max.singular": "Je kunt niet meer dan 1 bestand toevoegen aan de zone \"{section}\"",
"error.section.files.min.plural": "De \"{section}\" sectie moet minimaal {min} bestanden bevatten.",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "De titel mag niet leeg zijn",
"error.site.changeTitle.permission": "Je hebt geen rechten om de titel van de site te wijzigen",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Je hebt geen rechten om de site te updaten",
"error.structure.validation": "Er is een fout opgetreden in veld \"{field}\" in rij {index}",
@ -298,12 +301,12 @@
"error.validation.uuid": "Vul een geldige UUID in",
"error.validation.url": "Vul een geldige URL in",
"error.version.discard.permission": "You are not allowed to discard this version",
"error.version.publish.permission": "You are not allowed to publish this version",
"error.version.save.permission": "You are not allowed to change this version",
"error.version.discard.permission": "Je hebt geen rechten om deze versie te annuleren",
"error.version.publish.permission": "Je hebt geen rechten om deze versie te publiceren",
"error.version.save.permission": "Je hebt geen rechten om deze versie te wijzigen",
"expand": "Open",
"expand.all": "Expand all",
"expand.all": "Open alles",
"field.invalid": "Dit veld is niet geldig",
"field.required": "Dit veld is verplicht",
@ -384,7 +387,7 @@
"field.structure.delete.confirm": "Wil je deze rij verwijderen?",
"field.structure.delete.confirm.all": "Weet je zeker dat je alle items wil verwijderen?",
"field.structure.delete.confirm.selected": "Do you really want to delete the selected entries?",
"field.structure.delete.confirm.selected": "Wil je de geselecteerde items echt verwijderen?",
"field.structure.empty": "Nog geen items",
"field.users.empty": "Nog geen gebruikers geselecteerd",
@ -483,7 +486,7 @@
"license.code": "Code",
"license.code.help": "Je hebt de licentiecode via e-mail gekregen nadat je de aankoop hebt gedaan. Kopieer en plak de licentiecode hier.",
"license.code.label": "Vul je licentie in",
"license.remove.text": "<p>Removing the license will irreversibly delete the license file from this site. You can then activate this site with a different license key or re-register the same license key if the domain remains the same.</p><p>To change the domain associated with the license, please contact the Kirby team. <a href='https://getkirby.com/license'>Read more &rarr;</a></p>",
"license.remove.text": "<p>Als je de licentie verwijdert, wordt het licentiebestand onherroepelijk van deze site verwijderd. Je kan deze site vervolgens activeren met een andere licentiesleutel of dezelfde licentiesleutel opnieuw registreren als het domein hetzelfde blijft.</p> <p>Neem contact op met het Kirby-team om het domein dat aan de licentie is gekoppeld te wijzigen. <a href='https://getkirby.com/license'>Lees meer &rarr;</a> </p>",
"license.status.active.info": "Inclusief nieuwe major versies tot {date}",
"license.status.active.label": "Geldige licentie",
"license.status.demo.info": "Dit is een demo installatie",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Vorige",
"preview": "Voorbeeld",
"preview.browser.scroll": "Synchroniseer scroll",
"publish": "Publiceren",
"published": "Gepubliceerd",
@ -671,7 +676,7 @@
"security": "Beveiliging",
"select": "Selecteren",
"select.all": "Select all",
"select.all": "Selecteer alles",
"server": "Server",
"settings": "Opties",
"show": "Toon",
@ -780,12 +785,13 @@
"user.changePassword.current": "Je huidige wachtwoord",
"user.changePassword.new": "Nieuw wachtwoord",
"user.changePassword.new.confirm": "Bevestig het nieuwe wachtwoord...",
"user.changePassword.own": "Your own password",
"user.changePassword.own": "Je eigen wachtwoord",
"user.changeRole": "Verander rol",
"user.changeRole.select": "Kies een nieuwe rol",
"user.create": "Voeg een nieuwe gebruiker toe",
"user.delete": "Verwijder deze gebruiker",
"user.delete.confirm": "Weet je zeker dat je <br><strong>{email}</strong> wil verwijderen?",
"user.setPassword": "Stel wachtwoord in",
"users": "Gebruikers",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "Nie udało się znaleźć strony",
"error.page.update.permission": "Nie masz uprawnień, by zaktualizować \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Do sekcji \"{section}\" można dodać nie więcej niż {max} plików",
"error.section.files.max.singular": "Do sekcji \"{section}\" można dodać tylko jeden plik",
"error.section.files.min.plural": "W sekcji \"{section}\" musi być co najmniej {min} pliki/-ów",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Tytuł nie może być pusty",
"error.site.changeTitle.permission": "Nie masz uprawnień, by zmienić tytuł strony",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Nie masz uprawnień, by zaktualizować stronę",
"error.structure.validation": "Wystąpił błąd w polu \"{field}\" w wierszu {index}",
@ -635,7 +638,9 @@
"plugin": "Wtyczka",
"plugins": "Wtyczki",
"prev": "Poprzednie",
"preview": "Podgląd",
"preview.browser.scroll": "Sync scroll",
"publish": "Opublikuj",
"published": "Opublikowane",
@ -786,6 +791,7 @@
"user.create": "Dodaj nowego użytkownika",
"user.delete": "Usuń tego użytkownika",
"user.delete.confirm": "Czy na pewno chcesz usunąć <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Użytkownicy",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "P\u00e1gina n\u00e3o encontrada",
"error.page.update.permission": "Você não tem permissão para atualizar \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Você não pode adicionar mais do que {max} arquivos à seção \"{section}\"",
"error.section.files.max.singular": "Você não pode adicionar mais do que um arquivo à seção \"{section}\"",
"error.section.files.min.plural": "A seção “{section}” precisa ter pelo menos {min} arquivos",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "O título não pode ficar em branco",
"error.site.changeTitle.permission": "Você não tem permissão para alterar o título do site",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Você não tem permissão para atualizar o site",
"error.structure.validation": "Existe um erro no campo \"{field}\" na linha {index}",
@ -298,9 +301,9 @@
"error.validation.uuid": "Por favor, insira um UUID válido",
"error.validation.url": "Digite uma URL válida",
"error.version.discard.permission": "You are not allowed to discard this version",
"error.version.publish.permission": "You are not allowed to publish this version",
"error.version.save.permission": "You are not allowed to change this version",
"error.version.discard.permission": "Não tem permissão para reverter esta versão",
"error.version.publish.permission": "Não tem permissão para publicar esta versão",
"error.version.save.permission": "Não tem permissão para alterar esta versão",
"expand": "Expandir",
"expand.all": "Expandir todos",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Anterior",
"preview": "Visualizar",
"preview.browser.scroll": "Sincronizar scroll",
"publish": "Publicar",
"published": "Publicadas",
@ -786,6 +791,7 @@
"user.create": "Adicionar novo usuário",
"user.delete": "Deletar este usuário",
"user.delete.confirm": "Deseja realmente deletar <br><strong>{email}</strong>?",
"user.setPassword": "Definir palavra-passe",
"users": "Usuários",

View file

@ -56,7 +56,7 @@
"dimensions": "Dimensões",
"disable": "Desativar",
"disabled": "Desativado",
"discard": "Descartar",
"discard": "Reverter",
"drawer.fields.empty": "Esta janela não tem campos",
@ -204,6 +204,8 @@
"error.page.undefined": "Não foi possível encontrar a página",
"error.page.update.permission": "Não tem permissões para atualizar \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Não pode adicionar mais do que {max} ficheiros à secção \"{section}\"",
"error.section.files.max.singular": "Não pode adicionar mais do que um ficheiro à secção \"{section}\"",
"error.section.files.min.plural": "A secção \"{section}\" requer no mínimo {min} ficheiros",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "O título não pode ficar em branco",
"error.site.changeTitle.permission": "Não tem permissões para alterar o título do site",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Não tem permissões para atualizar o site",
"error.structure.validation": "Existe um erro no campo \"{field}\" na linha {index}",
@ -298,9 +301,9 @@
"error.validation.uuid": "Por favor, insira um UUID válido",
"error.validation.url": "Por favor, insira um URL válido",
"error.version.discard.permission": "You are not allowed to discard this version",
"error.version.publish.permission": "You are not allowed to publish this version",
"error.version.save.permission": "You are not allowed to change this version",
"error.version.discard.permission": "Não tem permissão para reverter esta versão",
"error.version.publish.permission": "Não tem permissão para publicar esta versão",
"error.version.save.permission": "Não tem permissão para alterar esta versão",
"expand": "Expandir",
"expand.all": "Expandir todos",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Anterior",
"preview": "Pré-visualizar",
"preview.browser.scroll": "Sincronizar scroll",
"publish": "Publicar",
"published": "Publicadas",
@ -786,6 +791,7 @@
"user.create": "Adicionar um novo utilizador",
"user.delete": "Eliminar este utilizador",
"user.delete.confirm": "Tem a certeza que pretende eliminar <br><strong>{email}</strong>?",
"user.setPassword": "Definir palavra-passe",
"users": "Utilizadores",

View file

@ -1,5 +1,5 @@
{
"account": "Account",
"account": "Cont",
"account.changeName": "Schimbă-ți numele",
"account.delete": "Șterge-ți contul",
"account.delete.confirm": "Chiar vrei să îți ștergi contul? Vei fi deconectat imediat. Contul nu poate fi recuperat.",
@ -16,7 +16,7 @@
"changes": "Schimbări",
"confirm": "Ok",
"collapse": "Pliază",
"collapse.all": "Collapse all",
"collapse.all": "Închide toate",
"color": "Culoare",
"coordinates": "Coordonate",
"copy": "Copiază",
@ -44,8 +44,8 @@
"delete": "\u0218terge",
"delete.all": "Șterge toate",
"deselect": "Deselect",
"deselect.all": "Deselect all",
"deselect": "Deselectează",
"deselect.all": "Deselectează toate",
"dialog.fields.empty": "Acest dialog nu are niciun câmp",
"dialog.files.empty": "Nu există fișiere de selectat",
@ -204,6 +204,8 @@
"error.page.undefined": "Pagina nu a fost găsită",
"error.page.update.permission": "Nu ai voie să actualizezi \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Nu poți avea mai mult de {max} fișiere în secțiunea \"{section}\"",
"error.section.files.max.singular": "Nu poți avea mai mult de un fișier în secțiunea \"{section}\"",
"error.section.files.min.plural": "Secțiunea \"{section}\" are nevoie de cel puțin {min} fișiere",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Titlul nu poate să rămână gol",
"error.site.changeTitle.permission": "Nu ai voie să schimbi titlul site-ului",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Nu ai voie să actualizezi site-ul",
"error.structure.validation": "Există o eroare la câmpul \"{field}\" pe rândul {index}",
@ -298,12 +301,12 @@
"error.validation.uuid": "Te rog introdu un UUID valid",
"error.validation.url": "Te rog introdu un URL valid",
"error.version.discard.permission": "You are not allowed to discard this version",
"error.version.publish.permission": "You are not allowed to publish this version",
"error.version.save.permission": "You are not allowed to change this version",
"error.version.discard.permission": "Nu ai voie să înlături această versiune",
"error.version.publish.permission": "Nu ai voie să publici această versiune",
"error.version.save.permission": "Nu ai voie să modifici această versiune",
"expand": "Extinde",
"expand.all": "Expand all",
"expand.all": "Deschide toate",
"field.invalid": "Câmpul este nevalid",
"field.required": "Acest câmp este necesar",
@ -384,7 +387,7 @@
"field.structure.delete.confirm": "Chiar vrei să ștergi acest rând?",
"field.structure.delete.confirm.all": "Chiar vrei să ștergi toate înregistrările?",
"field.structure.delete.confirm.selected": "Do you really want to delete the selected entries?",
"field.structure.delete.confirm.selected": "Chiar vrei să ștergi toate intrările alese?",
"field.structure.empty": "Nicio înregistrare deocamdată",
"field.users.empty": "Niciun utilizator ales deocamdată",
@ -483,7 +486,7 @@
"license.code": "Cod",
"license.code.help": "Ați primit codul de licență după achiziție prin e-mail. Vă rugăm să-l copiați și să-l inserezi aici.",
"license.code.label": "Te rog introdu codul tău de licență",
"license.remove.text": "<p>Removing the license will irreversibly delete the license file from this site. You can then activate this site with a different license key or re-register the same license key if the domain remains the same.</p><p>To change the domain associated with the license, please contact the Kirby team. <a href='https://getkirby.com/license'>Read more &rarr;</a></p>",
"license.remove.text": "<p>Prin înlăturarea licenței se va șterge în mod ireversibil fișierul de licență de pe acest site. Vei putea apoi activa site-ul cu o altă cheie de licență sau să folosești din nou aceeași cheie în caz că domeniul rămâne același.</p><p>Pentru a schimba domeniul asociat licenței, contactează te rog echipa Kirby. <a href='https://getkirby.com/license'>Citește mai multe &rarr;</a></p>",
"license.status.active.info": "Include noi versiuni majore până la data de {date}",
"license.status.active.label": "Licență validă",
"license.status.demo.info": "Aceasta este o instalare demo",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugin-uri",
"prev": "Precedenta",
"preview": "Previzualizează",
"preview.browser.scroll": "Sync scroll",
"publish": "Publică",
"published": "Publicate",
@ -671,7 +676,7 @@
"security": "Securitate",
"select": "Alege",
"select.all": "Select all",
"select.all": "Alege toate",
"server": "Server",
"settings": "Reglaje",
"show": "Arată",
@ -780,12 +785,13 @@
"user.changePassword.current": "Parola ta curentă",
"user.changePassword.new": "Parola nouă",
"user.changePassword.new.confirm": "Confirmă parola nouă...",
"user.changePassword.own": "Your own password",
"user.changePassword.own": "Parola proprie",
"user.changeRole": "Schimbă rolul",
"user.changeRole.select": "Alege un rol nou",
"user.create": "Adaugă un nou utilizator",
"user.delete": "Șterge acest utilizator",
"user.delete.confirm": "Chiar vrei să ștergi <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Utilizatori",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430",
"error.page.update.permission": "У вас нет права обновить \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Нельзя добавить больше чем {max} файлов в секции \"{section}\"",
"error.section.files.max.singular": "Можно добавить не больше 1 файла в секции \"{section}\"",
"error.section.files.min.plural": "Секция \"{section}\" требует хотя бы {min} файлов",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Название не может быть пустым",
"error.site.changeTitle.permission": "У вас нет права изменять название сайта",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "У вас нет права обновить сайт",
"error.structure.validation": "Ошибка в поле \"{field}\" в строке {index}",
@ -635,7 +638,9 @@
"plugin": "Расширение",
"plugins": "Плагины",
"prev": "Предыдущий",
"preview": "Предпросмотр",
"preview.browser.scroll": "Sync scroll",
"publish": "Опубликовать",
"published": "Опубликовано",
@ -786,6 +791,7 @@
"user.create": "Добавить нового пользователя",
"user.delete": "Удалить этого пользователя",
"user.delete.confirm": "Вы действительно хотите аккаунт <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Пользователи",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "Stránku nie je možné nájsť",
"error.page.update.permission": "Nemáte povolenie na aktualizáciu \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Nemôžete pridať viac ako {max} súbory/ov do sekcie \"{section}\"",
"error.section.files.max.singular": "Nemôžete pridať viac ako 1 súbor do sekcie \"{section}\"",
"error.section.files.min.plural": "The \"{section}\" section requires at least {min} files",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Titulok nemôže byť prázdny",
"error.site.changeTitle.permission": "Nemáte povolenie na zmenu titulku pre portál",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Nemáte povolenie na aktualizovanie portálu",
"error.structure.validation": "There's an error on the \"{field}\" field in row {index}",
@ -635,7 +638,9 @@
"plugin": "Plugin",
"plugins": "Plugins",
"prev": "Predchádzajúci",
"preview": "Preview",
"preview.browser.scroll": "Sync scroll",
"publish": "Publish",
"published": "Zverejnené",
@ -786,6 +791,7 @@
"user.create": "Pridať nového užívateľa",
"user.delete": "Zmazať tohto užívateľa",
"user.delete.confirm": "Ste si istý, že chcete zmazať <br><strong>{email}</strong>?",
"user.setPassword": "Set password",
"users": "Užívatelia",

View file

@ -1,5 +1,5 @@
{
"account": "Account",
"account": "Nalog",
"account.changeName": "Promenite vaše ime",
"account.delete": "Izbrišite vaš nalog",
"account.delete.confirm": "Da li zaista želite da izbrišete vaš nalog? Bićete odjavljeni odmah, i vaš nalog ne može biti povraćen.",
@ -16,14 +16,14 @@
"changes": "Promene",
"confirm": "Ok",
"collapse": "Skupi",
"collapse.all": "Collapse all",
"collapse.all": "Skupi sve",
"color": "Boja",
"coordinates": "Koordinate",
"copy": "Kopiraj",
"copy.all": "Kopiraj sve",
"copy.success": "Copied",
"copy.success": "Kopirano",
"copy.success.multiple": "{count} kopirano!",
"copy.url": "Copy URL",
"copy.url": "Kopirajte URL",
"create": "Kreiraj",
"custom": "Običaj",
@ -44,8 +44,8 @@
"delete": "Obriši",
"delete.all": "Obriši sve",
"deselect": "Deselect",
"deselect.all": "Deselect all",
"deselect": "Poništi izbor",
"deselect.all": "Poništi sve izbore",
"dialog.fields.empty": "Ovaj dijalog nema polja",
"dialog.files.empty": "Nema fajlova koji se mogu izabrati",
@ -96,23 +96,23 @@
"error.cache.type.invalid": "Neispravan tip keša \"{type}\"",
"error.content.lock.delete": "The version is locked and cannot be deleted",
"error.content.lock.move": "The source version is locked and cannot be moved",
"error.content.lock.publish": "This version is already published",
"error.content.lock.replace": "The version is locked and cannot be replaced",
"error.content.lock.update": "The version is locked and cannot be updated",
"error.content.lock.delete": "Ova verzija je zaključana i ne može biti obrisana",
"error.content.lock.move": "Izvorna verzija je zaključana i ne može da se premesti",
"error.content.lock.publish": "Ova verzija je već objavljena",
"error.content.lock.replace": "Ova verzija je zaključana i ne može biti zamenjena",
"error.content.lock.update": "Ova verzija je zaključana i ne može biti ažurirana",
"error.entries.max.plural": "You must not add more than {max} entries",
"error.entries.max.singular": "You must not add more than one entry",
"error.entries.min.plural": "You must add at least {min} entries",
"error.entries.min.singular": "You must add at least one entry",
"error.entries.supports": "\"{type}\" field type is not supported for the entries field",
"error.entries.max.plural": "Ne smete dodati više od {max} unosa",
"error.entries.max.singular": "Ne smete dodati više od jednog unosa",
"error.entries.min.plural": "Morate dodati najmanje {min} unosa",
"error.entries.min.singular": "Morate dodati barem jedan unos",
"error.entries.supports": "\"{type}\" tip polja nije podržano za polje unosa",
"error.entries.validation": "Postoji greška u redu \"{field}\" na ovom polju {index}",
"error.email.preset.notFound": "Email preset \"{name}\" nije pronađen",
"error.field.converter.invalid": "Neispravan converter \"{converter}\"",
"error.field.link.options": "Invalid options: {options}",
"error.field.link.options": "Nevažeće opcije: {options}",
"error.field.type.missing": "Polje \"{ name }\": Tip polja \"{ type }\" ne postoji",
"error.file.changeName.empty": "Naziv ne sme biti prazan",
@ -120,7 +120,7 @@
"error.file.changeTemplate.invalid": "Šablon za datoteku \"{id}\" se ne može promeniti u \"{template}\" (valid: \"{blueprints}\")",
"error.file.changeTemplate.permission": "Nije vam dozvoljeno da menjate šablon za datoteku \"{id}\"",
"error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.",
"error.file.delete.multiple": "Nije bilo moguće obrisati sve fajlove. Probajte brisati svaki fajl individualno da vidite specifičnu grešku koja onemogućava brisanje.",
"error.file.duplicate": "Fajl sa nazivom \"{filename}\" već postoji",
"error.file.extension.forbidden": "Extension \"{extension}\" nije dozvoljena",
"error.file.extension.invalid": "Nevažeći dodatak: {extension}",
@ -139,7 +139,7 @@
"error.file.name.missing": "Ime fajla ne može biti prazno",
"error.file.notFound": "Fajl \"{filename}\" nije mogao biti pronadjen",
"error.file.orientation": "Orijentacija slike mora biti \"{orientation}\"",
"error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"",
"error.file.sort.permission": "Niste ovlašćeni da promenite sortiranje \"{filename}\"",
"error.file.type.forbidden": "Niste ovlašćeni da otpremate {type} fajlove",
"error.file.type.invalid": "Nevažeći tip datoteke: {type}",
"error.file.undefined": "Fajl nije mogao biti pronadjen",
@ -148,12 +148,12 @@
"error.form.notSaved": "Formular nije mogao biti sačuvan",
"error.language.code": "Molimo ukucajte validan kod za jezik",
"error.language.create.permission": "You are not allowed to create a language",
"error.language.delete.permission": "You are not allowed to delete the language",
"error.language.create.permission": "Niste ovlašćeni da kreirate jezik",
"error.language.delete.permission": "Niste ovlašćeni da izbrišete jezik",
"error.language.duplicate": "Jezik već postoji",
"error.language.name": "Molimo upišite validno ime za jezik",
"error.language.notFound": "Jezik nije mogao biti pronađen",
"error.language.update.permission": "You are not allowed to update the language",
"error.language.update.permission": "Niste ovlašćeni da ažurirate jezik",
"error.layout.validation.block": "Postoji greška u \"{field}\" polju u bloku {blockIndex} koji koristi \"{fieldset}\" tip bloka u rasporedu {layoutIndex}",
"error.layout.validation.settings": "Došlo je do greške u {index} podešavanjima ",
@ -183,7 +183,7 @@
"error.page.delete": "Stranica \"{slug}\" ne može biti obrisana",
"error.page.delete.confirm": "Molimo ukucajte naslov stranice da potvrdite",
"error.page.delete.hasChildren": "Stranica ima podstranice i ne može biti obrisana",
"error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.",
"error.page.delete.multiple": "Nije bilo moguće obrisati sve stranice. Probajte brisati svaku stranicu individualno da vidite specifičnu grešku koja onemogućava brisanje.",
"error.page.delete.permission": "Nemate ovlašćenja da obrišete \"{slug}\"",
"error.page.draft.duplicate": "Draft stranice sa URL appendix \"{slug}\" već postoji",
"error.page.duplicate": "Stranica sa URL appendix-om \"{slug}\" već postoji",
@ -191,7 +191,7 @@
"error.page.move.ancestor": "Stranica se ne može premestiti u sebe",
"error.page.move.directory": "Direktorijum stranice ne može da se premesti",
"error.page.move.duplicate": "Podstranica sa dodatkom URL \"{slug}\" već postoji",
"error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint",
"error.page.move.noSections": "Stranica \"{parent}\" ne može biti roditelj nijedne stranice zato što joj nedostaje sekcija sa stranicama u svom blueprint-u",
"error.page.move.notFound": "Premeštena stranica nije pronađena",
"error.page.move.permission": "Nije vam dozvoljeno da se krećete \"{slug}\"",
"error.page.move.template": "Šablon \"{template}\" nije prihvaćen kao podstranica \"{parent}\"",
@ -204,6 +204,8 @@
"error.page.undefined": "Stranica ne može biti pronađena",
"error.page.update.permission": "Nije Vam dozvoljeno da ažurirate \"{slug}\"",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "Ne možete dodati više od {max} fajlova u \"{section}\" sekciju",
"error.section.files.max.singular": "Ne možete dodati više od jednog fajla u \"{section}\" sekciju",
"error.section.files.min.plural": "\"{section}\" sekcija zahteva najmanje {min} fajlova",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Naslov ne može biti prazan",
"error.site.changeTitle.permission": "Nije Vam dozvoljeno da promenite naziv sajta",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "Nije Vam dozvoljeno da ažurirate sajt",
"error.structure.validation": "Postoji greška u redu \"{field}\" na ovom polju {index}",
@ -298,12 +301,12 @@
"error.validation.uuid": "Molimo vas unesite važeći UUID",
"error.validation.url": "Molimo vas da unesete važeći URL",
"error.version.discard.permission": "You are not allowed to discard this version",
"error.version.publish.permission": "You are not allowed to publish this version",
"error.version.save.permission": "You are not allowed to change this version",
"error.version.discard.permission": "Nije vam dozvoljeno da odbacite ovu verziju",
"error.version.publish.permission": "Nije vam dozvoljeno da objavite ovu verziju",
"error.version.save.permission": "Nije vam dozvoljeno da menjate ovu verziju",
"expand": "Proširite",
"expand.all": "Expand all",
"expand.all": "Proširi sve",
"field.invalid": "Polje je nevažeće",
"field.required": "Polje je obavezno",
@ -325,9 +328,9 @@
"field.blocks.heading.name": "Naslov",
"field.blocks.heading.text": "Tekst",
"field.blocks.heading.placeholder": "Naslov ...",
"field.blocks.figure.back.plain": "Plain",
"field.blocks.figure.back.pattern.light": "Pattern (light)",
"field.blocks.figure.back.pattern.dark": "Pattern (dark)",
"field.blocks.figure.back.plain": "Obično",
"field.blocks.figure.back.pattern.light": "Šablon (svetli)",
"field.blocks.figure.back.pattern.dark": "Šablon (tamni)",
"field.blocks.image.alt": "Alternativni tekst",
"field.blocks.image.caption": "Natpis",
"field.blocks.image.crop": "Isecite",
@ -351,16 +354,16 @@
"field.blocks.quote.citation.placeholder": "od …",
"field.blocks.text.name": "Tekst",
"field.blocks.text.placeholder": "Tekst ...",
"field.blocks.video.autoplay": "Autoplay",
"field.blocks.video.autoplay": "Automatska reprodukcija",
"field.blocks.video.caption": "Natpis",
"field.blocks.video.controls": "Controls",
"field.blocks.video.controls": "Kontrole",
"field.blocks.video.location": "Lokacija",
"field.blocks.video.loop": "Loop",
"field.blocks.video.muted": "Muted",
"field.blocks.video.loop": "Petlja",
"field.blocks.video.muted": "Prigušeno",
"field.blocks.video.name": "Video",
"field.blocks.video.placeholder": "Unesite URL video snimka",
"field.blocks.video.poster": "Poster",
"field.blocks.video.preload": "Preload",
"field.blocks.video.preload": "Prethodno učitavanje",
"field.blocks.video.url.label": "Video-URL",
"field.blocks.video.url.placeholder": "https://youtube.com/?v=",
@ -368,7 +371,7 @@
"field.entries.empty": "Još nema unosa",
"field.files.empty": "Još nijedna datoteka nije izabrana",
"field.files.empty.single": "No file selected yet",
"field.files.empty.single": "Još nije izabrana datoteka",
"field.layout.change": "Promenite izgled",
"field.layout.delete": "Brisanje rasporeda",
@ -380,15 +383,15 @@
"field.object.empty": "Još nema informacija",
"field.pages.empty": "Još nijedna stranica nije izabrana",
"field.pages.empty.single": "No page selected yet",
"field.pages.empty.single": "Još nije izabrana nijedna stranica",
"field.structure.delete.confirm": "Da li zaista želite da izbrišete ovaj red?",
"field.structure.delete.confirm.all": "Da li zaista želite da izbrišete sve unose?",
"field.structure.delete.confirm.selected": "Do you really want to delete the selected entries?",
"field.structure.delete.confirm.selected": "Da li zaista želite da obrišete izabrane unose?",
"field.structure.empty": "Još nema unosa",
"field.users.empty": "Još nije izabran nijedan korisnik",
"field.users.empty.single": "No user selected yet",
"field.users.empty.single": "Još nije izabran korisnik",
"fields.empty": "Još uvek nema polja",
@ -403,17 +406,17 @@
"file.sort": "Promena pozicije",
"files": "Fajlovi",
"files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.",
"files.delete.confirm.selected": "Da li zaista želite da obrišete izabrane datoteke? Ova radnja se ne može poništiti.",
"files.empty": "Još nema fajlova",
"filter": "Filter",
"form.discard": "Discard changes",
"form.discard.confirm": "Do you really want to <strong>discard all your changes</strong>?",
"form.locked": "This content is disabled for you as it is currently edited by another user",
"form.unsaved": "The current changes have not yet been saved",
"form.preview": "Preview changes",
"form.preview.draft": "Preview draft",
"form.discard": "Odbaci izmene",
"form.discard.confirm": "Da li zaista želite da <strong>odbacite sve vaše promene</strong>?",
"form.locked": "Ovaj sadržaj je onemogućen za vas jer ga trenutno uređuje drugi korisnik",
"form.unsaved": "Trenutne izmene još nisu sačuvane",
"form.preview": "Pregled promena",
"form.preview.draft": "Pregled nacrta",
"hide": "Sakriti",
"hour": "Čas",
@ -458,12 +461,12 @@
"language.variables.empty": "Još uvek nema prevoda",
"language.variable.delete.confirm": "Da li zaista želite da izbrišete promenljivu za {key}?",
"language.variable.entries": "Values",
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
"language.variable.entries": "Vrednosti",
"language.variable.entries.help": "Svaki string će se koristiti za odgovarajući broj, npr. tri stringa će se podudarati da bi se brojalo <code>0</code>, <code>1</code>, <code>2 i više</code>. Koristite <code>{count}</code> rezervisano mesto da biste uneli stvarni broj.",
"language.variable.key": "Ključ",
"language.variable.multiple": "Countable?",
"language.variable.multiple.text": "Use different translation strings",
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
"language.variable.multiple": "Brojivo?",
"language.variable.multiple.text": "Koristite različite prevodilačke stringove",
"language.variable.multiple.help": "Možete koristiti različite vrednosti u zavisnosti od broja koji prosleđujete zajedno sa jezičkom promenljivom, što vam omogućava da kreirate dinamičke prevode, npr. jedninu i množinu.",
"language.variable.notFound": "Promenljiva nije pronađena\n \n \n ",
"language.variable.value": "Vrednost",
@ -483,7 +486,7 @@
"license.code": "Kod",
"license.code.help": "Nakon kupovine dobili ste svoju šifru licence putem email. Molimo vas da je kopirate i nalepite ovde.",
"license.code.label": "Molimo vas unesite šifru licence",
"license.remove.text": "<p>Removing the license will irreversibly delete the license file from this site. You can then activate this site with a different license key or re-register the same license key if the domain remains the same.</p><p>To change the domain associated with the license, please contact the Kirby team. <a href='https://getkirby.com/license'>Read more &rarr;</a></p>",
"license.remove.text": "<p>Uklanjanje licence će nepovratno izbrisati datoteku licence sa ove stranice. Zatim možete aktivirati ovu stranicu drugim licencnim ključem ili ponovo registrovati isti licencni ključ ako domen ostane isti.</p><p>Da biste promenili domen povezan sa licencom, kontaktirajte Kirby tim. <a href='https://getkirby.com/license'>Pročitajte više &rarr;</a></p>",
"license.status.active.info": "Uključuje nove glavne verzije do {date}",
"license.status.active.label": "Validna licenca",
"license.status.demo.info": "Ovo je demo instalacija",
@ -496,8 +499,8 @@
"license.status.missing.bubble": "Da li ste spremni da pokrenete svoj sajt?",
"license.status.missing.info": "Nema važeće licence",
"license.status.missing.label": "Molimo vas aktivirajte svoju licencu",
"license.status.unknown.info": "The license status is unknown",
"license.status.unknown.label": "Unknown",
"license.status.unknown.info": "Status licence je nepoznat",
"license.status.unknown.label": "Nepoznato",
"license.manage": "Upravljajte svojom licencom",
"license.purchased": "Kupljeno",
"license.success": "Hvala vam što podržavate Kirby",
@ -509,10 +512,10 @@
"loading": "Učitavanje",
"lock.unsaved": "Nesačuvane promene",
"lock.unsaved.empty": "There are no unsaved changes",
"lock.unsaved.files": "Unsaved files",
"lock.unsaved.pages": "Unsaved pages",
"lock.unsaved.users": "Unsaved accounts",
"lock.unsaved.empty": "Nema nesačuvanih promena",
"lock.unsaved.files": "Nesačuvane datoteke",
"lock.unsaved.pages": "Nesačuvane stranice",
"lock.unsaved.users": "Nesačuvani nalozi",
"lock.isLocked": "Nesačuvane promene {email}",
"lock.unlock": "Otključati",
"lock.unlock.submit": "Otključajte i zamenite nesačuvane promene <strong>{email}</strong>",
@ -619,7 +622,7 @@
"page.status.unlisted.description": "Stranica je dostupna samo preko URL-a",
"pages": "Stranice",
"pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.",
"pages.delete.confirm.selected": "Da li zaista želite da obrišete izabrane stranice? Ova radnja se ne može poništiti.",
"pages.empty": "Još nema stranica",
"pages.status.draft": "Nacrti",
"pages.status.listed": "Objavljeno",
@ -635,9 +638,11 @@
"plugin": "Dodatak",
"plugins": "Dodaci",
"prev": "Prethodna",
"preview": "Pregled",
"publish": "Publish",
"preview": "Pregled",
"preview.browser.scroll": "Sinhronizacija skrolovanja",
"publish": "Objavi",
"published": "Objavljeno",
"remove": "Ukloniti",
@ -659,9 +664,9 @@
"role.nobody.title": "Niko",
"save": "Sačuvaj",
"saved": "Saved",
"saved": "Sačuvano",
"search": "Pretraga",
"searching": "Searching",
"searching": "Pretraga",
"search.min": "Unesite {min} karakter za pretragu",
"search.all": "Prikaži sve {count} rezultate ",
"search.results.none": "Nema rezultata",
@ -671,7 +676,7 @@
"security": "Bezbednost",
"select": "Izaberite",
"select.all": "Select all",
"select.all": "Izaberite sve",
"server": "Server",
"settings": "Podešavanja",
"show": "Prikaži",
@ -685,8 +690,8 @@
"stats.empty": "Nema izveštaja",
"status": "Status",
"system.info.copy": "Copy info",
"system.info.copied": "System info copied",
"system.info.copy": "Kopiraj informacije",
"system.info.copied": "Sistemske informacije su kopirane",
"system.issues.content": "Čini se da je fascikla sa sadržajem izložena",
"system.issues.eol.kirby": "Vaša instalirana Kirby verzija je stigla do kraja svog životnog veka i neće dobijati dalja bezbednosna ažuriranja",
"system.issues.eol.plugin": "Vaša instalirana verzija { plugin } dodatka je stigla do kraja svog životnog veka i neće dobijati dalja bezbednosna ažuriranja",
@ -695,9 +700,9 @@
"system.issues.git": "Čini se da je .git fascikla izložena",
"system.issues.https": "Preporučujemo HTTPS za sve vaše sajtove",
"system.issues.kirby": "Čini se da je Kirby fascikla izložena",
"system.issues.local": "The site is running locally with relaxed security checks",
"system.issues.local": "Sajt radi lokalno sa opuštenim bezbednosnim proverama",
"system.issues.site": "Čini se da je fascikla sajta izložena",
"system.issues.vue.compiler": "The Vue template compiler is enabled",
"system.issues.vue.compiler": "Kompilator šablona Vue je omogućen",
"system.issues.vulnerability.kirby": "Na vašu instalaciju može uticati sledeća ranjivost ({ severity } severity): { description }",
"system.issues.vulnerability.plugin": "Na vašu instalaciju može uticati sledeća ranjivost u { plugin } dodatku ({ severity } severity): { description }",
"system.updateStatus": "Ažuriraj status",
@ -714,10 +719,10 @@
"tel.placeholder": "+49123456789",
"template": "Šablon",
"theme": "Theme",
"theme.light": "Lights on",
"theme.dark": "Lights off",
"theme.automatic": "Match system default",
"theme": "Tema",
"theme.light": "Svetla",
"theme.dark": "Tamna",
"theme.automatic": "Podrazumevano podudaranje sistema",
"title": "Naslov",
"today": "Danas",
@ -777,26 +782,27 @@
"user.changeLanguage": "Promenite jezik",
"user.changeName": "Preimenujte ovog korisnika",
"user.changePassword": "Promenite lozinku",
"user.changePassword.current": "Your current password",
"user.changePassword.current": "Vaša trenutna lozinka",
"user.changePassword.new": "Nova lozinka",
"user.changePassword.new.confirm": "Potvrdite novu lozinku…",
"user.changePassword.own": "Your own password",
"user.changePassword.own": "Vaša sopstvena lozinka",
"user.changeRole": "Promenite ulogu",
"user.changeRole.select": "Izaberite novu ulogu",
"user.create": "Dodajte novog korisnika",
"user.delete": "Izbrišite ovog korisnika",
"user.delete.confirm": "Da li zaista želite da izbrišete <br><strong>{email}</strong>?",
"user.setPassword": "Postavite lozinku",
"users": "Korisnici",
"version": "Verzija",
"version.changes": "Changed version",
"version.compare": "Compare versions",
"version.changes": "Promenjena verzija",
"version.compare": "Uporedite verzije",
"version.current": "Trenutna verzija",
"version.latest": "Najnovija verzija",
"versionInformation": "Informacije o verziji",
"view": "View",
"view": "Vidite",
"view.account": "Tvoj nalog",
"view.installation": "Instalacija",
"view.languages": "Jezici",

View file

@ -1,5 +1,5 @@
{
"account": "Account",
"account": "Konto",
"account.changeName": "Ändra ditt namn",
"account.delete": "Radera ditt konto",
"account.delete.confirm": "Vill du verkligen radera ditt konto? Du kommer att loggas ut omedelbart. Ditt konto kan inte återställas.",
@ -16,7 +16,7 @@
"changes": "Ändringar",
"confirm": "Spara",
"collapse": "Kollapsa",
"collapse.all": "Collapse all",
"collapse.all": "Stäng alla",
"color": "Färg",
"coordinates": "Koordinater",
"copy": "Kopiera",
@ -44,8 +44,8 @@
"delete": "Radera",
"delete.all": "Radera allt",
"deselect": "Deselect",
"deselect.all": "Deselect all",
"deselect": "Avmarkera",
"deselect.all": "Avmarkera alla",
"dialog.fields.empty": "Den här dialogrutan har inga fält",
"dialog.files.empty": "Inga filer att välja",
@ -204,6 +204,8 @@
"error.page.undefined": "Sidan kan inte hittas",
"error.page.update.permission": "Du har inte behörighet att uppdatera \"{slug}\"",
"error.role.notFound": "Rollen \"{name}\" hittades inte",
"error.section.files.max.plural": "Du får inte lägga till mer än {max} filer till sektionen \"{section}\"",
"error.section.files.max.singular": "Du får inte lägga till mer än en fil i sektionen \"{section}\"",
"error.section.files.min.plural": "Sektionen \"{section}\" kräver minst {min} filer",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Titeln får inte vara tom",
"error.site.changeTitle.permission": "Du har inte behörighet att ändra titeln på webbplatsen",
"error.site.notAccessible": "Sajten är inte tillgänglig",
"error.site.update.permission": "Du har inte behörighet att uppdatera webbplatsen",
"error.structure.validation": "Det finns ett fel i fältet \"{field}\" i rad {index}",
@ -298,12 +301,12 @@
"error.validation.uuid": "Ange ett giltigt UUID",
"error.validation.url": "Ange en giltig URL",
"error.version.discard.permission": "You are not allowed to discard this version",
"error.version.publish.permission": "You are not allowed to publish this version",
"error.version.save.permission": "You are not allowed to change this version",
"error.version.discard.permission": "Du har inte behörighet att slänga denna version",
"error.version.publish.permission": "Du har inte behörighet att publicera denna version",
"error.version.save.permission": "Du har inte behörighet att ändra denna version",
"expand": "Expandera",
"expand.all": "Expand all",
"expand.all": "Öppna alla",
"field.invalid": "Fältet är ogiltigt",
"field.required": "Fältet krävs",
@ -384,7 +387,7 @@
"field.structure.delete.confirm": "Vill du verkligen radera denna rad?",
"field.structure.delete.confirm.all": "Vill du verkligen radera alla poster?",
"field.structure.delete.confirm.selected": "Do you really want to delete the selected entries?",
"field.structure.delete.confirm.selected": "Vill du verkligen radera de valda posterna?",
"field.structure.empty": "Inga poster än",
"field.users.empty": "Inga användare valda än",
@ -458,12 +461,12 @@
"language.variables.empty": "Inga översättningar ännu",
"language.variable.delete.confirm": "Vill du verkligen ta bort variabeln för {key}?",
"language.variable.entries": "Values",
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
"language.variable.entries": "Värden",
"language.variable.entries.help": "Varje textsträng kommer att användas för sitt matchande antal, t.ex. tre strängar kommer att matcha för att få antalen <code>0</code>, <code>1</code>, <code>2 och fler</code>. Använd platshållaren <code>{count}</code> för att infoga det faktiska antalet.",
"language.variable.key": "Nyckel",
"language.variable.multiple": "Countable?",
"language.variable.multiple.text": "Use different translation strings",
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
"language.variable.multiple": "Räknebar?",
"language.variable.multiple.text": "Använd olika översättningssträngar",
"language.variable.multiple.help": "Du kan använda olika värden beroende på ett antal som du skickar tillsammans med språkvariabeln, vilket gör att du kan skapa dynamiska översättningar, t.ex. singular och plural.",
"language.variable.notFound": "Variabeln kunde inte hittas",
"language.variable.value": "Värde",
@ -483,7 +486,7 @@
"license.code": "Kod",
"license.code.help": "Du fick din licenskod efter köpet via e-post. Kopiera och klistra in det här.",
"license.code.label": "Ange din licenskod",
"license.remove.text": "<p>Removing the license will irreversibly delete the license file from this site. You can then activate this site with a different license key or re-register the same license key if the domain remains the same.</p><p>To change the domain associated with the license, please contact the Kirby team. <a href='https://getkirby.com/license'>Read more &rarr;</a></p>",
"license.remove.text": "<p>Om du tar bort licensen raderas licensfilen permanent från webbplatsen. Du kan sedan aktivera webbplatsen med en annan licensnyckel eller omregistrera samma licensnyckel om domänen förblir densamma.</p><p>För att ändra domänen som är kopplad till licensen, vänligen kontakta Kirby-teamet. <a href='https://getkirby.com/license'>Läs mer &rarr;</a></p>",
"license.status.active.info": "Inkluderar nya större versioner fram till {date}",
"license.status.active.label": "Giltig licens",
"license.status.demo.info": "Detta är en demoinstallation",
@ -635,7 +638,9 @@
"plugin": "Tillägg",
"plugins": "Tillägg",
"prev": "Föregående",
"preview": "Förhandsgranska",
"preview.browser.scroll": "Synkronisera skrollning",
"publish": "Publicera",
"published": "Publicerade",
@ -671,7 +676,7 @@
"security": "Säkerhet",
"select": "Välj",
"select.all": "Select all",
"select.all": "Markera alla",
"server": "Server",
"settings": "Inställningar",
"show": "Visa",
@ -780,12 +785,13 @@
"user.changePassword.current": "Ditt nuvarande lösenord",
"user.changePassword.new": "Nytt lösenord",
"user.changePassword.new.confirm": "Bekräfta det nya lösenordet...",
"user.changePassword.own": "Your own password",
"user.changePassword.own": "Ditt eget lösenord",
"user.changeRole": "Ändra roll",
"user.changeRole.select": "Välj en ny roll",
"user.create": "Lägg till en ny användare",
"user.delete": "Radera denna användare",
"user.delete.confirm": "Vill du verkligen radera <br><strong>{email}</strong>?",
"user.setPassword": "Ange lösenord",
"users": "Användare",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "Sayfa bulunamad\u0131",
"error.page.update.permission": "\"{slug}\" güncellemesine izin verilmiyor",
"error.role.notFound": "\"{name}\" rolü bulunamadı",
"error.section.files.max.plural": "\"{section}\" bölümüne {max} dosyadan daha fazlasını eklememelisiniz",
"error.section.files.max.singular": "\"{section}\" bölümüne birden fazla dosya eklememelisiniz",
"error.section.files.min.plural": "\"{section}\" bölümü en az {min} dosya gerektiriyor",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "Başlık boş bırakılamaz",
"error.site.changeTitle.permission": "Sitenin başlığını değiştiremezsin",
"error.site.notAccessible": "Siteye erişilemiyor",
"error.site.update.permission": "Siteyi güncellemenize izin verilmiyor",
"error.structure.validation": "{index} satırındaki \"{field}\" alanında bir hata var",
@ -635,7 +638,9 @@
"plugin": "Eklenti",
"plugins": "Eklentiler",
"prev": "Önceki",
"preview": "Önizle",
"preview.browser.scroll": "Senkronize kaydırma",
"publish": "Yayınla",
"published": "Yayınlandı",
@ -786,6 +791,7 @@
"user.create": "Yeni bir kullanıcı ekle",
"user.delete": "Bu kullanıcıyı sil",
"user.delete.confirm": "<strong>{email}</strong> kullanıcısını silmek istediğinizden emin misiniz?",
"user.setPassword": "Şifre belirle",
"users": "Kullanıcılar",

View file

@ -204,6 +204,8 @@
"error.page.undefined": "\u627e\u4e0d\u5230\u9801\u9762",
"error.page.update.permission": "你沒有更新「{slug}」的權限",
"error.role.notFound": "The role \"{name}\" cannot be found",
"error.section.files.max.plural": "「{section}」區段中最多只能加入 {max} 個檔案",
"error.section.files.max.singular": "「{section}」區段中最多只能加入 1 個檔案",
"error.section.files.min.plural": "「{section}」區段中至少需要 {min} 個檔案",
@ -219,6 +221,7 @@
"error.site.changeTitle.empty": "網站標題不得為空",
"error.site.changeTitle.permission": "你沒有變更網站標題的權限",
"error.site.notAccessible": "The site is not accessible",
"error.site.update.permission": "你沒有變更網站設定的權限",
"error.structure.validation": "第 {index} 列的「{field}」欄位發生錯誤",
@ -635,7 +638,9 @@
"plugin": "外掛",
"plugins": "外掛列表",
"prev": "上一步",
"preview": "預覽",
"preview.browser.scroll": "Sync scroll",
"publish": "發佈",
"published": "已發佈",
@ -786,6 +791,7 @@
"user.create": "新增使用者",
"user.delete": "刪除使用者",
"user.delete.confirm": "你確定要刪除「{email}」嗎?",
"user.setPassword": "Set password",
"users": "使用者",

File diff suppressed because one or more lines are too long

View file

@ -384,12 +384,18 @@
<symbol id="icon-layout-columns" viewBox="0 0 24 24">
<path d="M11 5H5V19H11V5ZM13 5V19H19V5H13ZM4 3H20C20.5523 3 21 3.44772 21 4V20C21 20.5523 20.5523 21 20 21H4C3.44772 21 3 20.5523 3 20V4C3 3.44772 3.44772 3 4 3Z" />
</symbol>
<symbol id="icon-layout-bottom" viewBox="0 0 24 24">
<path d="M21 3C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4C2 3.44772 2.44772 3 3 3H21ZM4 16V19H20V16H4ZM4 14H20V5H4V14Z" />
</symbol>
<symbol id="icon-layout-left" viewBox="0 0 24 24">
<path d="M21 3C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4C2 3.44772 2.44772 3 3 3H21ZM7 5H4V19H7V5ZM20 5H9V19H20V5Z" />
</symbol>
<symbol id="icon-layout-right" viewBox="0 0 24 24">
<path d="M21 3C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4C2 3.44772 2.44772 3 3 3H21ZM15 5H4V19H15V5ZM20 5H17V19H20V5Z" />
</symbol>
<symbol id="icon-layout-top" viewBox="0 0 24 24">
<path d="M21 3C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4C2 3.44772 2.44772 3 3 3H21ZM4 10V19H20V10H4ZM4 8H20V5H4V8Z" />
</symbol>
<symbol id="icon-linkedin" viewBox="0 0 24 24">
<path d="M6.94048 4.99993C6.94011 5.81424 6.44608 6.54702 5.69134 6.85273C4.9366 7.15845 4.07187 6.97605 3.5049 6.39155C2.93793 5.80704 2.78195 4.93715 3.1105 4.19207C3.43906 3.44699 4.18654 2.9755 5.00048 2.99993C6.08155 3.03238 6.94097 3.91837 6.94048 4.99993ZM7.00048 8.47993H3.00048V20.9999H7.00048V8.47993ZM13.3205 8.47993H9.34048V20.9999H13.2805V14.4299C13.2805 10.7699 18.0505 10.4299 18.0505 14.4299V20.9999H22.0005V13.0699C22.0005 6.89993 14.9405 7.12993 13.2805 10.1599L13.3205 8.47993Z"/>
</symbol>
@ -523,6 +529,9 @@
<symbol id="icon-rss" viewBox="0 0 24 24">
<path d="M3 3C12.9411 3 21 11.0589 21 21H18C18 12.7157 11.2843 6 3 6V3ZM3 10C9.07513 10 14 14.9249 14 21H11C11 16.5817 7.41828 13 3 13V10ZM3 17C5.20914 17 7 18.7909 7 21H3V17Z"/>
</symbol>
<symbol id="icon-scroll-to-bottom" viewBox="0 0 24 24">
<path d="M8.01266 4.56502C8.75361 4.16876 9.5587 4 11.1411 4H12.8589C14.4413 4 15.2464 4.16876 15.9873 4.56502C16.6166 4.90155 17.0985 5.38342 17.435 6.01266C17.8312 6.75361 18 7.5587 18 9.14111V14.8589C18 16.4413 17.8312 17.2464 17.435 17.9873C17.0985 18.6166 16.6166 19.0985 15.9873 19.435C15.2464 19.8312 14.4413 20 12.8589 20H11.1411C9.5587 20 8.75361 19.8312 8.01266 19.435C7.38342 19.0985 6.90155 18.6166 6.56502 17.9873C6.16876 17.2464 6 16.4413 6 14.8589V9.14111C6 7.5587 6.16876 6.75361 6.56502 6.01266C6.90155 5.38342 7.38342 4.90155 8.01266 4.56502ZM12.8589 2H11.1411C9.12721 2 8.04724 2.27848 7.06946 2.8014C6.09168 3.32432 5.32432 4.09168 4.8014 5.06946C4.27848 6.04724 4 7.12721 4 9.14111V14.8589C4 16.8728 4.27848 17.9528 4.8014 18.9305C5.32432 19.9083 6.09168 20.6757 7.06946 21.1986C8.04724 21.7215 9.12721 22 11.1411 22H12.8589C14.8728 22 15.9528 21.7215 16.9305 21.1986C17.9083 20.6757 18.6757 19.9083 19.1986 18.9305C19.7215 17.9528 20 16.8728 20 14.8589V9.14111C20 7.12721 19.7215 6.04724 19.1986 5.06946C18.6757 4.09168 17.9083 3.32432 16.9305 2.8014C15.9528 2.27848 14.8728 2 12.8589 2ZM13 6H11V11H13V6ZM7.75781 13.758L12.0005 18.0006L16.2431 13.758L14.8289 12.3438L12.0005 15.1722L9.17203 12.3438L7.75781 13.758Z" />
</symbol>
<symbol id="icon-search" viewBox="0 0 24 24">
<path d="M18.031 16.6168L22.3137 20.8995L20.8995 22.3137L16.6168 18.031C15.0769 19.263 13.124 20 11 20C6.032 20 2 15.968 2 11C2 6.032 6.032 2 11 2C15.968 2 20 6.032 20 11C20 13.124 19.263 15.0769 18.031 16.6168ZM16.0247 15.8748C17.2475 14.6146 18 12.8956 18 11C18 7.1325 14.8675 4 11 4C7.1325 4 4 7.1325 4 11C4 14.8675 7.1325 18 11 18C12.8956 18 14.6146 17.2475 15.8748 16.0247L16.0247 15.8748Z"/>
</symbol>

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,28 @@
import js from "@eslint/js";
import prettier from "eslint-config-prettier";
import vue from "eslint-plugin-vue";
export default [
js.configs.recommended,
...vue.configs["flat/vue2-recommended"],
prettier,
{
rules: {
"vue/attributes-order": "error",
"vue/component-definition-name-casing": "off",
"vue/html-closing-bracket-newline": [
"error",
{
singleline: "never",
multiline: "always"
}
],
"vue/multi-word-component-names": "off",
"vue/require-default-prop": "off",
"vue/require-prop-types": "error"
},
languageOptions: {
ecmaVersion: 2022
}
}
];

175
kirby/panel/vite.config.js Normal file
View file

@ -0,0 +1,175 @@
/* eslint-env node */
import path from "path";
import { defineConfig, loadEnv } from "vite";
import { minify } from "terser";
import vue from "@vitejs/plugin-vue2";
import { viteStaticCopy } from "vite-plugin-static-copy";
import kirby from "./scripts/vite-kirby";
import postcssLightDarkFunction from "@csstools/postcss-light-dark-function";
/**
* Returns all aliases used in the project
*/
function createAliases(proxy) {
const aliases = {
"@": path.resolve(__dirname, "src")
};
if (!process.env.VITEST) {
// use absolute proxied url to avoid Vue being loaded twice
aliases.vue =
proxy.target + ":3000/node_modules/vue/dist/vue.esm.browser.js";
}
return aliases;
}
/**
* Returns the server configuration
*/
async function createServer(proxy) {
return {
allowedHosts: [proxy.target.substring(8)],
cors: { origin: proxy.target },
proxy: {
"/api": proxy,
"/env": proxy,
"/media": proxy
},
open: proxy.target + "/panel",
port: 3000,
...(await createCustomServer())
};
}
/**
* Returns custom server configuration, if it exists
*/
async function createCustomServer() {
try {
const module = await import("./vite.config.custom.js");
return module.default ?? {};
} catch {
return {};
}
}
/**
* Returns an array of plugins used,
* depending on the mode (development or build)
*/
function createPlugins(mode) {
const plugins = [vue(), kirby()];
// when building…
if (mode === "production") {
//copy Vue to the dist directory
plugins.push(
viteStaticCopy({
targets: [
{
src: "node_modules/vue/dist/vue.runtime.esm.js",
dest: "js",
rename: "vue.runtime.esm.min.js",
// TODO: we can simplify this in Vue 3 as a minified version
// is provided by Vue itself by default
transform: async (content) => {
content = content.replaceAll(
"process.env.NODE_ENV",
"'production'"
);
const minified = await minify(content);
return minified.code;
}
},
{
src: "node_modules/vue/dist/vue.esm.browser.min.js",
dest: "js"
},
// Also copy the non-minified version to the dist folder as
// we will expose this for plugins in dev mode with Vue 3
{
src: "node_modules/vue/dist/vue.esm.browser.js",
dest: "js"
}
]
})
);
}
return plugins;
}
/**
* Returns vitest configuration
*/
function createTest() {
return {
environment: "node",
include: ["**/*.test.js"],
setupFiles: ["vitest.setup.js"]
};
}
/**
* Returns the Vite configuration
*/
export default defineConfig(async ({ mode }) => {
// Load env file based on `mode` in the current working directory.
// Set the third parameter to '' to load all env regardless of the `VITE_` prefix.
process.env = {
...process.env,
...loadEnv(mode, process.cwd(), "")
};
const proxy = {
target: process.env.SERVER ?? "https://sandbox.test",
changeOrigin: true,
secure: false
};
return {
plugins: createPlugins(mode),
base: "./",
build: {
minify: "terser",
cssCodeSplit: false,
rollupOptions: {
external: ["vue"],
input: "./src/index.js",
output: {
entryFileNames: "js/[name].min.js",
chunkFileNames: "js/[name].min.js",
assetFileNames: "[ext]/[name].min.[ext]",
manualChunks(id) {
if (id.includes("sortablejs")) {
return "sortable";
}
if (id.includes("node_modules")) {
return "vendor";
}
return null;
}
}
}
},
css: {
postcss: {
plugins: [postcssLightDarkFunction()]
}
},
optimizeDeps: {
entries: "src/**/*.{js,vue}",
exclude: ["vitest", "vue"],
holdUntilCrawlEnd: false
},
resolve: {
alias: createAliases(proxy)
},
server: await createServer(proxy),
test: createTest()
};
});

View file

@ -615,8 +615,16 @@ class Api
public function upload(
Closure $callback,
bool $single = false,
bool $debug = false
bool $debug = false,
string|null $template = null
): array {
return (new Upload($this, $single, $debug))->process($callback);
$upload = new Upload(
api: $this,
single: $single,
debug: $debug,
template: $template
);
return $upload->process($callback);
}
}

View file

@ -107,8 +107,13 @@ class Collection
*/
public function toResponse(): array
{
if ($query = $this->api->requestQuery('query')) {
$this->data = $this->data->query($query);
if (is_array($query = $this->api->requestQuery('query')) === true) {
$this->data = $this->data->query(array_filter([
'limit' => $query['limit'] ?? null,
'offset' => $query['offset'] ?? null,
'paginate' => $query['paginate'] ?? null,
'search' => $query['search'] ?? null,
], fn ($value) => $value !== null));
}
if (!$this->data->pagination()) {

View file

@ -34,7 +34,8 @@ readonly class Upload
public function __construct(
protected Api $api,
protected bool $single = true,
protected bool $debug = false
protected bool $debug = false,
protected string|null $template = null
) {
}
@ -62,10 +63,16 @@ readonly class Upload
public static function chunkSize(): int
{
$max = [
Str::toBytes(ini_get('upload_max_filesize')),
Str::toBytes(ini_get('post_max_size'))
Str::toBytes(ini_get('upload_max_filesize'))
];
$postMaxSize = Str::toBytes(ini_get('post_max_size'));
// in PHP, post_max_size=0 means "no limit", so it must not be treated
if ($postMaxSize > 0) {
$max[] = $postMaxSize;
}
// consider cloudflare proxy limit, if detected
if (isset($_SERVER['HTTP_CF_CONNECTING_IP']) === true) {
$max[] = Str::toBytes('100M');
@ -179,7 +186,7 @@ readonly class Upload
// (incomplete chunk request will return empty $source)
$data = match ($source) {
null => null,
default => $callback($source, $filename)
default => $callback($source, $filename, $this->template)
};
$uploads[$upload['name']] = match (true) {
@ -233,7 +240,7 @@ readonly class Upload
tmp: $tmpRoot,
total: $total,
offset: $this->api->requestHeaders('Upload-Offset'),
template: $this->api->requestBody('template'),
template: $this->template ?? $this->api->requestBody('template'),
);
// stream chunk content and append it to partial file
@ -412,11 +419,12 @@ readonly class Upload
$postMaxSize = Str::toBytes(ini_get('post_max_size'));
$uploadMaxFileSize = Str::toBytes(ini_get('upload_max_filesize'));
// in PHP, post_max_size=0 means "no limit", so it must not be treated
// as smaller than upload_max_filesize.
// @codeCoverageIgnoreStart
if ($postMaxSize < $uploadMaxFileSize) {
if ($postMaxSize > 0 && $postMaxSize < $uploadMaxFileSize) {
throw new Exception(
message:
I18n::translate(
message: I18n::translate(
'upload.error.iniPostSize',
'The uploaded file exceeds the post_max_size directive in php.ini'
)
@ -425,8 +433,7 @@ readonly class Upload
// @codeCoverageIgnoreEnd
throw new Exception(
message:
I18n::translate(
message: I18n::translate(
'upload.error.noFiles',
'No files were uploaded'
)

View file

@ -4,6 +4,7 @@ namespace Kirby\Cms;
use Kirby\Api\Api as BaseApi;
use Kirby\Exception\NotFoundException;
use Kirby\Exception\PermissionException;
use Kirby\Form\Form;
use Kirby\Session\Session;
@ -105,7 +106,7 @@ class Api extends BaseApi
*/
public function files(string $path): Files
{
return $this->parent($path)->files()->filter('isAccessible', true);
return $this->parent($path)->files()->filter('isListable', true);
}
/**
@ -168,7 +169,7 @@ class Api extends BaseApi
default => $parent->children()
};
return $pages->filter('isAccessible', true);
return $pages->filter('isListable', true);
}
/**
@ -183,7 +184,12 @@ class Api extends BaseApi
return $pages->search($this->requestQuery('q'));
}
return $pages->query($this->requestBody());
return $pages->query(array_filter([
'limit' => $this->requestBody('limit'),
'offset' => $this->requestBody('offset'),
'paginate' => $this->requestBody('paginate'),
'search' => $this->requestBody('search'),
], fn ($value) => $value !== null));
}
/**
@ -224,10 +230,13 @@ class Api extends BaseApi
/**
* Returns the site object
*
* @throws \Kirby\Exception\NotFoundException if the site cannot be accessed
* @since 5.4.0
*/
public function site(): Site
{
return $this->kirby->site();
return Find::site();
}
/**
@ -255,6 +264,18 @@ class Api extends BaseApi
*/
public function users(): Users
{
return $this->kirby->users();
return Find::users();
}
/**
* Validates that the acting user has access to the given area.
*
* @throws \Kirby\Exception\PermissionException
*/
public function validateAreaAccess(string $area): void
{
if ($this->kirby->user()?->role()->permissions()->for('access', $area) !== true) {
throw new PermissionException('No access');
}
}
}

View file

@ -36,6 +36,7 @@ use Kirby\Toolkit\LazyValue;
use Kirby\Toolkit\Locale;
use Kirby\Toolkit\Str;
use Kirby\Uuid\Uuid;
use Kirby\Uuid\Uuids;
use Throwable;
/**
@ -102,9 +103,11 @@ class App
$this->core = new Core($this);
$this->events = new Events($this);
// start with a fresh snippet and version cache
// start with fresh caches
Snippet::$cache = [];
VersionCache::reset();
ModelPermissions::$cache = [];
Uuids::$enabled = null;
// register all roots to be able to load stuff afterwards
$this->bakeRoots($props['roots'] ?? []);
@ -421,6 +424,7 @@ class App
public function contentToken(object|null $model, string $value): string
{
$default = $this->root('content');
$default = realpath($default) ?: $default;
if ($model !== null && method_exists($model, 'id') === true) {
$default .= '/' . $model->id();
@ -1029,7 +1033,7 @@ class App
// load the main config options
$root = $this->root('config');
$options = F::load($root . '/config.php', [], allowOutput: false);
$options = F::load($root . '/config.php', [], allowOutput: false, cache: true);
// merge into one clean options array
return $this->options = array_replace_recursive(Config::$data, $options);
@ -1044,7 +1048,7 @@ class App
$root = $this->root('config');
// first load `config/env.php` to access its `url` option
$envOptions = F::load($root . '/env.php', [], allowOutput: false);
$envOptions = F::load($root . '/env.php', [], allowOutput: false, cache: true);
// use the option from the main `config.php`,
// but allow the `env.php` to override it
@ -1258,7 +1262,7 @@ class App
// search for a draft if the page cannot be found
if (!$page && $draft = $site->draft($path)) {
if (
$this->user() ||
($this->user() && $draft->isAccessible()) ||
$draft->renderVersionFromRequest() !== null
) {
$page = $draft;

View file

@ -4,7 +4,6 @@ namespace Kirby\Cms;
use Closure;
use Kirby\Exception\Exception;
use Kirby\Filesystem\F;
use Kirby\Http\Response;
use Kirby\Toolkit\I18n;
use Throwable;
@ -38,6 +37,25 @@ trait AppErrors
*/
protected Whoops $whoops;
/**
* Replaces absolute file paths with placeholders such as
* {kirby_folder}, {site_folder} or {index_folder} to avoid
* exposing too many details about the filesystem and keeping
* error responses short and readable in debug mode.
*
* @since 5.3.0
*/
protected function disguiseFilePath(string $file): string
{
$disguise = [
$this->root('kirby') => '{kirby}',
$this->root('site') => '{site}',
$this->root('index') => '{index}'
];
return str_replace(array_keys($disguise), array_values($disguise), $file);
}
/**
* Registers the PHP error handler for CLI usage
*/
@ -150,10 +168,7 @@ trait AppErrors
'code' => $code,
'message' => $exception->getMessage(),
'details' => $details,
'file' => F::relativepath(
$exception->getFile(),
$this->environment()->get('DOCUMENT_ROOT', '')
),
'file' => $this->disguiseFilePath($exception->getFile()),
'line' => $exception->getLine(),
], $httpCode);
} else {

View file

@ -351,6 +351,7 @@ class Auth
/**
* Returns the hashed ip of the visitor
* which is used to track invalid logins
* @deprecated 5.3.0 Use `$visitor->ip(hash: true)` instead. Will be removed in Kirby 6.
*/
public function ipHash(): string
{
@ -365,7 +366,7 @@ class Auth
*/
public function isBlocked(string $email): bool
{
$ip = $this->ipHash();
$ip = $this->kirby->visitor()->ip(hash: true);
$log = $this->log();
$trials = $this->kirby->option('auth.trials', 10);
@ -669,7 +670,7 @@ class Auth
$this->kirby->trigger('user.login:failed', compact('email'));
}
$ip = $this->ipHash();
$ip = $this->kirby->visitor()->ip(hash: true);
$log = $this->log();
$time = time();

View file

@ -5,6 +5,7 @@ namespace Kirby\Cms;
use Kirby\Content\Content;
use Kirby\Content\Field;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Toolkit\BlockCollectionAccess;
use Kirby\Toolkit\Str;
use Stringable;
use Throwable;
@ -101,6 +102,7 @@ class Block extends Item implements Stringable
/**
* Controller for the block snippet
*/
#[BlockCollectionAccess]
public function controller(): array
{
return [
@ -195,6 +197,7 @@ class Block extends Item implements Stringable
* object. This can be used further
* with all available field methods
*/
#[BlockCollectionAccess]
public function toField(): Field
{
return new Field($this->parent(), $this->id(), $this->toHtml());
@ -203,6 +206,7 @@ class Block extends Item implements Stringable
/**
* Converts the block to HTML
*/
#[BlockCollectionAccess]
public function toHtml(): string
{
try {

View file

@ -124,8 +124,14 @@ class Blueprint
continue;
}
$template = $section->template();
$templates = match ($section->type()) {
'files' => [...$templates, $section->template() ?? 'default'],
'files' => [
...$templates,
...($template
? [$template]
: App::instance()->blueprints('files'))
],
'fields' => [
...$templates,
...$this->acceptedFileTemplatesFromFields($section->fields())

View file

@ -167,7 +167,7 @@ class Collection extends BaseCollection
$groups = new self(parent: $this->parent());
if (is_string($field) === true) {
foreach ($this->data as $key => $item) {
foreach ($this as $key => $item) {
$value = $this->getAttribute($item, $field);
// make sure that there's always a proper value to group by

View file

@ -130,10 +130,14 @@ class Core
'blocks/video' => $this->root . '/blocks/video/video.yml',
// file blueprints
'files/default' => ['title' => 'File'],
'files/default' => [
'title' => 'File'
],
// page blueprints
'pages/default' => ['title' => 'Page'],
'pages/default' => [
'title' => 'Page'
],
// site blueprints
'site' => [

View file

@ -8,6 +8,7 @@ use Kirby\Exception\InvalidArgumentException;
use Kirby\Filesystem\F;
use Kirby\Filesystem\IsFile;
use Kirby\Panel\File as Panel;
use Kirby\Toolkit\BlockCollectionAccess;
use Kirby\Toolkit\Str;
/**
@ -145,6 +146,7 @@ class File extends ModelWithContent
* Returns the url to api endpoint
* @internal
*/
#[BlockCollectionAccess]
public function apiUrl(bool $relative = false): string
{
return $this->parent()->apiUrl($relative) . '/files/' . $this->filename();
@ -375,6 +377,7 @@ class File extends ModelWithContent
* for the file and its versions
* @since 5.0.0
*/
#[BlockCollectionAccess]
public function mediaDir(): string
{
return $this->parent()->mediaDir() . '/' . $this->mediaHash();
@ -383,6 +386,7 @@ class File extends ModelWithContent
/**
* Creates a unique media hash
*/
#[BlockCollectionAccess]
public function mediaHash(): string
{
return $this->mediaToken() . '-' . $this->modifiedFile();
@ -393,6 +397,7 @@ class File extends ModelWithContent
*
* @param string|null $filename Optional override for the filename
*/
#[BlockCollectionAccess]
public function mediaRoot(string|null $filename = null): string
{
$filename ??= $this->filename();
@ -403,6 +408,7 @@ class File extends ModelWithContent
/**
* Creates a non-guessable token string for this file
*/
#[BlockCollectionAccess]
public function mediaToken(): string
{
$token = $this->kirby()->contentToken($this, $this->id());
@ -528,6 +534,7 @@ class File extends ModelWithContent
/**
* Returns the absolute root to the file
*/
#[BlockCollectionAccess]
public function root(): string|null
{
return $this->root ??= $this->parent()->root() . '/' . $this->filename();
@ -598,6 +605,7 @@ class File extends ModelWithContent
* by injecting the information from
* the asset.
*/
#[BlockCollectionAccess]
public function toArray(): array
{
return [
@ -623,6 +631,7 @@ class File extends ModelWithContent
* option is used to disable this behavior or enable it
* on a per-file basis.
*/
#[BlockCollectionAccess]
public function previewUrl(): string|null
{
// check if the clean file URL is accessible,

View file

@ -9,6 +9,7 @@ use Kirby\Content\VersionCache;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\LogicException;
use Kirby\Filesystem\F;
use Kirby\Toolkit\BlockCollectionAccess;
use Kirby\Uuid\Uuid;
use Kirby\Uuid\Uuids;
@ -36,6 +37,7 @@ trait FileActions
*
* @throws \Kirby\Exception\LogicException
*/
#[BlockCollectionAccess]
public function changeName(
string $name,
bool $sanitize = true,
@ -90,6 +92,8 @@ trait FileActions
$newFile->parent()->files()->remove($oldFile->id());
$newFile->parent()->files()->set($newFile->id(), $newFile);
$newFile->uuid()?->populate();
return $newFile;
});
}
@ -97,6 +101,7 @@ trait FileActions
/**
* Changes the file's sorting number in the meta file
*/
#[BlockCollectionAccess]
public function changeSort(int $sort): static
{
// skip if the sort number stays the same
@ -127,6 +132,7 @@ trait FileActions
/**
* @return $this|static
*/
#[BlockCollectionAccess]
public function changeTemplate(string|null $template): static
{
if ($template === $this->template()) {
@ -175,6 +181,7 @@ trait FileActions
/**
* Copy the file to the given page
*/
#[BlockCollectionAccess]
public function copy(Page $page): static
{
F::copy($this->root(), $page->root() . '/' . $this->filename());
@ -189,6 +196,7 @@ trait FileActions
// overwrite with new UUID (remove old, add new)
if (Uuids::enabled() === true) {
$copy = $copy->save(['uuid' => Uuid::generate()]);
$copy->uuid()->populate();
}
return $copy;
@ -204,6 +212,7 @@ trait FileActions
* @throws \Kirby\Exception\InvalidArgumentException
* @throws \Kirby\Exception\LogicException
*/
#[BlockCollectionAccess]
public static function create(array $props, bool $move = false): static
{
$props = static::normalizeProps($props);
@ -292,6 +301,8 @@ trait FileActions
// store the content if necessary
$file->changeStorage($storage);
$file->uuid()?->populate();
// return a fresh clone
return $file->clone();
});
@ -301,6 +312,7 @@ trait FileActions
* Deletes the file. The store is used to
* manipulate the filesystem or whatever you prefer.
*/
#[BlockCollectionAccess]
public function delete(): bool
{
return $this->commit('delete', ['file' => $this], function ($file) {
@ -329,6 +341,7 @@ trait FileActions
/**
* Resizes/crops the original file with Kirby's thumb handler
*/
#[BlockCollectionAccess]
public function manipulate(array|null $options = []): static
{
// nothing to process
@ -351,6 +364,11 @@ trait FileActions
protected static function normalizeProps(array $props): array
{
// Prevent injecting blueprint as this always must be derived from
// the template/model name and blueprint object in the app,
// never directly be supplied by the caller
unset($props['blueprint']);
if (isset($props['source'], $props['parent']) === false) {
throw new InvalidArgumentException(
message: 'Please provide the "source" and "parent" props for the File'
@ -380,6 +398,7 @@ trait FileActions
*
* @return $this
*/
#[BlockCollectionAccess]
public function publish(): static
{
Media::publish($this, $this->mediaRoot());
@ -396,6 +415,7 @@ trait FileActions
* @param bool $move If set to `true`, the source will be deleted
* @throws \Kirby\Exception\LogicException
*/
#[BlockCollectionAccess]
public function replace(string $source, bool $move = false): static
{
$file = $this->clone();
@ -433,6 +453,7 @@ trait FileActions
*
* @return $this
*/
#[BlockCollectionAccess]
public function unpublish(bool $onlyMedia = false): static
{
// unpublish media files
@ -452,6 +473,7 @@ trait FileActions
*
* @throws \Kirby\Exception\InvalidArgumentException If the input array contains invalid values
*/
#[BlockCollectionAccess]
public function update(
array|null $input = null,
string|null $languageCode = null,

View file

@ -5,6 +5,7 @@ namespace Kirby\Cms;
use Kirby\Content\Field;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Filesystem\Asset;
use Kirby\Toolkit\BlockCollectionAccess;
/**
* Trait for image resizing, blurring etc.
@ -20,6 +21,7 @@ trait FileModifications
/**
* Blurs the image by the given amount of pixels
*/
#[BlockCollectionAccess]
public function blur(int|bool $pixels = true): FileVersion|File|Asset
{
return $this->thumb(['blur' => $pixels]);
@ -28,6 +30,7 @@ trait FileModifications
/**
* Converts the image to black and white
*/
#[BlockCollectionAccess]
public function bw(): FileVersion|File|Asset
{
return $this->thumb(['grayscale' => true]);
@ -36,6 +39,7 @@ trait FileModifications
/**
* Crops the image by the given width and height
*/
#[BlockCollectionAccess]
public function crop(
int $width,
int|null $height = null,
@ -66,6 +70,7 @@ trait FileModifications
/**
* Alias for File::bw()
*/
#[BlockCollectionAccess]
public function grayscale(): FileVersion|File|Asset
{
return $this->thumb(['grayscale' => true]);
@ -74,6 +79,7 @@ trait FileModifications
/**
* Alias for File::bw()
*/
#[BlockCollectionAccess]
public function greyscale(): FileVersion|File|Asset
{
return $this->thumb(['grayscale' => true]);
@ -82,6 +88,7 @@ trait FileModifications
/**
* Sets the JPEG compression quality
*/
#[BlockCollectionAccess]
public function quality(int $quality): FileVersion|File|Asset
{
return $this->thumb(['quality' => $quality]);
@ -93,6 +100,7 @@ trait FileModifications
*
* @throws \Kirby\Exception\InvalidArgumentException
*/
#[BlockCollectionAccess]
public function resize(
int|null $width = null,
int|null $height = null,
@ -108,6 +116,7 @@ trait FileModifications
/**
* Sharpens the image
*/
#[BlockCollectionAccess]
public function sharpen(int $amount = 50): FileVersion|File|Asset
{
return $this->thumb(['sharpen' => $amount]);
@ -119,6 +128,7 @@ trait FileModifications
* also be set up in the config with the thumbs.srcsets option.
* @since 3.1.0
*/
#[BlockCollectionAccess]
public function srcset(array|string|null $sizes = null): string|null
{
if (empty($sizes) === true) {
@ -168,6 +178,7 @@ trait FileModifications
*
* @throws \Kirby\Exception\InvalidArgumentException
*/
#[BlockCollectionAccess]
public function thumb(
array|string|null $options = null
): FileVersion|File|Asset {

View file

@ -115,14 +115,14 @@ class Find
$kirby = App::instance();
$model = match ($modelName) {
'site' => $kirby->site(),
'site' => static::site(),
'account' => static::user(),
'page' => static::page(basename($path)),
// regular expression to split the path at the last
// occurrence of /files/ which separates parent path
// and filename
'file' => static::file(...preg_split('$.*\K(/files/)$', $path)),
'user' => $kirby->user(basename($path)),
'user' => static::user(basename($path)),
default => throw new InvalidArgumentException(
message: 'Invalid model type: ' . $modelType
)
@ -133,6 +133,55 @@ class Find
);
}
/**
* Returns the role object for the given name
*
* @param string $name Role name/id
* @throws \Kirby\Exception\NotFoundException if the role cannot be found
*/
public static function role(string $name): Role
{
$role = App::instance()->role($name);
if ($role?->isAccessible() === true) {
return $role;
}
throw new NotFoundException(
key: 'role.notFound',
data: ['name' => $name]
);
}
/**
* Returns all accessible roles
*
* @since 5.4.0
*/
public static function roles(): Roles
{
return App::instance()->roles()->filter('isAccessible', true);
}
/**
* Returns the site object if the site is accessible
*
* @throws \Kirby\Exception\NotFoundException if the site cannot be accessed
* @since 5.4.0
*/
public static function site(): Site
{
$site = App::instance()->site();
if ($site->isAccessible() === true) {
return $site;
}
throw new NotFoundException(
key: 'site.notAccessible'
);
}
/**
* Returns the user object for the given id or
* returns the current authenticated user if no
@ -141,7 +190,7 @@ class Find
* @param string|null $id User's id
* @throws \Kirby\Exception\NotFoundException if the user for the given id cannot be found
*/
public static function user(string|null $id = null): User|null
public static function user(string|null $id = null): User
{
// account is a reserved word to find the current
// user. It's used in various API and area routes.
@ -158,15 +207,35 @@ class Find
$kirby->option('api.allowImpersonation', false)
);
return $user ?? throw new NotFoundException(
if ($user?->isAccessible() === true) {
return $user;
}
throw new NotFoundException(
key: 'user.undefined'
);
}
// get a specific user by id
return $kirby->user($id) ?? throw new NotFoundException(
$user = $kirby->user($id);
if ($user?->isAccessible() === true) {
return $user;
}
throw new NotFoundException(
key: 'user.notFound',
data: ['name' => $id]
);
}
/**
* Returns all accessible users
*
* @since 5.4.0
*/
public static function users(): Users
{
return App::instance()->users()->filter('isListable', true);
}
}

View file

@ -2,6 +2,7 @@
namespace Kirby\Cms;
use Kirby\Toolkit\BlockCollectionAccess;
use Kirby\Uuid\Uuid;
/**
@ -41,6 +42,7 @@ trait HasFiles
*
* @param bool $move If set to `true`, the source will be deleted
*/
#[BlockCollectionAccess]
public function createFile(array $props, bool $move = false): File
{
$props = [

View file

@ -4,6 +4,7 @@ namespace Kirby\Cms;
use Closure;
use Kirby\Exception\BadMethodCallException;
use Kirby\Toolkit\BlockCollectionAccess;
/**
* HasMethods
@ -27,6 +28,7 @@ trait HasMethods
*
* @throws \Kirby\Exception\BadMethodCallException
*/
#[BlockCollectionAccess]
protected function callMethod(string $method, array $args = []): mixed
{
$closure = $this->getMethod($method);
@ -43,6 +45,7 @@ trait HasMethods
/**
* Checks if the object has a registered custom method
*/
#[BlockCollectionAccess]
public function hasMethod(string $method): bool
{
return $this->getMethod($method) !== null;
@ -53,7 +56,8 @@ trait HasMethods
* the current class or from a parent class ordered by
* inheritance order (top to bottom)
*/
protected function getMethod(string $method): Closure|null
#[BlockCollectionAccess]
public function getMethod(string $method): Closure|null
{
if (isset(static::$methods[$method]) === true) {
return static::$methods[$method];

View file

@ -2,6 +2,8 @@
namespace Kirby\Cms;
use Kirby\Toolkit\BlockCollectionAccess;
/**
* HasModels
*
@ -23,6 +25,7 @@ trait HasModels
* Adds new models to the registry
* @internal
*/
#[BlockCollectionAccess]
public static function extendModels(array $models): array
{
return static::$models = [

View file

@ -7,6 +7,7 @@ use Kirby\Exception\Exception;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\NotFoundException;
use Kirby\Filesystem\F;
use Kirby\Toolkit\BlockCollectionAccess;
use Kirby\Toolkit\Locale;
use Kirby\Toolkit\Str;
use Stringable;
@ -153,6 +154,7 @@ class Language implements Stringable
/**
* Creates a new language object
*/
#[BlockCollectionAccess]
public static function create(array $props): static
{
$kirby = App::instance();
@ -216,6 +218,7 @@ class Language implements Stringable
*
* @throws \Kirby\Exception\Exception
*/
#[BlockCollectionAccess]
public function delete(): bool
{
$kirby = App::instance();
@ -301,6 +304,14 @@ class Language implements Stringable
return file_exists($this->root());
}
/**
* Check if the language url is custom domain
*/
public function hasCustomDomain(): bool
{
return Url::isAbsolute($this->url);
}
/**
* Checks if the language is the same
* as the given language or language code
@ -448,6 +459,7 @@ class Language implements Stringable
/**
* Returns the absolute path to the language file
*/
#[BlockCollectionAccess]
public function root(): string
{
return App::instance()->root('languages') . '/' . $this->code() . '.php';
@ -481,6 +493,7 @@ class Language implements Stringable
*
* @return $this
*/
#[BlockCollectionAccess]
public function save(): static
{
$existingData = Data::read($this->root(), fail: false);
@ -547,13 +560,14 @@ class Language implements Stringable
public function toArray(): array
{
return [
'code' => $this->code(),
'default' => $this->isDefault(),
'direction' => $this->direction(),
'locale' => $this->locale(),
'name' => $this->name(),
'rules' => $this->rules(),
'url' => $this->url()
'code' => $this->code(),
'default' => $this->isDefault(),
'direction' => $this->direction(),
'hasCustomDomain' => $this->hasCustomDomain(),
'locale' => $this->locale(),
'name' => $this->name(),
'rules' => $this->rules(),
'url' => $this->url(),
];
}
@ -578,6 +592,7 @@ class Language implements Stringable
/**
* Update language properties and save them
*/
#[BlockCollectionAccess]
public function update(array|null $props = null): static
{
$kirby = App::instance();

View file

@ -0,0 +1,551 @@
<?php
namespace Kirby\Cms;
use Closure;
use Iterator;
use Kirby\Exception\LogicException;
/**
* The LazyCollection class is a variant of the CMS
* Collection that is only initialized with keys for
* each collection element or without any data.
* Collection elements and their values (= objects)
* are loaded and initialized lazily when they are
* first used.
*
* You can use LazyCollection in two ways:
* 1. Initialize with keys only (values are `null`),
* define `hydrateElement` method that initializes
* an element dynamically.
* 2. Option 1, but also don't initialize any keys,
* set `$initialized` prop to `false` and define
* `initialize` method that defines which keys
* are available.
*
* @package Kirby Cms
* @author Lukas Bestle <lukas@getkirby.com>
* @link https://getkirby.com
* @copyright Bastian Allgeier
* @license https://getkirby.com/license
*
* @template TValue
* @extends \Kirby\Cms\Collection<TValue>
*/
abstract class LazyCollection extends Collection
{
/**
* Flag that tells whether hydration has been
* completed for all collection elements;
* this is used to increase performance
*/
protected bool $hydrated = false;
/**
* Flag that tells whether all possible collection
* items have been loaded (only relevant in lazy
* initialization mode)
*/
protected bool $initialized = true;
/**
* Temporary auto-hydration whenever a collection
* method is called; some methods may not need raw
* access to all collection data, so performance
* will be improved if methods call initialization
* or hydration themselves only if they need it
* @deprecated
* @todo Remove this in v6
*/
public function __call(string $key, $arguments)
{
$this->hydrate();
return parent::__call($key, $arguments);
}
/**
* Low-level getter for elements
*
* @return TValue|null
*/
public function __get(string $key)
{
$element = parent::__get($key);
// `$element === null` could mean "element does not exist"
// or "element found but not hydrated"
if (
$element === null &&
(array_key_exists($key, $this->data) || $this->initialized === false)
) {
return $this->hydrateElement($key);
}
return $element;
}
/**
* Low-level element remover
*/
public function __unset(string $key)
{
// first initialize, otherwise a later initialization
// might bring back the element that was unset
$this->initialize();
return parent::__unset($key);
}
/**
* Creates chunks of the same size.
* The last chunk may be smaller
*
* @param int $size Number of elements per chunk
* @return static A new collection with an element for each chunk and
* a sub collection in each chunk
*/
public function chunk(int $size): static
{
// chunking at least requires the collection structure
$this->initialize();
return parent::chunk($size);
}
/**
* Counts all elements
*/
public function count(): int
{
$this->initialize();
return parent::count();
}
/**
* Returns the current element
* @deprecated
* @todo Remove in v6
*
* @return TValue
*/
public function current(): mixed
{
$current = parent::current();
// `$current === null` could mean "empty collection"
// or "element found but not hydrated"
if ($current === null && $key = $this->key()) {
return $this->hydrateElement($key);
}
return $current;
}
/**
* Clone and remove all elements from the collection
*/
public function empty(): static
{
$empty = parent::empty();
// prevent new collection from initializing its
// elements into the now empty collection
// (relevant when emptying a collection that
// has not been (fully) initialized yet)
$empty->initialized = true;
return $empty;
}
/**
* Find one or multiple elements by id
*
* @param string ...$keys
* @return TValue|static
*/
public function find(...$keys)
{
$result = parent::find(...$keys);
// when the result is a cloned collection (multiple keys),
// mark it as initialized to prevent it from initializing
// all of its elements again after we filtered it above
// (relevant when finding elements in a collection that
// has not been (fully) initialized yet)
if ($result instanceof static && $result !== $this) {
$result->initialized = true;
}
return $result;
}
/**
* Returns the elements in reverse order
*/
public function flip(): static
{
// flipping at least requires the collection structure
$this->initialize();
return parent::flip();
}
/**
* Filters elements by one of the
* predefined filter methods, by a
* custom filter function or an array of filters
*/
public function filter(string|array|Closure $field, ...$args): static
{
// to filter through values, we need all values present
$this->hydrate();
return parent::filter($field, ...$args);
}
/**
* Returns the first element
*
* @return TValue|null
*/
public function first()
{
// returning a specific offset requires the collection structure
$this->initialize();
$first = parent::first();
// `$first === null` could mean "empty collection"
// or "element found but not hydrated"
if ($first === null && $key = array_key_first($this->data)) {
return $this->hydrateElement($key);
}
return $first;
}
/**
* Returns an iterator for the elements
* @return \Iterator<TKey, TValue>
*/
public function getIterator(): Iterator
{
// ensure we are looping over all possible elements
$this->initialize();
foreach ($this->data as $key => $value) {
if ($value === null) {
$value = $this->hydrateElement($key);
}
yield $key => $value;
}
}
/**
* Checks by key if an element is included
* @param TKey $key
*/
public function has(mixed $key): bool
{
$this->initialize();
return parent::has($key);
}
/**
* Ensures that all collection elements are loaded,
* essentially converting the lazy collection into a
* normal collection
*/
public function hydrate(): void
{
// first ensure all keys are initialized
$this->initialize();
// skip another hydration loop if no longer needed
if ($this->hydrated === true) {
return;
}
foreach ($this->data as $key => $value) {
if ($value === null) {
$this->hydrateElement($key);
}
}
$this->hydrated = true;
}
/**
* Loads a collection element, sets it in `$this->data[$key]`
* and returns the hydrated object value (or `null` if the
* element does not exist in the collection); to be
* implemented in each specific collection
*/
abstract protected function hydrateElement(string $key): object|null;
/**
* Ensures that the keys for all valid collection elements
* are loaded in the `$data` array and sets `$initialized`
* to `true` afterwards; to be implemented in each collection
* that wants to use lazy initialization; be sure to keep
* existing `$data` values and not overwrite the entire array
*/
public function initialize(): void
{
if ($this->initialized === true) {
return;
}
throw new LogicException(static::class . ' class does not implement `initialize()` method that is required for lazy initialization'); // @codeCoverageIgnore
}
/**
* Returns an array of all keys
*/
public function keys(): array
{
// ensure we are returning all possible keys
$this->initialize();
return parent::keys();
}
/**
* Tries to find the key for the given element
*
* @param TValue $needle the element to search for
* @return int|string|false the name of the key or false
*/
public function keyOf(mixed $needle): int|string|false
{
// quick lookup without having to hydrate the collection
// (keys in CMS collections are the object IDs)
if (
is_object($needle) === true &&
method_exists($needle, 'id') === true
) {
return $needle->id();
}
$this->hydrate();
return parent::keyOf($needle);
}
/**
* Returns the last element
*
* @return TValue|null
*/
public function last()
{
// returning a specific offset requires the collection structure
$this->initialize();
$last = parent::last();
// `$last === null` could mean "empty collection"
// or "element found but not hydrated"
if ($last === null && $key = array_key_last($this->data)) {
return $this->hydrateElement($key);
}
return $last;
}
/**
* Map a function to each element
*
* @return $this
*/
public function map(callable $callback): static
{
// to map a function, we need all values present
$this->hydrate();
return parent::map($callback);
}
/**
* Moves the cursor to the next element
* and returns it
* @deprecated
* @todo Remove in v6
*
* @return TValue
*/
public function next(): mixed
{
$this->initialize();
$next = parent::next();
// `$next === null` could mean "empty collection"
// or "element found but not hydrated"
if ($next === null && $key = $this->key()) {
return $this->hydrateElement($key);
}
return $next;
}
/**
* Returns the nth element from the collection
*
* @return TValue|null
*/
public function nth(int $n)
{
// returning a specific offset requires the collection structure
$this->initialize();
$nth = parent::nth($n);
// `$nth === null` could mean "empty collection"
// or "element found but not hydrated"
if ($nth === null) {
$key = array_keys($this->data)[$n] ?? null;
if (is_string($key) === true) {
return $this->hydrateElement($key);
}
}
return $nth;
}
/**
* Prepends an element to the data array
*
* ```php
* $collection->prepend('key', $value);
* $collection->prepend($value);
* ```
*
* @param string|TValue ...$args
* @return $this
*/
public function prepend(...$args): static
{
// prepending to an uninitialized collection would
// destroy the order on later initialization
$this->initialize();
return parent::prepend(...$args);
}
/**
* Moves the cursor to the previous element
* and returns it
* @deprecated
* @todo Remove in v6
*
* @return TValue
*/
public function prev(): mixed
{
$this->initialize();
$prev = parent::prev();
// `$prev === null` could mean "empty collection"
// or "element found but not hydrated"
if ($prev === null && $key = $this->key()) {
return $this->hydrateElement($key);
}
return $prev;
}
/**
* Returns a new collection consisting of random elements,
* from the original collection, shuffled or ordered
*/
public function random(int $count = 1, bool $shuffle = false): static
{
// picking random elements at least requires the collection structure
$this->initialize();
return parent::random($count, $shuffle);
}
/**
* Shuffle all elements
*/
public function shuffle(): static
{
// shuffling at least requires the collection structure
$this->initialize();
return parent::shuffle();
}
/**
* Returns a slice of the object
*
* @param int $offset The optional index to start the slice from
* @param int|null $limit The optional number of elements to return
* @return $this|static
* @psalm-return ($offset is 0 && $limit is null ? $this : static)
*/
public function slice(
int $offset = 0,
int|null $limit = null
): static {
// returning a specific subset requires the collection structure
$this->initialize();
return parent::slice($offset, $limit);
}
/**
* Sorts the elements by any number of fields
*
* ```php
* $collection->sort('fieldName');
* $collection->sort('fieldName', 'desc');
* $collection->sort('fieldName', 'asc', SORT_REGULAR);
* $collection->sort(fn ($a) => ...);
* ```
*
* @param string|callable $field Field name or value callback to sort by
* @param string|null $direction asc or desc
* @param int|null $method The sort flag, SORT_REGULAR, SORT_NUMERIC etc.
* @return $this|static
*/
public function sort(...$args): static
{
// to sort through values, we need all values present
$this->hydrate();
return parent::sort(...$args);
}
/**
* Converts all objects in the collection
* to an array. This can also take a callback
* function to further modify the array result.
*/
public function toArray(Closure|null $map = null): array
{
// to export an array, we need all values present
$this->hydrate();
return parent::toArray($map);
}
/**
* Returns a non-associative array
* with all values. If a mapping Closure is passed,
* all values are processed by the Closure.
*/
public function values(Closure|null $map = null): array
{
// to export an array, we need all values present
$this->hydrate();
return parent::values($map);
}
}

View file

@ -20,6 +20,13 @@ enum LicenseStatus: string
*/
case Active = 'active';
/**
* Compliance with the conditional license
* requirements has been confirmed
* @since 5.4.0
*/
case Acknowledged = 'acknowledged';
/**
* Only used for the demo instance
*/
@ -57,10 +64,11 @@ enum LicenseStatus: string
public function activatable(): bool
{
return match ($this) {
static::Active,
static::Inactive,
static::Legacy => true,
default => false
static::Active => true,
static::Acknowledged => true,
static::Inactive => true,
static::Legacy => true,
default => false
};
}
@ -84,12 +92,13 @@ enum LicenseStatus: string
public function icon(): string
{
return match ($this) {
static::Active => 'check',
static::Demo => 'preview',
static::Inactive => 'clock',
static::Legacy => 'alert',
static::Missing => 'key',
static::Unknown => 'question',
static::Active => 'check',
static::Acknowledged => 'shield',
static::Demo => 'preview',
static::Inactive => 'clock',
static::Legacy => 'alert',
static::Missing => 'key',
static::Unknown => 'question',
};
}
@ -118,9 +127,10 @@ enum LicenseStatus: string
public function renewable(): bool
{
return match ($this) {
static::Active,
static::Demo => false,
default => true
static::Active => false,
static::Acknowledged => false,
static::Demo => false,
default => true
};
}
@ -132,12 +142,13 @@ enum LicenseStatus: string
public function theme(): string
{
return match ($this) {
static::Active => 'positive',
static::Demo => 'notice',
static::Inactive => 'notice',
static::Legacy => 'negative',
static::Missing => 'love',
static::Unknown => 'passive',
static::Active => 'positive',
static::Acknowledged => 'passive',
static::Demo => 'notice',
static::Inactive => 'notice',
static::Legacy => 'negative',
static::Missing => 'love',
static::Unknown => 'passive',
};
}

View file

@ -26,6 +26,12 @@ enum LicenseType: string
*/
case Enterprise = 'enterprise';
/**
* Develop/private installation
* @since 5.4.0
*/
case Free = 'free';
/**
* Invalid license codes
*/
@ -44,6 +50,7 @@ enum LicenseType: string
return match (true) {
static::Basic->isValidCode($code) => static::Basic,
static::Enterprise->isValidCode($code) => static::Enterprise,
static::Free->isValidCode($code) => static::Free,
static::Legacy->isValidCode($code) => static::Legacy,
default => static::Invalid
};
@ -70,6 +77,7 @@ enum LicenseType: string
return match ($this) {
static::Basic => 38,
static::Enterprise => 38,
static::Free => strlen(static::Free->prefix()),
static::Legacy => 39,
static::Invalid => 0,
};
@ -83,6 +91,7 @@ enum LicenseType: string
return match ($this) {
static::Basic => 'Kirby Basic',
static::Enterprise => 'Kirby Enterprise',
static::Free => I18n::translate('license.free.label'),
static::Legacy => 'Kirby 3',
static::Invalid => I18n::translate('license.unregistered.label'),
};
@ -96,6 +105,7 @@ enum LicenseType: string
return match ($this) {
static::Basic => 'K-BAS-',
static::Enterprise => 'K-ENT-',
static::Free => 'FREE',
static::Legacy => 'K3-PRO-',
static::Invalid => null,
};

View file

@ -7,6 +7,7 @@ use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\NotFoundException;
use Kirby\Filesystem\Dir;
use Kirby\Filesystem\F;
use Kirby\Http\Response;
use Kirby\Toolkit\Str;
use Throwable;
@ -53,7 +54,7 @@ class Media
}
// send the file to the browser
return Response::file($file->publish()->mediaRoot());
return Response::file($file->publish()->root());
}
// try to generate a thumb for the file

View file

@ -145,10 +145,20 @@ class ModelCommit
'create' => [
'user' => $state
],
'createAvatar' => [
'user' => $state
],
'delete' => [
'status' => $state,
'user' => $model
],
'deleteAvatar' => [
'status' => $state,
'user' => $model
],
'replaceAvatar' => [
'user' => $state
],
default => [
'newUser' => $state,
'oldUser' => $model

View file

@ -19,7 +19,7 @@ abstract class ModelPermissions
protected const CATEGORY = 'model';
protected array $options;
protected static array $cache = [];
public static array $cache = [];
public function __construct(protected ModelWithContent|Language $model)
{

View file

@ -17,6 +17,7 @@ use Kirby\Exception\InvalidArgumentException;
use Kirby\Form\Fields;
use Kirby\Form\Form;
use Kirby\Panel\Model;
use Kirby\Toolkit\BlockCollectionAccess;
use Kirby\Toolkit\Str;
use Kirby\Uuid\Identifiable;
use Kirby\Uuid\Uuid;
@ -109,6 +110,7 @@ abstract class ModelWithContent implements Identifiable, Stringable
* @since 5.0.0
* @unstable
*/
#[BlockCollectionAccess]
public function changeStorage(Storage|string $toStorage, bool $copy = false): static
{
if (is_string($toStorage) === true) {
@ -132,6 +134,7 @@ abstract class ModelWithContent implements Identifiable, Stringable
*
* @todo eventually refactor without need of propertyData
*/
#[BlockCollectionAccess]
public function clone(array $props = []): static
{
$props = array_replace_recursive($this->propertyData, $props);
@ -268,6 +271,7 @@ abstract class ModelWithContent implements Identifiable, Stringable
/**
* Decrement a given field value
*/
#[BlockCollectionAccess]
public function decrement(
string $field,
int $by = 1,
@ -300,6 +304,7 @@ abstract class ModelWithContent implements Identifiable, Stringable
* Creates a clone and fetches all
* lazy-loaded getters to get a full copy
*/
#[BlockCollectionAccess]
public function hardcopy(): static
{
$clone = $this->clone();
@ -324,6 +329,7 @@ abstract class ModelWithContent implements Identifiable, Stringable
/**
* Increment a given field value
*/
#[BlockCollectionAccess]
public function increment(
string $field,
int $by = 1,
@ -442,6 +448,7 @@ abstract class ModelWithContent implements Identifiable, Stringable
* Low-level method to save the model with the given data.
* Consider using `::update()` instead.
*/
#[BlockCollectionAccess]
public function save(
array|null $data = null,
string|null $languageCode = null,
@ -655,6 +662,7 @@ abstract class ModelWithContent implements Identifiable, Stringable
*
* @throws \Kirby\Exception\InvalidArgumentException If the input array contains invalid values
*/
#[BlockCollectionAccess]
public function update(
array|null $input = null,
string|null $languageCode = null,
@ -723,6 +731,7 @@ abstract class ModelWithContent implements Identifiable, Stringable
* @internal
* @deprecated 5.0.0 Use `->version()->save()` instead
*/
#[BlockCollectionAccess]
public function writeContent(array $data, string|null $languageCode = null): bool
{
Helpers::deprecated('$model->writeContent() is deprecated. Use $model->version()->save() instead.'); // @codeCoverageIgnore

View file

@ -13,6 +13,7 @@ use Kirby\Http\Response;
use Kirby\Panel\Page as Panel;
use Kirby\Template\Template;
use Kirby\Toolkit\A;
use Kirby\Toolkit\BlockCollectionAccess;
use Kirby\Toolkit\LazyValue;
use Kirby\Toolkit\Str;
use Throwable;
@ -123,7 +124,7 @@ class Page extends ModelWithContent
*/
public function __construct(array $props)
{
if (isset($props['slug']) === false) {
if (isset($props['slug']) === false || $props['slug'] === '') {
throw new InvalidArgumentException(
message: 'The page slug is required'
);
@ -194,6 +195,7 @@ class Page extends ModelWithContent
* Returns the url to the api endpoint
* @internal
*/
#[BlockCollectionAccess]
public function apiUrl(bool $relative = false): string
{
if ($relative === true) {
@ -298,6 +300,7 @@ class Page extends ModelWithContent
*
* @throws \Kirby\Exception\InvalidArgumentException If the controller returns invalid objects for `kirby`, `site`, `pages` or `page`
*/
#[BlockCollectionAccess]
public function controller(
array $data = [],
string $contentType = 'html'
@ -423,6 +426,7 @@ class Page extends ModelWithContent
* @param array $options Options for `Kirby\Http\Uri` to create URL parts
* @param int $code HTTP status code
*/
#[BlockCollectionAccess]
public function go(array $options = [], int $code = 302): void
{
Response::go($this->url($options), $code);
@ -472,6 +476,7 @@ class Page extends ModelWithContent
/**
* Returns the inventory of files children and content files
*/
#[BlockCollectionAccess]
public function inventory(): array
{
if ($this->inventory !== null) {
@ -768,6 +773,7 @@ class Page extends ModelWithContent
/**
* Returns the absolute path to the media folder for the page
*/
#[BlockCollectionAccess]
public function mediaDir(): string
{
return $this->kirby()->root('media') . '/pages/' . $this->id();
@ -776,6 +782,7 @@ class Page extends ModelWithContent
/**
* @see `::mediaDir`
*/
#[BlockCollectionAccess]
public function mediaRoot(): string
{
return $this->mediaDir();
@ -887,6 +894,7 @@ class Page extends ModelWithContent
* Returns the preview URL with authentication for drafts and versions
* @unstable
*/
#[BlockCollectionAccess]
public function previewUrl(VersionId|string $versionId = 'latest'): string|null
{
if ($this->permissions()->can('preview') !== true) {
@ -907,6 +915,7 @@ class Page extends ModelWithContent
* @param \Kirby\Content\VersionId|string|null $versionId Optional override for the auto-detected version to render
* @throws \Kirby\Exception\NotFoundException If the default template cannot be found
*/
#[BlockCollectionAccess]
public function render(
array $data = [],
$contentType = 'html',
@ -1003,6 +1012,7 @@ class Page extends ModelWithContent
* based on the token authentication in the current request
* @unstable
*/
#[BlockCollectionAccess]
public function renderVersionFromRequest(): VersionId|null
{
$request = $this->kirby()->request();
@ -1037,6 +1047,7 @@ class Page extends ModelWithContent
/**
* @throws \Kirby\Exception\NotFoundException If the content representation cannot be found
*/
#[BlockCollectionAccess]
public function representation(mixed $type): Template
{
$kirby = $this->kirby();
@ -1056,6 +1067,7 @@ class Page extends ModelWithContent
* Returns the absolute root to the page directory
* No matter if it exists or not.
*/
#[BlockCollectionAccess]
public function root(): string
{
return $this->root ??= $this->kirby()->root('content') . '/' . $this->diruri();
@ -1074,6 +1086,7 @@ class Page extends ModelWithContent
/**
* Search all pages within the current page
*/
#[BlockCollectionAccess]
public function search(string|null $query = null, string|array $params = []): Pages
{
return $this->index()->search($query, $params);
@ -1190,6 +1203,7 @@ class Page extends ModelWithContent
* Converts the most important
* properties to array
*/
#[BlockCollectionAccess]
public function toArray(): array
{
return [

View file

@ -12,6 +12,7 @@ use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\LogicException;
use Kirby\Filesystem\Dir;
use Kirby\Toolkit\A;
use Kirby\Toolkit\BlockCollectionAccess;
use Kirby\Toolkit\I18n;
use Kirby\Toolkit\Str;
use Kirby\Uuid\Uuid;
@ -30,14 +31,16 @@ trait PageActions
{
/**
* Changes the sorting number.
* The sorting number must already be correct
* when the method is called.
* This only affects this page,
* siblings will not be resorted.
* Low-level method, ensure to run all necessary checks
* (e.g. permissions) before calling it. The sorting number
* must already be correct when the method is called.
* This only affects this page, siblings will not be resorted.
* @internal
*
* @return $this|static
* @throws \Kirby\Exception\LogicException If a draft is being sorted or the directory cannot be moved
*/
#[BlockCollectionAccess]
public function changeNum(int|null $num = null): static
{
if ($this->isDraft() === true) {
@ -82,6 +85,7 @@ trait PageActions
* @return $this|static
* @throws \Kirby\Exception\LogicException If the directory cannot be moved
*/
#[BlockCollectionAccess]
public function changeSlug(
string $slug,
string|null $languageCode = null
@ -119,7 +123,7 @@ trait PageActions
]);
// clear UUID cache recursively (for children and files as well)
$oldPage->uuid()?->clear(true);
$oldPage->uuid()?->clear(recursive: true);
if ($oldPage->exists() === true) {
// actually move stuff on disk
@ -142,6 +146,8 @@ trait PageActions
Dir::remove($oldPage->mediaRoot());
}
$newPage->uuid()?->populate(recursive: true);
return $newPage;
});
}
@ -197,6 +203,7 @@ trait PageActions
* @param int|null $position Optional sorting number
* @throws \Kirby\Exception\InvalidArgumentException If an invalid status is being passed
*/
#[BlockCollectionAccess]
public function changeStatus(
string $status,
int|null $position = null
@ -285,6 +292,7 @@ trait PageActions
*
* @return $this|static
*/
#[BlockCollectionAccess]
public function changeSort(int|null $position = null): static
{
return $this->changeStatus('listed', $position);
@ -296,6 +304,7 @@ trait PageActions
* @return $this|static
* @throws \Kirby\Exception\LogicException If the textfile cannot be renamed/moved
*/
#[BlockCollectionAccess]
public function changeTemplate(string $template): static
{
if ($template === $this->intendedTemplate()->name()) {
@ -311,6 +320,7 @@ trait PageActions
/**
* Change the page title
*/
#[BlockCollectionAccess]
public function changeTitle(
string $title,
string|null $languageCode = null
@ -359,10 +369,14 @@ trait PageActions
}
/**
* Copies the page to a new parent
* Copies the page to a new parent.
* Low-level method, ensure to run all necessary checks
* (e.g. permissions) before calling it.
* @internal
*
* @throws \Kirby\Exception\DuplicateException If the page already exists
*/
#[BlockCollectionAccess]
public function copy(array $options = []): static
{
$slug = $options['slug'] ?? $this->slug();
@ -422,12 +436,15 @@ trait PageActions
parent: $parentModel
);
$copy->uuid()?->populate(recursive: true);
return $copy;
}
/**
* Creates and stores a new page
*/
#[BlockCollectionAccess]
public static function create(array $props): Page
{
$props = self::normalizeProps($props);
@ -478,7 +495,11 @@ trait PageActions
],
function ($page) use ($storage) {
// move to final storage
return $page->changeStorage($storage);
$page->changeStorage($storage);
$page->uuid()?->populate();
return $page;
}
);
@ -493,6 +514,7 @@ trait PageActions
/**
* Creates a child of the current page
*/
#[BlockCollectionAccess]
public function createChild(array $props): Page
{
$props = [
@ -517,6 +539,7 @@ trait PageActions
* Create the sorting number for the page
* depending on the blueprint settings
*/
#[BlockCollectionAccess]
public function createNum(int|null $num = null): int
{
$mode = $this->blueprint()->num();
@ -573,6 +596,7 @@ trait PageActions
/**
* Deletes the page
*/
#[BlockCollectionAccess]
public function delete(bool $force = false): bool
{
return $this->commit('delete', ['page' => $this, 'force' => $force], function ($page, $force) {
@ -583,7 +607,7 @@ trait PageActions
$page->changeStorage(ImmutableMemoryStorage::class);
// clear UUID cache
$page->uuid()?->clear();
$page->uuid()?->clear(recursive: true);
// Explanation: The two while loops below are only
// necessary because our property caches result in
@ -625,6 +649,7 @@ trait PageActions
* Duplicates the page with the given
* slug and optionally copies all files
*/
#[BlockCollectionAccess]
public function duplicate(string|null $slug = null, array $options = []): static
{
// create the slug for the duplicate
@ -657,6 +682,7 @@ trait PageActions
* Moves the page to a new parent if the
* new parent accepts the page type
*/
#[BlockCollectionAccess]
public function move(Site|Page $parent): Page
{
// nothing to move
@ -697,12 +723,19 @@ trait PageActions
);
}
$newPage->uuid()?->populate(recursive: true);
return $newPage;
});
}
protected static function normalizeProps(array $props): array
{
// Prevent injecting blueprint as this always must be derived from
// the template/model name and blueprint object in the app,
// never directly be supplied by the caller
unset($props['blueprint']);
$content = $props['content'] ?? [];
$template = $props['template'] ?? 'default';
@ -720,6 +753,7 @@ trait PageActions
* @return $this|static
* @throws \Kirby\Exception\LogicException If the folder cannot be moved
*/
#[BlockCollectionAccess]
public function publish(): static
{
if ($this->isDraft() === false) {
@ -767,6 +801,7 @@ trait PageActions
*
* @return $this
*/
#[BlockCollectionAccess]
public function purge(): static
{
parent::purge();
@ -836,6 +871,7 @@ trait PageActions
/**
* @internal
*/
#[BlockCollectionAccess]
public function resortSiblingsAfterUnlisting(): bool
{
$index = 0;
@ -872,6 +908,7 @@ trait PageActions
* @return $this|static
* @throws \Kirby\Exception\LogicException If the folder cannot be moved
*/
#[BlockCollectionAccess]
public function unpublish(): static
{
if ($this->isDraft() === true) {
@ -916,6 +953,7 @@ trait PageActions
/**
* Updates the page data
*/
#[BlockCollectionAccess]
public function update(
array|null $input = null,
string|null $languageCode = null,

View file

@ -214,6 +214,12 @@ class PageRules
);
}
// creating a non-draft bypasses the normal publish flow;
// enforce the same rules
if ($page->isDraft() === false) {
self::publish($page);
}
self::validateSlugLength($page->slug());
self::validateSlugProtectedPaths($page, $page->slug());

View file

@ -62,10 +62,12 @@ class Permissions
'update' => true
],
'site' => [
'access' => true,
'changeTitle' => true,
'update' => true
],
'users' => [
'access' => true,
'changeEmail' => true,
'changeLanguage' => true,
'changeName' => true,
@ -73,15 +75,18 @@ class Permissions
'changeRole' => true,
'create' => true,
'delete' => true,
'list' => true,
'update' => true
],
'user' => [
'access' => true,
'changeEmail' => true,
'changeLanguage' => true,
'changeName' => true,
'changePassword' => true,
'changeRole' => true,
'delete' => true,
'list' => true,
'update' => true
]
];

Some files were not shown because too many files have changed in this diff Show more