Update Kirby and dependencies

This commit is contained in:
Paul Nicoué 2022-08-31 16:08:03 +02:00
parent 07150a0011
commit c7c5d615a4
451 changed files with 67540 additions and 63740 deletions

197
composer.lock generated
View file

@ -53,16 +53,16 @@
}, },
{ {
"name": "claviska/simpleimage", "name": "claviska/simpleimage",
"version": "3.6.5", "version": "3.7.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/claviska/SimpleImage.git", "url": "https://github.com/claviska/SimpleImage.git",
"reference": "00f90662686696b9b7157dbb176183aabe89700f" "reference": "abd15ced313c7b8041d7d73d8d2398b4f2510cf1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/claviska/SimpleImage/zipball/00f90662686696b9b7157dbb176183aabe89700f", "url": "https://api.github.com/repos/claviska/SimpleImage/zipball/abd15ced313c7b8041d7d73d8d2398b4f2510cf1",
"reference": "00f90662686696b9b7157dbb176183aabe89700f", "reference": "abd15ced313c7b8041d7d73d8d2398b4f2510cf1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -90,7 +90,7 @@
"description": "A PHP class that makes working with images as simple as possible.", "description": "A PHP class that makes working with images as simple as possible.",
"support": { "support": {
"issues": "https://github.com/claviska/SimpleImage/issues", "issues": "https://github.com/claviska/SimpleImage/issues",
"source": "https://github.com/claviska/SimpleImage/tree/3.6.5" "source": "https://github.com/claviska/SimpleImage/tree/3.7.0"
}, },
"funding": [ "funding": [
{ {
@ -98,7 +98,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2021-12-01T12:42:55+00:00" "time": "2022-07-05T13:18:44+00:00"
}, },
{ {
"name": "filp/whoops", "name": "filp/whoops",
@ -173,35 +173,53 @@
}, },
{ {
"name": "getkirby/cms", "name": "getkirby/cms",
"version": "3.6.3", "version": "3.7.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/getkirby/kirby.git", "url": "https://github.com/getkirby/kirby.git",
"reference": "6b20fa11843f57cd9a1e611bc9e8e8a91b855156" "reference": "021561f7444896fc9917eccb52768a6e715e9a74"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/getkirby/kirby/zipball/6b20fa11843f57cd9a1e611bc9e8e8a91b855156", "url": "https://api.github.com/repos/getkirby/kirby/zipball/021561f7444896fc9917eccb52768a6e715e9a74",
"reference": "6b20fa11843f57cd9a1e611bc9e8e8a91b855156", "reference": "021561f7444896fc9917eccb52768a6e715e9a74",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"claviska/simpleimage": "3.6.5", "claviska/simpleimage": "3.7.0",
"ext-ctype": "*", "ext-ctype": "*",
"ext-curl": "*",
"ext-dom": "*",
"ext-filter": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mbstring": "*", "ext-mbstring": "*",
"ext-openssl": "*",
"ext-simplexml": "*",
"filp/whoops": "2.14.5", "filp/whoops": "2.14.5",
"getkirby/composer-installer": "^1.2.1", "getkirby/composer-installer": "^1.2.1",
"laminas/laminas-escaper": "2.9.0", "laminas/laminas-escaper": "2.10.0",
"michelf/php-smartypants": "1.8.1", "michelf/php-smartypants": "1.8.1",
"php": ">=7.4.0 <8.2.0", "php": ">=7.4.0 <8.2.0",
"phpmailer/phpmailer": "6.5.4", "phpmailer/phpmailer": "6.6.4",
"psr/log": "1.1.4", "symfony/polyfill-intl-idn": "1.26.0",
"symfony/polyfill-intl-idn": "1.24.0", "symfony/polyfill-mbstring": "1.26.0"
"symfony/polyfill-mbstring": "1.24.0"
}, },
"replace": { "replace": {
"symfony/polyfill-php72": "*" "symfony/polyfill-php72": "*"
}, },
"suggest": {
"ext-PDO": "Support for using databases",
"ext-apcu": "Support for the Apcu cache driver",
"ext-exif": "Support for exif information from images",
"ext-fileinfo": "Improved mime type detection for files",
"ext-intl": "Improved i18n number formatting",
"ext-memcached": "Support for the Memcached cache driver",
"ext-zip": "Support for ZIP archive file functions",
"ext-zlib": "Sanitization and validation for svgz files"
},
"type": "kirby-cms", "type": "kirby-cms",
"extra": { "extra": {
"unused": [ "unused": [
@ -250,7 +268,7 @@
"type": "custom" "type": "custom"
} }
], ],
"time": "2022-03-22T09:36:50+00:00" "time": "2022-08-30T18:27:48+00:00"
}, },
{ {
"name": "getkirby/composer-installer", "name": "getkirby/composer-installer",
@ -338,33 +356,33 @@
}, },
{ {
"name": "laminas/laminas-escaper", "name": "laminas/laminas-escaper",
"version": "2.9.0", "version": "2.10.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laminas/laminas-escaper.git", "url": "https://github.com/laminas/laminas-escaper.git",
"reference": "891ad70986729e20ed2e86355fcf93c9dc238a5f" "reference": "58af67282db37d24e584a837a94ee55b9c7552be"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/891ad70986729e20ed2e86355fcf93c9dc238a5f", "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/58af67282db37d24e584a837a94ee55b9c7552be",
"reference": "891ad70986729e20ed2e86355fcf93c9dc238a5f", "reference": "58af67282db37d24e584a837a94ee55b9c7552be",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.3 || ~8.0.0 || ~8.1.0" "ext-ctype": "*",
"ext-mbstring": "*",
"php": "^7.4 || ~8.0.0 || ~8.1.0"
}, },
"conflict": { "conflict": {
"zendframework/zend-escaper": "*" "zendframework/zend-escaper": "*"
}, },
"require-dev": { "require-dev": {
"infection/infection": "^0.26.6",
"laminas/laminas-coding-standard": "~2.3.0", "laminas/laminas-coding-standard": "~2.3.0",
"phpunit/phpunit": "^9.3", "maglnet/composer-require-checker": "^3.8.0",
"psalm/plugin-phpunit": "^0.12.2", "phpunit/phpunit": "^9.5.18",
"vimeo/psalm": "^3.16" "psalm/plugin-phpunit": "^0.16.1",
}, "vimeo/psalm": "^4.22.0"
"suggest": {
"ext-iconv": "*",
"ext-mbstring": "*"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -396,7 +414,7 @@
"type": "community_bridge" "type": "community_bridge"
} }
], ],
"time": "2021-09-02T17:10:53+00:00" "time": "2022-03-08T20:15:36+00:00"
}, },
{ {
"name": "league/color-extractor", "name": "league/color-extractor",
@ -512,21 +530,24 @@
}, },
{ {
"name": "mullema/k3-image-clip", "name": "mullema/k3-image-clip",
"version": "3.0.0", "version": "3.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/mullema/k3-image-clip.git", "url": "https://github.com/mullema/k3-image-clip.git",
"reference": "ac6a4a461ae8972557da24755005a3937a275b0c" "reference": "c2e01f2ceb9eb5bc56895177359d398a3a2dbcf4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/mullema/k3-image-clip/zipball/ac6a4a461ae8972557da24755005a3937a275b0c", "url": "https://api.github.com/repos/mullema/k3-image-clip/zipball/c2e01f2ceb9eb5bc56895177359d398a3a2dbcf4",
"reference": "ac6a4a461ae8972557da24755005a3937a275b0c", "reference": "c2e01f2ceb9eb5bc56895177359d398a3a2dbcf4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"getkirby/composer-installer": "^1.2" "getkirby/composer-installer": "^1.2"
}, },
"conflict": {
"getkirby/cms": "<3.6"
},
"type": "kirby-plugin", "type": "kirby-plugin",
"extra": { "extra": {
"installer-name": "k3-image-clip" "installer-name": "k3-image-clip"
@ -545,22 +566,22 @@
"description": "Visual image clip for Kirby 3", "description": "Visual image clip for Kirby 3",
"support": { "support": {
"issues": "https://github.com/mullema/k3-image-clip/issues", "issues": "https://github.com/mullema/k3-image-clip/issues",
"source": "https://github.com/mullema/k3-image-clip/tree/3.0.0" "source": "https://github.com/mullema/k3-image-clip/tree/3.1.0"
}, },
"time": "2021-12-05T21:47:42+00:00" "time": "2022-06-26T08:59:46+00:00"
}, },
{ {
"name": "phpmailer/phpmailer", "name": "phpmailer/phpmailer",
"version": "v6.5.4", "version": "v6.6.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git", "url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "c0d9f7dd3c2aa247ca44791e9209233829d82285" "reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/c0d9f7dd3c2aa247ca44791e9209233829d82285", "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/a94fdebaea6bd17f51be0c2373ab80d3d681269b",
"reference": "c0d9f7dd3c2aa247ca44791e9209233829d82285", "reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -572,8 +593,8 @@
"require-dev": { "require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"doctrine/annotations": "^1.2", "doctrine/annotations": "^1.2",
"php-parallel-lint/php-console-highlighter": "^0.5.0", "php-parallel-lint/php-console-highlighter": "^1.0.0",
"php-parallel-lint/php-parallel-lint": "^1.3.1", "php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcompatibility/php-compatibility": "^9.3.5", "phpcompatibility/php-compatibility": "^9.3.5",
"roave/security-advisories": "dev-latest", "roave/security-advisories": "dev-latest",
"squizlabs/php_codesniffer": "^3.6.2", "squizlabs/php_codesniffer": "^3.6.2",
@ -617,7 +638,7 @@
"description": "PHPMailer is a full-featured email creation and transfer class for PHP", "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": { "support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues", "issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.5.4" "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.4"
}, },
"funding": [ "funding": [
{ {
@ -625,34 +646,34 @@
"type": "github" "type": "github"
} }
], ],
"time": "2022-02-17T08:19:04+00:00" "time": "2022-08-22T09:22:00+00:00"
}, },
{ {
"name": "psr/log", "name": "psr/log",
"version": "1.1.4", "version": "3.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/php-fig/log.git", "url": "https://github.com/php-fig/log.git",
"reference": "d49695b909c3b7628b6289db5479a1c204601f11" "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
"reference": "d49695b909c3b7628b6289db5479a1c204601f11", "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.0" "php": ">=8.0.0"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.1.x-dev" "dev-master": "3.x-dev"
} }
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Psr\\Log\\": "Psr/Log/" "Psr\\Log\\": "src"
} }
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
@ -673,9 +694,9 @@
"psr-3" "psr-3"
], ],
"support": { "support": {
"source": "https://github.com/php-fig/log/tree/1.1.4" "source": "https://github.com/php-fig/log/tree/3.0.0"
}, },
"time": "2021-05-03T11:20:27+00:00" "time": "2021-07-14T16:46:02+00:00"
}, },
{ {
"name": "sylvainjule/matomo", "name": "sylvainjule/matomo",
@ -717,16 +738,16 @@
}, },
{ {
"name": "symfony/polyfill-ctype", "name": "symfony/polyfill-ctype",
"version": "v1.25.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git", "url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "30885182c981ab175d4d034db0f6f469898070ab" "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
"reference": "30885182c981ab175d4d034db0f6f469898070ab", "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -741,7 +762,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -779,7 +800,7 @@
"portable" "portable"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -795,20 +816,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-10-20T20:35:02+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-idn", "name": "symfony/polyfill-intl-idn",
"version": "v1.24.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git", "url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "749045c69efb97c70d25d7463abba812e91f3a44" "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/749045c69efb97c70d25d7463abba812e91f3a44", "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/59a8d271f00dd0e4c2e518104cc7963f655a1aa8",
"reference": "749045c69efb97c70d25d7463abba812e91f3a44", "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -822,7 +843,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -866,7 +887,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.24.0" "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -882,20 +903,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-09-14T14:02:44+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-normalizer", "name": "symfony/polyfill-intl-normalizer",
"version": "v1.25.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" "reference": "219aa369ceff116e673852dce47c3a41794c14bd"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd",
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", "reference": "219aa369ceff116e673852dce47c3a41794c14bd",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -907,7 +928,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -950,7 +971,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0" "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -966,20 +987,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-02-19T12:13:01+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.24.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git", "url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -994,7 +1015,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -1033,7 +1054,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.0" "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -1049,20 +1070,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-11-30T18:21:41+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "twig/twig", "name": "twig/twig",
"version": "v3.3.8", "version": "v3.4.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/twigphp/Twig.git", "url": "https://github.com/twigphp/Twig.git",
"reference": "972d8604a92b7054828b539f2febb0211dd5945c" "reference": "e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c", "url": "https://api.github.com/repos/twigphp/Twig/zipball/e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077",
"reference": "972d8604a92b7054828b539f2febb0211dd5945c", "reference": "e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1077,7 +1098,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.3-dev" "dev-master": "3.4-dev"
} }
}, },
"autoload": { "autoload": {
@ -1113,7 +1134,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/twigphp/Twig/issues", "issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.3.8" "source": "https://github.com/twigphp/Twig/tree/v3.4.2"
}, },
"funding": [ "funding": [
{ {
@ -1125,7 +1146,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-02-04T06:59:48+00:00" "time": "2022-08-12T06:47:24+00:00"
} }
], ],
"packages-dev": [], "packages-dev": [],

View file

@ -6,10 +6,19 @@
root = true root = true
[*.php] [*]
charset = utf-8 charset = utf-8
end_of_line = lf end_of_line = lf
insert_final_newline = true indent_style = tab
indent_size = 2
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.php]
indent_size = 4
insert_final_newline = true
[*.yml]
indent_style = space indent_style = space
indent_size = 4
[*.md]
trim_trailing_whitespace = false

View file

@ -1,6 +0,0 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}

View file

@ -1,4 +0,0 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}

View file

@ -28,7 +28,7 @@ Please post all bug reports in our [issue tracker](https://github.com/getkirby/k
If you have ideas for a feature or enhancement for Kirby, please use our [feedback platform](https://feedback.getkirby.com). If you have ideas for a feature or enhancement for Kirby, please use our [feedback platform](https://feedback.getkirby.com).
**Translations, bug fixes, code contributions ...** **Translations, bug fixes, code contributions ...**
Read about how to contribute to the development in our [contributing guide](/.github/CONTRIBUTING.md). Read about how to contribute to the development in our [contributing guide](/CONTRIBUTING.md).

View file

@ -5,31 +5,28 @@
* stop at older or too recent versions * stop at older or too recent versions
*/ */
if ( if (
version_compare(PHP_VERSION, '7.4.0', '>=') === false || version_compare(PHP_VERSION, '7.4.0', '>=') === false ||
version_compare(PHP_VERSION, '8.2.0', '<') === false version_compare(PHP_VERSION, '8.2.0', '<') === false
) { ) {
die(include __DIR__ . '/views/php.php'); die(include __DIR__ . '/views/php.php');
} }
if (is_file($autoloader = dirname(__DIR__) . '/vendor/autoload.php')) { if (is_file($autoloader = dirname(__DIR__) . '/vendor/autoload.php')) {
/**
/** * Always prefer a site-wide Composer autoloader
* Always prefer a site-wide Composer autoloader * if it exists, it means that the user has probably
* if it exists, it means that the user has probably * installed additional packages
* installed additional packages */
*/ include $autoloader;
include $autoloader;
} elseif (is_file($autoloader = __DIR__ . '/vendor/autoload.php')) { } elseif (is_file($autoloader = __DIR__ . '/vendor/autoload.php')) {
/**
/** * Fall back to the local autoloader if that exists
* Fall back to the local autoloader if that exists */
*/ include $autoloader;
include $autoloader;
} else { } else {
/**
/** * If neither one exists, don't bother searching;
* If neither one exists, don't bother searching; * it's a custom directory setup and the users need to
* it's a custom directory setup and the users need to * load the autoloader themselves
* load the autoloader themselves */
*/
} }

View file

@ -1,7 +1,7 @@
## ##
## Bundle of CA Root Certificates ## Bundle of CA Root Certificates
## ##
## Certificate data from Mozilla as of: Fri Mar 18 12:29:51 2022 GMT ## Certificate data from Mozilla as of: Tue Jul 19 03:12:06 2022 GMT
## ##
## This is a bundle of X.509 certificates of public Certificate Authorities ## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates ## (CA). These were automatically extracted from Mozilla's root certificates
@ -14,7 +14,7 @@
## Just configure this file as the SSLCACertificateFile. ## Just configure this file as the SSLCACertificateFile.
## ##
## Conversion done with mk-ca-bundle.pl version 1.29. ## Conversion done with mk-ca-bundle.pl version 1.29.
## SHA256: 187ef9dc231135324fe78830cf4462f1ecdeab3e6c9d5e38d623391e88dc5d3c ## SHA256: 9bf3799611fb58197f61d45e71ce3dc19f30e7dd73731915872ce5108a7bb066
## ##
@ -993,30 +993,6 @@ tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29
mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
-----END CERTIFICATE----- -----END CERTIFICATE-----
Hellenic Academic and Research Institutions RootCA 2011
=======================================================
-----BEGIN CERTIFICATE-----
MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT
O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y
aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT
AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo
IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI
1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa
71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u
8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH
3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/
MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8
MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu
b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt
XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD
/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N
7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4
-----END CERTIFICATE-----
Actalis Authentication Root CA Actalis Authentication Root CA
============================== ==============================
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
@ -3279,3 +3255,206 @@ PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/C
r8deVl5c1RxYIigL9zC2L7F8AjEA8GE8p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh r8deVl5c1RxYIigL9zC2L7F8AjEA8GE8p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh
4rsUecrNIdSUtUlD 4rsUecrNIdSUtUlD
-----END CERTIFICATE----- -----END CERTIFICATE-----
Telia Root CA v2
================
-----BEGIN CERTIFICATE-----
MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQxCzAJBgNVBAYT
AkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2
MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQK
DBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZI
hvcNAQEBBQADggIPADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ7
6zBqAMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9vVYiQJ3q
9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9lRdU2HhE8Qx3FZLgmEKn
pNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTODn3WhUidhOPFZPY5Q4L15POdslv5e2QJl
tI5c0BE0312/UqeBAMN/mUWZFdUXyApT7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW
5olWK8jjfN7j/4nlNW4o6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNr
RBH0pUPCTEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6WT0E
BXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63RDolUK5X6wK0dmBR4
M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZIpEYslOqodmJHixBTB0hXbOKSTbau
BcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGjYzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7W
xy+G2CQ5MB0GA1UdDgQWBBRyrOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYD
VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ
8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi0f6X+J8wfBj5
tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMMA8iZGok1GTzTyVR8qPAs5m4H
eW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBSSRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+C
y748fdHif64W1lZYudogsYMVoe+KTTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygC
QMez2P2ccGrGKMOF6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15
h2Er3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMtTy3EHD70
sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pTVmBds9hCG1xLEooc6+t9
xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAWysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQ
raVplI/owd8k+BsHMYeB2F326CjYSlKArBPuUBQemMc=
-----END CERTIFICATE-----
D-TRUST BR Root CA 1 2020
=========================
-----BEGIN CERTIFICATE-----
MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQswCQYDVQQGEwJE
RTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEJSIFJvb3QgQ0EgMSAy
MDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNV
BAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAG
ByqGSM49AgEGBSuBBAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7
dPYSzuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0QVK5buXu
QqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/VbNafAkl1bK6CKBrqx9t
MA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6gPKA6hjhodHRwOi8vY3JsLmQtdHJ1c3Qu
bmV0L2NybC9kLXRydXN0X2JyX3Jvb3RfY2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVj
dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxP
PUQtVHJ1c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQD
AwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFWwKrY7RjEsK70Pvom
AjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHVdWNbFJWcHwHP2NVypw87
-----END CERTIFICATE-----
D-TRUST EV Root CA 1 2020
=========================
-----BEGIN CERTIFICATE-----
MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQswCQYDVQQGEwJE
RTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEVWIFJvb3QgQ0EgMSAy
MDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNV
BAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAG
ByqGSM49AgEGBSuBBAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8
ZRCC/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rDwpdhQntJ
raOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3OqQo5FD4pPfsazK2/umL
MA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6gPKA6hjhodHRwOi8vY3JsLmQtdHJ1c3Qu
bmV0L2NybC9kLXRydXN0X2V2X3Jvb3RfY2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVj
dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxP
PUQtVHJ1c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQD
AwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CAy/m0sRtW9XLS/BnR
AjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJbgfM0agPnIjhQW+0ZT0MW
-----END CERTIFICATE-----
DigiCert TLS ECC P384 Root G5
=============================
-----BEGIN CERTIFICATE-----
MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQswCQYDVQQGEwJV
UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURpZ2lDZXJ0IFRMUyBFQ0MgUDM4
NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMx
FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQg
Um9vdCBHNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1Tzvd
lHJS7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp0zVozptj
n4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICISB4CIfBFqMA4GA1UdDwEB
/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQCJao1H5+z8blUD2Wds
Jk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQLgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIx
AJSdYsiJvRmEFOml+wG4DXZDjC5Ty3zfDBeWUA==
-----END CERTIFICATE-----
DigiCert TLS RSA4096 Root G5
============================
-----BEGIN CERTIFICATE-----
MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBNMQswCQYDVQQG
EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0
MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcNNDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJV
UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2
IFJvb3QgRzUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS8
7IE+ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG02C+JFvuU
AT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgpwgscONyfMXdcvyej/Ces
tyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZMpG2T6T867jp8nVid9E6P/DsjyG244gXa
zOvswzH016cpVIDPRFtMbzCe88zdH5RDnU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnV
DdXifBBiqmvwPXbzP6PosMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9q
TXeXAaDxZre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cdLvvy
z6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvXKyY//SovcfXWJL5/
MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNeXoVPzthwiHvOAbWWl9fNff2C+MIk
wcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPLtgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4E
FgQUUTMc7TZArxfTJc1paPKvTiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8w
DQYJKoZIhvcNAQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw
GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7HPNtQOa27PShN
lnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLFO4uJ+DQtpBflF+aZfTCIITfN
MBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQREtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/
u4cnYiWB39yhL/btp/96j1EuMPikAdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9G
OUrYU9DzLjtxpdRv/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh
47a+p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilwMUc/dNAU
FvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WFqUITVuwhd4GTWgzqltlJ
yqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCKovfepEWFJqgejF0pW8hL2JpqA15w8oVP
bEtoL8pU9ozaMv7Da4M/OMZ+
-----END CERTIFICATE-----
Certainly Root R1
=================
-----BEGIN CERTIFICATE-----
MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAwPTELMAkGA1UE
BhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2VydGFpbmx5IFJvb3QgUjEwHhcN
MjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2Vy
dGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBANA21B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O
5MQTvqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbedaFySpvXl
8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b01C7jcvk2xusVtyWMOvwl
DbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGI
XsXwClTNSaa/ApzSRKft43jvRl5tcdF5cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkN
KPl6I7ENPT2a/Z2B7yyQwHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQ
AjeZjOVJ6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA2Cnb
rlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyHWyf5QBGenDPBt+U1
VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMReiFPCyEQtkA6qyI6BJyLm4SGcprS
p6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
DgQWBBTgqj8ljZ9EXME66C6ud0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAsz
HQNTVfSVcOQrPbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d
8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi1wrykXprOQ4v
MMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrdrRT90+7iIgXr0PK3aBLXWopB
GsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9ditaY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+
gjwN/KUD+nsa2UUeYNrEjvn8K8l7lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgH
JBu6haEaBQmAupVjyTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7
fpYnKx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLyyCwzk5Iw
x06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5nwXARPbv0+Em34yaXOp/S
X3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6OV+KmalBWQewLK8=
-----END CERTIFICATE-----
Certainly Root E1
=================
-----BEGIN CERTIFICATE-----
MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQswCQYDVQQGEwJV
UzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBFMTAeFw0yMTA0
MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlu
bHkxGjAYBgNVBAMTEUNlcnRhaW5seSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4
fxzf7flHh4axpMCK+IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9
YBk2QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8EBAMCAQYw
DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4hevIIgcwCgYIKoZIzj0E
AwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozmut6Dacpps6kFtZaSF4fC0urQe87YQVt8
rgIwRt7qy12a7DLCZRawTDBcMPPaTnOGBtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR
-----END CERTIFICATE-----
E-Tugra Global Root CA RSA v3
=============================
-----BEGIN CERTIFICATE-----
MIIF8zCCA9ugAwIBAgIUDU3FzRYilZYIfrgLfxUGNPt5EDQwDQYJKoZIhvcNAQELBQAwgYAxCzAJ
BgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4xHTAb
BgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290
IENBIFJTQSB2MzAeFw0yMDAzMTgwOTA3MTdaFw00NTAzMTIwOTA3MTdaMIGAMQswCQYDVQQGEwJU
UjEPMA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRF
LVR1Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBSU0Eg
djMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCiZvCJt3J77gnJY9LTQ91ew6aEOErx
jYG7FL1H6EAX8z3DeEVypi6Q3po61CBxyryfHUuXCscxuj7X/iWpKo429NEvx7epXTPcMHD4QGxL
sqYxYdE0PD0xesevxKenhOGXpOhL9hd87jwH7eKKV9y2+/hDJVDqJ4GohryPUkqWOmAalrv9c/SF
/YP9f4RtNGx/ardLAQO/rWm31zLZ9Vdq6YaCPqVmMbMWPcLzJmAy01IesGykNz709a/r4d+ABs8q
QedmCeFLl+d3vSFtKbZnwy1+7dZ5ZdHPOrbRsV5WYVB6Ws5OUDGAA5hH5+QYfERaxqSzO8bGwzrw
bMOLyKSRBfP12baqBqG3q+Sx6iEUXIOk/P+2UNOMEiaZdnDpwA+mdPy70Bt4znKS4iicvObpCdg6
04nmvi533wEKb5b25Y08TVJ2Glbhc34XrD2tbKNSEhhw5oBOM/J+JjKsBY04pOZ2PJ8QaQ5tndLB
eSBrW88zjdGUdjXnXVXHt6woq0bM5zshtQoK5EpZ3IE1S0SVEgpnpaH/WwAH0sDM+T/8nzPyAPiM
bIedBi3x7+PmBvrFZhNb/FAHnnGGstpvdDDPk1Po3CLW3iAfYY2jLqN4MpBs3KwytQXk9TwzDdbg
h3cXTJ2w2AmoDVf3RIXwyAS+XF1a4xeOVGNpf0l0ZAWMowIDAQABo2MwYTAPBgNVHRMBAf8EBTAD
AQH/MB8GA1UdIwQYMBaAFLK0ruYt9ybVqnUtdkvAG1Mh0EjvMB0GA1UdDgQWBBSytK7mLfcm1ap1
LXZLwBtTIdBI7zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAImocn+M684uGMQQ
gC0QDP/7FM0E4BQ8Tpr7nym/Ip5XuYJzEmMmtcyQ6dIqKe6cLcwsmb5FJ+Sxce3kOJUxQfJ9emN4
38o2Fi+CiJ+8EUdPdk3ILY7r3y18Tjvarvbj2l0Upq7ohUSdBm6O++96SmotKygY/r+QLHUWnw/q
ln0F7psTpURs+APQ3SPh/QMSEgj0GDSz4DcLdxEBSL9htLX4GdnLTeqjjO/98Aa1bZL0SmFQhO3s
SdPkvmjmLuMxC1QLGpLWgti2omU8ZgT5Vdps+9u1FGZNlIM7zR6mK7L+d0CGq+ffCsn99t2HVhjY
sCxVYJb6CH5SkPVLpi6HfMsg2wY+oF0Dd32iPBMbKaITVaA9FCKvb7jQmhty3QUBjYZgv6Rn7rWl
DdF/5horYmbDB7rnoEgcOMPpRfunf/ztAmgayncSd6YAVSgU7NbHEqIbZULpkejLPoeJVF3Zr52X
nGnnCv8PWniLYypMfUeUP95L6VPQMPHF9p5J3zugkaOj/s1YzOrfr28oO6Bpm4/srK4rVJ2bBLFH
IK+WEj5jlB0E5y67hscMmoi/dkfv97ALl2bSRM9gUgfh1SxKOidhd8rXj+eHDjD/DLsE4mHDosiX
YY60MGo8bcIHX0pzLz/5FooBZu+6kcpSV3uu1OYP3Qt6f4ueJiDPO++BcYNZ
-----END CERTIFICATE-----
E-Tugra Global Root CA ECC v3
=============================
-----BEGIN CERTIFICATE-----
MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMwgYAxCzAJBgNV
BAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4xHTAbBgNV
BAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENB
IEVDQyB2MzAeFw0yMDAzMTgwOTQ2NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEP
MA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1
Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBFQ0MgdjMw
djAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQKczLWYHMjLiSF4mDKpL2
w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YKfWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31
Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQ
zPUwHQYDVR0OBBYEFP+CMXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjO
PQQDAwNpADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/67W4W
Aie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFxvmjkI6TZraE3
-----END CERTIFICATE-----

View file

@ -1,90 +1,110 @@
{ {
"name": "getkirby/cms", "name": "getkirby/cms",
"description": "The Kirby 3 core", "description": "The Kirby 3 core",
"license": "proprietary", "license": "proprietary",
"type": "kirby-cms", "type": "kirby-cms",
"version": "3.6.3", "version": "3.7.5",
"keywords": [ "keywords": [
"kirby", "kirby",
"cms", "cms",
"core" "core"
], ],
"authors": [ "authors": [
{ {
"name": "Kirby Team", "name": "Kirby Team",
"email": "support@getkirby.com", "email": "support@getkirby.com",
"homepage": "https://getkirby.com" "homepage": "https://getkirby.com"
} }
], ],
"homepage": "https://getkirby.com", "homepage": "https://getkirby.com",
"support": { "support": {
"email": "support@getkirby.com", "email": "support@getkirby.com",
"issues": "https://github.com/getkirby/kirby/issues", "issues": "https://github.com/getkirby/kirby/issues",
"forum": "https://forum.getkirby.com", "forum": "https://forum.getkirby.com",
"source": "https://github.com/getkirby/kirby" "source": "https://github.com/getkirby/kirby"
}, },
"_comment": "TODO: psr/log is not used by Kirby; drop pinned version when Kirby no longer supports PHP 7", "require": {
"require": { "php": ">=7.4.0 <8.2.0",
"php": ">=7.4.0 <8.2.0", "ext-SimpleXML": "*",
"ext-ctype": "*", "ext-ctype": "*",
"ext-mbstring": "*", "ext-curl": "*",
"claviska/simpleimage": "3.6.5", "ext-dom": "*",
"filp/whoops": "2.14.5", "ext-filter": "*",
"getkirby/composer-installer": "^1.2.1", "ext-hash": "*",
"laminas/laminas-escaper": "2.9.0", "ext-iconv": "*",
"michelf/php-smartypants": "1.8.1", "ext-json": "*",
"phpmailer/phpmailer": "6.5.4", "ext-libxml": "*",
"psr/log": "1.1.4", "ext-mbstring": "*",
"symfony/polyfill-intl-idn": "1.24.0", "ext-openssl": "*",
"symfony/polyfill-mbstring": "1.24.0" "claviska/simpleimage": "3.7.0",
}, "filp/whoops": "2.14.5",
"replace": { "getkirby/composer-installer": "^1.2.1",
"symfony/polyfill-php72": "*" "laminas/laminas-escaper": "2.10.0",
}, "michelf/php-smartypants": "1.8.1",
"autoload": { "phpmailer/phpmailer": "6.6.4",
"psr-4": { "symfony/polyfill-intl-idn": "1.26.0",
"Kirby\\": "src/" "symfony/polyfill-mbstring": "1.26.0"
}, },
"classmap": [ "replace": {
"dependencies/" "symfony/polyfill-php72": "*"
], },
"files": [ "suggest": {
"config/setup.php", "ext-PDO": "Support for using databases",
"config/helpers.php" "ext-apcu": "Support for the Apcu cache driver",
] "ext-exif": "Support for exif information from images",
}, "ext-fileinfo": "Improved mime type detection for files",
"config": { "ext-intl": "Improved i18n number formatting",
"allow-plugins": { "ext-memcached": "Support for the Memcached cache driver",
"getkirby/composer-installer": true "ext-zip": "Support for ZIP archive file functions",
}, "ext-zlib": "Sanitization and validation for svgz files"
"optimize-autoloader": true, },
"platform-check": false "autoload": {
}, "psr-4": {
"extra": { "Kirby\\": "src/"
"unused": [ },
"symfony/polyfill-intl-idn" "classmap": [
] "dependencies/"
}, ],
"scripts": { "files": [
"post-update-cmd": "curl -o cacert.pem https://curl.se/ca/cacert.pem", "config/setup.php",
"analyze": [ "config/helpers.php"
"@analyze:composer", ]
"@analyze:psalm", },
"@analyze:phpcpd", "config": {
"@analyze:phpmd" "allow-plugins": {
], "getkirby/composer-installer": true
"analyze:composer": "composer validate --strict --no-check-version --no-check-all", },
"analyze:phpcpd": "phpcpd --fuzzy --exclude tests --exclude vendor .", "optimize-autoloader": true,
"analyze:phpmd": "phpmd . ansi phpmd.xml.dist --exclude 'dependencies/*,tests/*,vendor/*'", "platform": {
"analyze:psalm": "psalm", "php": "7.4.0"
"build": "./scripts/build", },
"ci": [ "platform-check": false
"@fix", },
"@analyze", "extra": {
"@test" "unused": [
], "symfony/polyfill-intl-idn"
"fix": "php-cs-fixer fix", ]
"test": "phpunit --stderr --coverage-html=tests/coverage", },
"zip": "composer archive --format=zip --file=dist" "scripts": {
} "post-update-cmd": "curl -o cacert.pem https://curl.se/ca/cacert.pem",
"analyze": [
"@analyze:composer",
"@analyze:psalm",
"@analyze:phpcpd",
"@analyze:phpmd"
],
"analyze:composer": "composer validate --strict --no-check-version --no-check-all",
"analyze:phpcpd": "phpcpd --fuzzy --exclude tests --exclude vendor .",
"analyze:phpmd": "phpmd . ansi phpmd.xml.dist --exclude 'dependencies/*,tests/*,vendor/*'",
"analyze:psalm": "psalm",
"build": "./scripts/build",
"ci": [
"@fix",
"@analyze",
"@test"
],
"fix": "php-cs-fixer fix",
"test": "phpunit --stderr --coverage-html=tests/coverage",
"zip": "composer archive --format=zip --file=dist"
}
} }

114
kirby/composer.lock generated
View file

@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "d4cf75084dae428fe0ab54124637d51f", "content-hash": "fb087946fb5ac5910e25a4d263905d99",
"packages": [ "packages": [
{ {
"name": "claviska/simpleimage", "name": "claviska/simpleimage",
"version": "3.6.5", "version": "3.7.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/claviska/SimpleImage.git", "url": "https://github.com/claviska/SimpleImage.git",
"reference": "00f90662686696b9b7157dbb176183aabe89700f" "reference": "abd15ced313c7b8041d7d73d8d2398b4f2510cf1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/claviska/SimpleImage/zipball/00f90662686696b9b7157dbb176183aabe89700f", "url": "https://api.github.com/repos/claviska/SimpleImage/zipball/abd15ced313c7b8041d7d73d8d2398b4f2510cf1",
"reference": "00f90662686696b9b7157dbb176183aabe89700f", "reference": "abd15ced313c7b8041d7d73d8d2398b4f2510cf1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -45,7 +45,7 @@
"description": "A PHP class that makes working with images as simple as possible.", "description": "A PHP class that makes working with images as simple as possible.",
"support": { "support": {
"issues": "https://github.com/claviska/SimpleImage/issues", "issues": "https://github.com/claviska/SimpleImage/issues",
"source": "https://github.com/claviska/SimpleImage/tree/3.6.5" "source": "https://github.com/claviska/SimpleImage/tree/3.7.0"
}, },
"funding": [ "funding": [
{ {
@ -53,7 +53,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2021-12-01T12:42:55+00:00" "time": "2022-07-05T13:18:44+00:00"
}, },
{ {
"name": "filp/whoops", "name": "filp/whoops",
@ -175,33 +175,33 @@
}, },
{ {
"name": "laminas/laminas-escaper", "name": "laminas/laminas-escaper",
"version": "2.9.0", "version": "2.10.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laminas/laminas-escaper.git", "url": "https://github.com/laminas/laminas-escaper.git",
"reference": "891ad70986729e20ed2e86355fcf93c9dc238a5f" "reference": "58af67282db37d24e584a837a94ee55b9c7552be"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/891ad70986729e20ed2e86355fcf93c9dc238a5f", "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/58af67282db37d24e584a837a94ee55b9c7552be",
"reference": "891ad70986729e20ed2e86355fcf93c9dc238a5f", "reference": "58af67282db37d24e584a837a94ee55b9c7552be",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.3 || ~8.0.0 || ~8.1.0" "ext-ctype": "*",
"ext-mbstring": "*",
"php": "^7.4 || ~8.0.0 || ~8.1.0"
}, },
"conflict": { "conflict": {
"zendframework/zend-escaper": "*" "zendframework/zend-escaper": "*"
}, },
"require-dev": { "require-dev": {
"infection/infection": "^0.26.6",
"laminas/laminas-coding-standard": "~2.3.0", "laminas/laminas-coding-standard": "~2.3.0",
"phpunit/phpunit": "^9.3", "maglnet/composer-require-checker": "^3.8.0",
"psalm/plugin-phpunit": "^0.12.2", "phpunit/phpunit": "^9.5.18",
"vimeo/psalm": "^3.16" "psalm/plugin-phpunit": "^0.16.1",
}, "vimeo/psalm": "^4.22.0"
"suggest": {
"ext-iconv": "*",
"ext-mbstring": "*"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -233,7 +233,7 @@
"type": "community_bridge" "type": "community_bridge"
} }
], ],
"time": "2021-09-02T17:10:53+00:00" "time": "2022-03-08T20:15:36+00:00"
}, },
{ {
"name": "league/color-extractor", "name": "league/color-extractor",
@ -349,16 +349,16 @@
}, },
{ {
"name": "phpmailer/phpmailer", "name": "phpmailer/phpmailer",
"version": "v6.5.4", "version": "v6.6.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git", "url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "c0d9f7dd3c2aa247ca44791e9209233829d82285" "reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/c0d9f7dd3c2aa247ca44791e9209233829d82285", "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/a94fdebaea6bd17f51be0c2373ab80d3d681269b",
"reference": "c0d9f7dd3c2aa247ca44791e9209233829d82285", "reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -370,8 +370,8 @@
"require-dev": { "require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"doctrine/annotations": "^1.2", "doctrine/annotations": "^1.2",
"php-parallel-lint/php-console-highlighter": "^0.5.0", "php-parallel-lint/php-console-highlighter": "^1.0.0",
"php-parallel-lint/php-parallel-lint": "^1.3.1", "php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcompatibility/php-compatibility": "^9.3.5", "phpcompatibility/php-compatibility": "^9.3.5",
"roave/security-advisories": "dev-latest", "roave/security-advisories": "dev-latest",
"squizlabs/php_codesniffer": "^3.6.2", "squizlabs/php_codesniffer": "^3.6.2",
@ -415,7 +415,7 @@
"description": "PHPMailer is a full-featured email creation and transfer class for PHP", "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": { "support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues", "issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.5.4" "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.4"
}, },
"funding": [ "funding": [
{ {
@ -423,7 +423,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2022-02-17T08:19:04+00:00" "time": "2022-08-22T09:22:00+00:00"
}, },
{ {
"name": "psr/log", "name": "psr/log",
@ -477,16 +477,16 @@
}, },
{ {
"name": "symfony/polyfill-intl-idn", "name": "symfony/polyfill-intl-idn",
"version": "v1.24.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git", "url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "749045c69efb97c70d25d7463abba812e91f3a44" "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/749045c69efb97c70d25d7463abba812e91f3a44", "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/59a8d271f00dd0e4c2e518104cc7963f655a1aa8",
"reference": "749045c69efb97c70d25d7463abba812e91f3a44", "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -500,7 +500,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -544,7 +544,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.24.0" "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -560,20 +560,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-09-14T14:02:44+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-normalizer", "name": "symfony/polyfill-intl-normalizer",
"version": "v1.25.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" "reference": "219aa369ceff116e673852dce47c3a41794c14bd"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd",
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", "reference": "219aa369ceff116e673852dce47c3a41794c14bd",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -585,7 +585,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -628,7 +628,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0" "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -644,20 +644,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-02-19T12:13:01+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.24.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git", "url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -672,7 +672,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -711,7 +711,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.0" "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -727,7 +727,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-11-30T18:21:41+00:00" "time": "2022-05-24T11:49:31+00:00"
} }
], ],
"packages-dev": [], "packages-dev": [],
@ -738,9 +738,21 @@
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": ">=7.4.0 <8.2.0", "php": ">=7.4.0 <8.2.0",
"ext-simplexml": "*",
"ext-ctype": "*", "ext-ctype": "*",
"ext-mbstring": "*" "ext-curl": "*",
"ext-dom": "*",
"ext-filter": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-openssl": "*"
}, },
"platform-dev": [], "platform-dev": [],
"plugin-api-version": "2.1.0" "platform-overrides": {
"php": "7.4.0"
},
"plugin-api-version": "2.3.0"
} }

View file

@ -1,80 +1,81 @@
<?php <?php
return [ return [
// cms classes // cms classes
'collection' => 'Kirby\Cms\Collection', 'collection' => 'Kirby\Cms\Collection',
'field' => 'Kirby\Cms\Field', 'field' => 'Kirby\Cms\Field',
'file' => 'Kirby\Cms\File', 'file' => 'Kirby\Cms\File',
'files' => 'Kirby\Cms\Files', 'files' => 'Kirby\Cms\Files',
'find' => 'Kirby\Cms\Find', 'find' => 'Kirby\Cms\Find',
'html' => 'Kirby\Cms\Html', 'helpers' => 'Kirby\Cms\Helpers',
'kirby' => 'Kirby\Cms\App', 'html' => 'Kirby\Cms\Html',
'page' => 'Kirby\Cms\Page', 'kirby' => 'Kirby\Cms\App',
'pages' => 'Kirby\Cms\Pages', 'page' => 'Kirby\Cms\Page',
'pagination' => 'Kirby\Cms\Pagination', 'pages' => 'Kirby\Cms\Pages',
'r' => 'Kirby\Cms\R', 'pagination' => 'Kirby\Cms\Pagination',
'response' => 'Kirby\Cms\Response', 'r' => 'Kirby\Cms\R',
's' => 'Kirby\Cms\S', 'response' => 'Kirby\Cms\Response',
'sane' => 'Kirby\Sane\Sane', 's' => 'Kirby\Cms\S',
'site' => 'Kirby\Cms\Site', 'sane' => 'Kirby\Sane\Sane',
'structure' => 'Kirby\Cms\Structure', 'site' => 'Kirby\Cms\Site',
'url' => 'Kirby\Cms\Url', 'structure' => 'Kirby\Cms\Structure',
'user' => 'Kirby\Cms\User', 'url' => 'Kirby\Cms\Url',
'users' => 'Kirby\Cms\Users', 'user' => 'Kirby\Cms\User',
'visitor' => 'Kirby\Cms\Visitor', 'users' => 'Kirby\Cms\Users',
'visitor' => 'Kirby\Cms\Visitor',
// data handler // data handler
'data' => 'Kirby\Data\Data', 'data' => 'Kirby\Data\Data',
'json' => 'Kirby\Data\Json', 'json' => 'Kirby\Data\Json',
'yaml' => 'Kirby\Data\Yaml', 'yaml' => 'Kirby\Data\Yaml',
// file classes // file classes
'asset' => 'Kirby\Filesystem\Asset', 'asset' => 'Kirby\Filesystem\Asset',
'dir' => 'Kirby\Filesystem\Dir', 'dir' => 'Kirby\Filesystem\Dir',
'f' => 'Kirby\Filesystem\F', 'f' => 'Kirby\Filesystem\F',
'mime' => 'Kirby\Filesystem\Mime', 'mime' => 'Kirby\Filesystem\Mime',
// data classes // data classes
'database' => 'Kirby\Database\Database', 'database' => 'Kirby\Database\Database',
'db' => 'Kirby\Database\Db', 'db' => 'Kirby\Database\Db',
// exceptions // exceptions
'errorpageexception' => 'Kirby\Exception\ErrorPageException', 'errorpageexception' => 'Kirby\Exception\ErrorPageException',
// http classes // http classes
'cookie' => 'Kirby\Http\Cookie', 'cookie' => 'Kirby\Http\Cookie',
'header' => 'Kirby\Http\Header', 'header' => 'Kirby\Http\Header',
'remote' => 'Kirby\Http\Remote', 'remote' => 'Kirby\Http\Remote',
'server' => 'Kirby\Http\Server', 'server' => 'Kirby\Http\Server',
// image classes // image classes
'dimensions' => 'Kirby\Image\Dimensions', 'dimensions' => 'Kirby\Image\Dimensions',
// panel classes // panel classes
'panel' => 'Kirby\Panel\Panel', 'panel' => 'Kirby\Panel\Panel',
// toolkit classes // toolkit classes
'a' => 'Kirby\Toolkit\A', 'a' => 'Kirby\Toolkit\A',
'c' => 'Kirby\Toolkit\Config', 'c' => 'Kirby\Toolkit\Config',
'config' => 'Kirby\Toolkit\Config', 'config' => 'Kirby\Toolkit\Config',
'escape' => 'Kirby\Toolkit\Escape', 'escape' => 'Kirby\Toolkit\Escape',
'i18n' => 'Kirby\Toolkit\I18n', 'i18n' => 'Kirby\Toolkit\I18n',
'obj' => 'Kirby\Toolkit\Obj', 'obj' => 'Kirby\Toolkit\Obj',
'str' => 'Kirby\Toolkit\Str', 'str' => 'Kirby\Toolkit\Str',
'tpl' => 'Kirby\Toolkit\Tpl', 'tpl' => 'Kirby\Toolkit\Tpl',
'v' => 'Kirby\Toolkit\V', 'v' => 'Kirby\Toolkit\V',
'xml' => 'Kirby\Toolkit\Xml', 'xml' => 'Kirby\Toolkit\Xml',
// TODO: remove in 4.0.0 // TODO: remove in 4.0.0
'kirby\cms\asset' => 'Kirby\Filesystem\Asset', 'kirby\cms\asset' => 'Kirby\Filesystem\Asset',
'kirby\cms\dir' => 'Kirby\Filesystem\Dir', 'kirby\cms\dir' => 'Kirby\Filesystem\Dir',
'kirby\cms\filename' => 'Kirby\Filesystem\Filename', 'kirby\cms\filename' => 'Kirby\Filesystem\Filename',
'kirby\cms\filefoundation' => 'Kirby\Filesystem\IsFile', 'kirby\cms\filefoundation' => 'Kirby\Filesystem\IsFile',
'kirby\cms\form' => 'Kirby\Form\Form', 'kirby\cms\form' => 'Kirby\Form\Form',
'kirby\cms\kirbytag' => 'Kirby\Text\KirbyTag', 'kirby\cms\kirbytag' => 'Kirby\Text\KirbyTag',
'kirby\cms\kirbytags' => 'Kirby\Text\KirbyTags', 'kirby\cms\kirbytags' => 'Kirby\Text\KirbyTags',
'kirby\toolkit\dir' => 'Kirby\Filesystem\Dir', 'kirby\toolkit\dir' => 'Kirby\Filesystem\Dir',
'kirby\toolkit\f' => 'Kirby\Filesystem\F', 'kirby\toolkit\f' => 'Kirby\Filesystem\F',
'kirby\toolkit\file' => 'Kirby\Filesystem\File', 'kirby\toolkit\file' => 'Kirby\Filesystem\File',
'kirby\toolkit\mime' => 'Kirby\Filesystem\Mime', 'kirby\toolkit\mime' => 'Kirby\Filesystem\Mime',
]; ];

View file

@ -3,25 +3,25 @@
use Kirby\Exception\PermissionException; use Kirby\Exception\PermissionException;
return function () { return function () {
$auth = $this->kirby()->auth(); $auth = $this->kirby()->auth();
$allowImpersonation = $this->kirby()->option('api.allowImpersonation') ?? false; $allowImpersonation = $this->kirby()->option('api.allowImpersonation') ?? false;
// csrf token check // csrf token check
if ( if (
$auth->type($allowImpersonation) === 'session' && $auth->type($allowImpersonation) === 'session' &&
$auth->csrf() === false $auth->csrf() === false
) { ) {
throw new PermissionException('Unauthenticated'); throw new PermissionException('Unauthenticated');
} }
// get user from session or basic auth // get user from session or basic auth
if ($user = $auth->user(null, $allowImpersonation)) { if ($user = $auth->user(null, $allowImpersonation)) {
if ($user->role()->permissions()->for('access', 'panel') === false) { if ($user->role()->permissions()->for('access', 'panel') === false) {
throw new PermissionException(['key' => 'access.panel']); throw new PermissionException(['key' => 'access.panel']);
} }
return $user; return $user;
} }
throw new PermissionException('Unauthenticated'); throw new PermissionException('Unauthenticated');
}; };

View file

@ -5,66 +5,66 @@
*/ */
return [ return [
/** /**
* Children * Children
*/ */
'children' => [ 'children' => [
'model' => 'page', 'model' => 'page',
'type' => 'Kirby\Cms\Pages', 'type' => 'Kirby\Cms\Pages',
'view' => 'compact' 'view' => 'compact'
], ],
/** /**
* Files * Files
*/ */
'files' => [ 'files' => [
'model' => 'file', 'model' => 'file',
'type' => 'Kirby\Cms\Files' 'type' => 'Kirby\Cms\Files'
], ],
/** /**
* Languages * Languages
*/ */
'languages' => [ 'languages' => [
'model' => 'language', 'model' => 'language',
'type' => 'Kirby\Cms\Languages' 'type' => 'Kirby\Cms\Languages'
], ],
/** /**
* Pages * Pages
*/ */
'pages' => [ 'pages' => [
'model' => 'page', 'model' => 'page',
'type' => 'Kirby\Cms\Pages', 'type' => 'Kirby\Cms\Pages',
'view' => 'compact' 'view' => 'compact'
], ],
/** /**
* Roles * Roles
*/ */
'roles' => [ 'roles' => [
'model' => 'role', 'model' => 'role',
'type' => 'Kirby\Cms\Roles', 'type' => 'Kirby\Cms\Roles',
'view' => 'compact' 'view' => 'compact'
], ],
/** /**
* Translations * Translations
*/ */
'translations' => [ 'translations' => [
'model' => 'translation', 'model' => 'translation',
'type' => 'Kirby\Cms\Translations', 'type' => 'Kirby\Cms\Translations',
'view' => 'compact' 'view' => 'compact'
], ],
/** /**
* Users * Users
*/ */
'users' => [ 'users' => [
'default' => fn () => $this->users(), 'default' => fn () => $this->users(),
'model' => 'user', 'model' => 'user',
'type' => 'Kirby\Cms\Users', 'type' => 'Kirby\Cms\Users',
'view' => 'compact' 'view' => 'compact'
] ]
]; ];

View file

@ -4,17 +4,17 @@
* Api Model Definitions * Api Model Definitions
*/ */
return [ return [
'File' => include __DIR__ . '/models/File.php', 'File' => include __DIR__ . '/models/File.php',
'FileBlueprint' => include __DIR__ . '/models/FileBlueprint.php', 'FileBlueprint' => include __DIR__ . '/models/FileBlueprint.php',
'FileVersion' => include __DIR__ . '/models/FileVersion.php', 'FileVersion' => include __DIR__ . '/models/FileVersion.php',
'Language' => include __DIR__ . '/models/Language.php', 'Language' => include __DIR__ . '/models/Language.php',
'Page' => include __DIR__ . '/models/Page.php', 'Page' => include __DIR__ . '/models/Page.php',
'PageBlueprint' => include __DIR__ . '/models/PageBlueprint.php', 'PageBlueprint' => include __DIR__ . '/models/PageBlueprint.php',
'Role' => include __DIR__ . '/models/Role.php', 'Role' => include __DIR__ . '/models/Role.php',
'Site' => include __DIR__ . '/models/Site.php', 'Site' => include __DIR__ . '/models/Site.php',
'SiteBlueprint' => include __DIR__ . '/models/SiteBlueprint.php', 'SiteBlueprint' => include __DIR__ . '/models/SiteBlueprint.php',
'System' => include __DIR__ . '/models/System.php', 'System' => include __DIR__ . '/models/System.php',
'Translation' => include __DIR__ . '/models/Translation.php', 'Translation' => include __DIR__ . '/models/Translation.php',
'User' => include __DIR__ . '/models/User.php', 'User' => include __DIR__ . '/models/User.php',
'UserBlueprint' => include __DIR__ . '/models/UserBlueprint.php', 'UserBlueprint' => include __DIR__ . '/models/UserBlueprint.php',
]; ];

View file

@ -7,116 +7,109 @@ use Kirby\Form\Form;
* File * File
*/ */
return [ return [
'fields' => [ 'fields' => [
'blueprint' => fn (File $file) => $file->blueprint(), 'blueprint' => fn (File $file) => $file->blueprint(),
'content' => fn (File $file) => Form::for($file)->values(), 'content' => fn (File $file) => Form::for($file)->values(),
'dimensions' => fn (File $file) => $file->dimensions()->toArray(), 'dimensions' => fn (File $file) => $file->dimensions()->toArray(),
'dragText' => fn (File $file) => $file->panel()->dragText(), 'dragText' => fn (File $file) => $file->panel()->dragText(),
'exists' => fn (File $file) => $file->exists(), 'exists' => fn (File $file) => $file->exists(),
'extension' => fn (File $file) => $file->extension(), 'extension' => fn (File $file) => $file->extension(),
'filename' => fn (File $file) => $file->filename(), 'filename' => fn (File $file) => $file->filename(),
'id' => fn (File $file) => $file->id(), 'id' => fn (File $file) => $file->id(),
'link' => fn (File $file) => $file->panel()->url(true), 'link' => fn (File $file) => $file->panel()->url(true),
'mime' => fn (File $file) => $file->mime(), 'mime' => fn (File $file) => $file->mime(),
'modified' => fn (File $file) => $file->modified('c'), 'modified' => fn (File $file) => $file->modified('c'),
'name' => fn (File $file) => $file->name(), 'name' => fn (File $file) => $file->name(),
'next' => fn (File $file) => $file->next(), 'next' => fn (File $file) => $file->next(),
'nextWithTemplate' => function (File $file) { 'nextWithTemplate' => function (File $file) {
$files = $file->templateSiblings()->sorted(); $files = $file->templateSiblings()->sorted();
$index = $files->indexOf($file); $index = $files->indexOf($file);
return $files->nth($index + 1); return $files->nth($index + 1);
}, },
'niceSize' => fn (File $file) => $file->niceSize(), 'niceSize' => fn (File $file) => $file->niceSize(),
'options' => fn (File $file) => $file->panel()->options(), 'options' => fn (File $file) => $file->panel()->options(),
'panelIcon' => function (File $file) { 'panelImage' => fn (File $file) => $file->panel()->image(),
// TODO: remove in 3.7.0 'panelUrl' => fn (File $file) => $file->panel()->url(true),
// @codeCoverageIgnoreStart 'prev' => fn (File $file) => $file->prev(),
deprecated('The API field file.panelIcon has been deprecated and will be removed in 3.7.0. Use file.panelImage instead'); 'prevWithTemplate' => function (File $file) {
return $file->panel()->image(); $files = $file->templateSiblings()->sorted();
// @codeCoverageIgnoreEnd $index = $files->indexOf($file);
},
'panelImage' => fn (File $file) => $file->panel()->image(),
'panelUrl' => fn (File $file) => $file->panel()->url(true),
'prev' => fn (File $file) => $file->prev(),
'prevWithTemplate' => function (File $file) {
$files = $file->templateSiblings()->sorted();
$index = $files->indexOf($file);
return $files->nth($index - 1); return $files->nth($index - 1);
}, },
'parent' => fn (File $file) => $file->parent(), 'parent' => fn (File $file) => $file->parent(),
'parents' => fn (File $file) => $file->parents()->flip(), 'parents' => fn (File $file) => $file->parents()->flip(),
'size' => fn (File $file) => $file->size(), 'size' => fn (File $file) => $file->size(),
'template' => fn (File $file) => $file->template(), 'template' => fn (File $file) => $file->template(),
'thumbs' => function ($file) { 'thumbs' => function ($file) {
if ($file->isResizable() === false) { if ($file->isResizable() === false) {
return null; return null;
} }
return [ return [
'tiny' => $file->resize(128)->url(), 'tiny' => $file->resize(128)->url(),
'small' => $file->resize(256)->url(), 'small' => $file->resize(256)->url(),
'medium' => $file->resize(512)->url(), 'medium' => $file->resize(512)->url(),
'large' => $file->resize(768)->url(), 'large' => $file->resize(768)->url(),
'huge' => $file->resize(1024)->url(), 'huge' => $file->resize(1024)->url(),
]; ];
}, },
'type' => fn (File $file) => $file->type(), 'type' => fn (File $file) => $file->type(),
'url' => fn (File $file) => $file->url(), 'url' => fn (File $file) => $file->url(),
], ],
'type' => 'Kirby\Cms\File', 'type' => 'Kirby\Cms\File',
'views' => [ 'views' => [
'default' => [ 'default' => [
'content', 'content',
'dimensions', 'dimensions',
'exists', 'exists',
'extension', 'extension',
'filename', 'filename',
'id', 'id',
'link', 'link',
'mime', 'mime',
'modified', 'modified',
'name', 'name',
'next' => 'compact', 'next' => 'compact',
'niceSize', 'niceSize',
'parent' => 'compact', 'parent' => 'compact',
'options', 'options',
'prev' => 'compact', 'prev' => 'compact',
'size', 'size',
'template', 'template',
'type', 'type',
'url' 'url'
], ],
'compact' => [ 'compact' => [
'filename', 'filename',
'id', 'id',
'link', 'link',
'type', 'type',
'url', 'url',
], ],
'panel' => [ 'panel' => [
'blueprint', 'blueprint',
'content', 'content',
'dimensions', 'dimensions',
'extension', 'extension',
'filename', 'filename',
'id', 'id',
'link', 'link',
'mime', 'mime',
'modified', 'modified',
'name', 'name',
'nextWithTemplate' => 'compact', 'nextWithTemplate' => 'compact',
'niceSize', 'niceSize',
'options', 'options',
'panelIcon', 'panelIcon',
'panelImage', 'panelImage',
'parent' => 'compact', 'parent' => 'compact',
'parents' => ['id', 'slug', 'title'], 'parents' => ['id', 'slug', 'title'],
'prevWithTemplate' => 'compact', 'prevWithTemplate' => 'compact',
'template', 'template',
'type', 'type',
'url' 'url'
] ]
], ],
]; ];

View file

@ -6,13 +6,13 @@ use Kirby\Cms\FileBlueprint;
* FileBlueprint * FileBlueprint
*/ */
return [ return [
'fields' => [ 'fields' => [
'name' => fn (FileBlueprint $blueprint) => $blueprint->name(), 'name' => fn (FileBlueprint $blueprint) => $blueprint->name(),
'options' => fn (FileBlueprint $blueprint) => $blueprint->options(), 'options' => fn (FileBlueprint $blueprint) => $blueprint->options(),
'tabs' => fn (FileBlueprint $blueprint) => $blueprint->tabs(), 'tabs' => fn (FileBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (FileBlueprint $blueprint) => $blueprint->title(), 'title' => fn (FileBlueprint $blueprint) => $blueprint->title(),
], ],
'type' => 'Kirby\Cms\FileBlueprint', 'type' => 'Kirby\Cms\FileBlueprint',
'views' => [ 'views' => [
], ],
]; ];

View file

@ -6,54 +6,54 @@ use Kirby\Cms\FileVersion;
* FileVersion * FileVersion
*/ */
return [ return [
'fields' => [ 'fields' => [
'dimensions' => fn (FileVersion $file) => $file->dimensions()->toArray(), 'dimensions' => fn (FileVersion $file) => $file->dimensions()->toArray(),
'exists' => fn (FileVersion $file) => $file->exists(), 'exists' => fn (FileVersion $file) => $file->exists(),
'extension' => fn (FileVersion $file) => $file->extension(), 'extension' => fn (FileVersion $file) => $file->extension(),
'filename' => fn (FileVersion $file) => $file->filename(), 'filename' => fn (FileVersion $file) => $file->filename(),
'id' => fn (FileVersion $file) => $file->id(), 'id' => fn (FileVersion $file) => $file->id(),
'mime' => fn (FileVersion $file) => $file->mime(), 'mime' => fn (FileVersion $file) => $file->mime(),
'modified' => fn (FileVersion $file) => $file->modified('c'), 'modified' => fn (FileVersion $file) => $file->modified('c'),
'name' => fn (FileVersion $file) => $file->name(), 'name' => fn (FileVersion $file) => $file->name(),
'niceSize' => fn (FileVersion $file) => $file->niceSize(), 'niceSize' => fn (FileVersion $file) => $file->niceSize(),
'size' => fn (FileVersion $file) => $file->size(), 'size' => fn (FileVersion $file) => $file->size(),
'type' => fn (FileVersion $file) => $file->type(), 'type' => fn (FileVersion $file) => $file->type(),
'url' => fn (FileVersion $file) => $file->url(), 'url' => fn (FileVersion $file) => $file->url(),
], ],
'type' => 'Kirby\Cms\FileVersion', 'type' => 'Kirby\Cms\FileVersion',
'views' => [ 'views' => [
'default' => [ 'default' => [
'dimensions', 'dimensions',
'exists', 'exists',
'extension', 'extension',
'filename', 'filename',
'id', 'id',
'mime', 'mime',
'modified', 'modified',
'name', 'name',
'niceSize', 'niceSize',
'size', 'size',
'type', 'type',
'url' 'url'
], ],
'compact' => [ 'compact' => [
'filename', 'filename',
'id', 'id',
'type', 'type',
'url', 'url',
], ],
'panel' => [ 'panel' => [
'dimensions', 'dimensions',
'extension', 'extension',
'filename', 'filename',
'id', 'id',
'mime', 'mime',
'modified', 'modified',
'name', 'name',
'niceSize', 'niceSize',
'template', 'template',
'type', 'type',
'url' 'url'
] ]
], ],
]; ];

View file

@ -6,25 +6,25 @@ use Kirby\Cms\Language;
* Language * Language
*/ */
return [ return [
'fields' => [ 'fields' => [
'code' => fn (Language $language) => $language->code(), 'code' => fn (Language $language) => $language->code(),
'default' => fn (Language $language) => $language->isDefault(), 'default' => fn (Language $language) => $language->isDefault(),
'direction' => fn (Language $language) => $language->direction(), 'direction' => fn (Language $language) => $language->direction(),
'locale' => fn (Language $language) => $language->locale(), 'locale' => fn (Language $language) => $language->locale(),
'name' => fn (Language $language) => $language->name(), 'name' => fn (Language $language) => $language->name(),
'rules' => fn (Language $language) => $language->rules(), 'rules' => fn (Language $language) => $language->rules(),
'url' => fn (Language $language) => $language->url(), 'url' => fn (Language $language) => $language->url(),
], ],
'type' => 'Kirby\Cms\Language', 'type' => 'Kirby\Cms\Language',
'views' => [ 'views' => [
'default' => [ 'default' => [
'code', 'code',
'default', 'default',
'direction', 'direction',
'locale', 'locale',
'name', 'name',
'rules', 'rules',
'url' 'url'
] ]
] ]
]; ];

View file

@ -1,5 +1,6 @@
<?php <?php
use Kirby\Cms\Helpers;
use Kirby\Cms\Page; use Kirby\Cms\Page;
use Kirby\Form\Form; use Kirby\Form\Form;
@ -7,122 +8,116 @@ use Kirby\Form\Form;
* Page * Page
*/ */
return [ return [
'fields' => [ 'fields' => [
'blueprint' => fn (Page $page) => $page->blueprint(), 'blueprint' => fn (Page $page) => $page->blueprint(),
'blueprints' => fn (Page $page) => $page->blueprints(), 'blueprints' => fn (Page $page) => $page->blueprints(),
'children' => fn (Page $page) => $page->children(), 'children' => fn (Page $page) => $page->children(),
'content' => fn (Page $page) => Form::for($page)->values(), 'content' => fn (Page $page) => Form::for($page)->values(),
'drafts' => fn (Page $page) => $page->drafts(), 'drafts' => fn (Page $page) => $page->drafts(),
'errors' => fn (Page $page) => $page->errors(), 'errors' => fn (Page $page) => $page->errors(),
'files' => fn (Page $page) => $page->files()->sorted(), 'files' => fn (Page $page) => $page->files()->sorted(),
'hasChildren' => fn (Page $page) => $page->hasChildren(), 'hasChildren' => fn (Page $page) => $page->hasChildren(),
'hasDrafts' => fn (Page $page) => $page->hasDrafts(), 'hasDrafts' => fn (Page $page) => $page->hasDrafts(),
'hasFiles' => fn (Page $page) => $page->hasFiles(), 'hasFiles' => fn (Page $page) => $page->hasFiles(),
'id' => fn (Page $page) => $page->id(), 'id' => fn (Page $page) => $page->id(),
'isSortable' => fn (Page $page) => $page->isSortable(), 'isSortable' => fn (Page $page) => $page->isSortable(),
/** /**
* @deprecated 3.6.0 * @deprecated 3.6.0
* @todo Throw deprecated warning in 3.7.0 * @todo Remove in 3.8.0
* @todo Remove in 3.8.0 * @codeCoverageIgnore
* @codeCoverageIgnore */
*/ 'next' => function (Page $page) {
'next' => function (Page $page) { Helpers::deprecated('The API field page.next has been deprecated and will be removed in 3.8.0.');
return $page
->nextAll() return $page
->filter('intendedTemplate', $page->intendedTemplate()) ->nextAll()
->filter('status', $page->status()) ->filter('intendedTemplate', $page->intendedTemplate())
->filter('isReadable', true) ->filter('status', $page->status())
->first(); ->filter('isReadable', true)
}, ->first();
'num' => fn (Page $page) => $page->num(), },
'options' => fn (Page $page) => $page->panel()->options(['preview']), 'num' => fn (Page $page) => $page->num(),
/** 'options' => fn (Page $page) => $page->panel()->options(['preview']),
* @todo Remove in 3.7.0 'panelImage' => fn (Page $page) => $page->panel()->image(),
* @codeCoverageIgnore 'parent' => fn (Page $page) => $page->parent(),
*/ 'parents' => fn (Page $page) => $page->parents()->flip(),
'panelIcon' => function (Page $page) { /**
deprecated('The API field page.panelIcon has been deprecated and will be removed in 3.7.0. Use page.panelImage instead'); * @deprecated 3.6.0
return $page->panel()->image(); * @todo Remove in 3.8.0
}, * @codeCoverageIgnore
'panelImage' => fn (Page $page) => $page->panel()->image(), */
'parent' => fn (Page $page) => $page->parent(), 'prev' => function (Page $page) {
'parents' => fn (Page $page) => $page->parents()->flip(), Helpers::deprecated('The API field page.prev has been deprecated and will be removed in 3.8.0.');
/**
* @deprecated 3.6.0 return $page
* @todo Throw deprecated warning in 3.7.0 ->prevAll()
* @todo Remove in 3.8.0 ->filter('intendedTemplate', $page->intendedTemplate())
* @codeCoverageIgnore ->filter('status', $page->status())
*/ ->filter('isReadable', true)
'prev' => function (Page $page) { ->last();
return $page },
->prevAll() 'previewUrl' => fn (Page $page) => $page->previewUrl(),
->filter('intendedTemplate', $page->intendedTemplate()) 'siblings' => function (Page $page) {
->filter('status', $page->status()) if ($page->isDraft() === true) {
->filter('isReadable', true) return $page->parentModel()->children()->not($page);
->last(); } else {
}, return $page->siblings();
'previewUrl' => fn (Page $page) => $page->previewUrl(), }
'siblings' => function (Page $page) { },
if ($page->isDraft() === true) { 'slug' => fn (Page $page) => $page->slug(),
return $page->parentModel()->children()->not($page); 'status' => fn (Page $page) => $page->status(),
} else { 'template' => fn (Page $page) => $page->intendedTemplate()->name(),
return $page->siblings(); 'title' => fn (Page $page) => $page->title()->value(),
} 'url' => fn (Page $page) => $page->url(),
}, ],
'slug' => fn (Page $page) => $page->slug(), 'type' => 'Kirby\Cms\Page',
'status' => fn (Page $page) => $page->status(), 'views' => [
'template' => fn (Page $page) => $page->intendedTemplate()->name(), 'compact' => [
'title' => fn (Page $page) => $page->title()->value(), 'id',
'url' => fn (Page $page) => $page->url(), 'title',
], 'url',
'type' => 'Kirby\Cms\Page', 'num'
'views' => [ ],
'compact' => [ 'default' => [
'id', 'content',
'title', 'id',
'url', 'status',
'num' 'num',
], 'options',
'default' => [ 'parent' => 'compact',
'content', 'slug',
'id', 'template',
'status', 'title',
'num', 'url'
'options', ],
'parent' => 'compact', 'panel' => [
'slug', 'id',
'template', 'blueprint',
'title', 'content',
'url' 'status',
], 'options',
'panel' => [ 'next' => ['id', 'slug', 'title'],
'id', 'parents' => ['id', 'slug', 'title'],
'blueprint', 'prev' => ['id', 'slug', 'title'],
'content', 'previewUrl',
'status', 'slug',
'options', 'title',
'next' => ['id', 'slug', 'title'], 'url'
'parents' => ['id', 'slug', 'title'], ],
'prev' => ['id', 'slug', 'title'], 'selector' => [
'previewUrl', 'id',
'slug', 'title',
'title', 'parent' => [
'url' 'id',
], 'title'
'selector' => [ ],
'id', 'children' => [
'title', 'hasChildren',
'parent' => [ 'id',
'id', 'panelIcon',
'title' 'panelImage',
], 'title',
'children' => [ ],
'hasChildren', ]
'id', ],
'panelIcon',
'panelImage',
'title',
],
]
],
]; ];

View file

@ -6,16 +6,16 @@ use Kirby\Cms\PageBlueprint;
* PageBlueprint * PageBlueprint
*/ */
return [ return [
'fields' => [ 'fields' => [
'name' => fn (PageBlueprint $blueprint) => $blueprint->name(), 'name' => fn (PageBlueprint $blueprint) => $blueprint->name(),
'num' => fn (PageBlueprint $blueprint) => $blueprint->num(), 'num' => fn (PageBlueprint $blueprint) => $blueprint->num(),
'options' => fn (PageBlueprint $blueprint) => $blueprint->options(), 'options' => fn (PageBlueprint $blueprint) => $blueprint->options(),
'preview' => fn (PageBlueprint $blueprint) => $blueprint->preview(), 'preview' => fn (PageBlueprint $blueprint) => $blueprint->preview(),
'status' => fn (PageBlueprint $blueprint) => $blueprint->status(), 'status' => fn (PageBlueprint $blueprint) => $blueprint->status(),
'tabs' => fn (PageBlueprint $blueprint) => $blueprint->tabs(), 'tabs' => fn (PageBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (PageBlueprint $blueprint) => $blueprint->title(), 'title' => fn (PageBlueprint $blueprint) => $blueprint->title(),
], ],
'type' => 'Kirby\Cms\PageBlueprint', 'type' => 'Kirby\Cms\PageBlueprint',
'views' => [ 'views' => [
], ],
]; ];

View file

@ -6,18 +6,18 @@ use Kirby\Cms\Role;
* Role * Role
*/ */
return [ return [
'fields' => [ 'fields' => [
'description' => fn (Role $role) => $role->description(), 'description' => fn (Role $role) => $role->description(),
'name' => fn (Role $role) => $role->name(), 'name' => fn (Role $role) => $role->name(),
'permissions' => fn (Role $role) => $role->permissions()->toArray(), 'permissions' => fn (Role $role) => $role->permissions()->toArray(),
'title' => fn (Role $role) => $role->title(), 'title' => fn (Role $role) => $role->title(),
], ],
'type' => 'Kirby\Cms\Role', 'type' => 'Kirby\Cms\Role',
'views' => [ 'views' => [
'compact' => [ 'compact' => [
'description', 'description',
'name', 'name',
'title' 'title'
] ]
] ]
]; ];

View file

@ -7,46 +7,46 @@ use Kirby\Form\Form;
* Site * Site
*/ */
return [ return [
'default' => fn () => $this->site(), 'default' => fn () => $this->site(),
'fields' => [ 'fields' => [
'blueprint' => fn (Site $site) => $site->blueprint(), 'blueprint' => fn (Site $site) => $site->blueprint(),
'children' => fn (Site $site) => $site->children(), 'children' => fn (Site $site) => $site->children(),
'content' => fn (Site $site) => Form::for($site)->values(), 'content' => fn (Site $site) => Form::for($site)->values(),
'drafts' => fn (Site $site) => $site->drafts(), 'drafts' => fn (Site $site) => $site->drafts(),
'files' => fn (Site $site) => $site->files()->sorted(), 'files' => fn (Site $site) => $site->files()->sorted(),
'options' => fn (Site $site) => $site->permissions()->toArray(), 'options' => fn (Site $site) => $site->permissions()->toArray(),
'previewUrl' => fn (Site $site) => $site->previewUrl(), 'previewUrl' => fn (Site $site) => $site->previewUrl(),
'title' => fn (Site $site) => $site->title()->value(), 'title' => fn (Site $site) => $site->title()->value(),
'url' => fn (Site $site) => $site->url(), 'url' => fn (Site $site) => $site->url(),
], ],
'type' => 'Kirby\Cms\Site', 'type' => 'Kirby\Cms\Site',
'views' => [ 'views' => [
'compact' => [ 'compact' => [
'title', 'title',
'url' 'url'
], ],
'default' => [ 'default' => [
'content', 'content',
'options', 'options',
'title', 'title',
'url' 'url'
], ],
'panel' => [ 'panel' => [
'title', 'title',
'blueprint', 'blueprint',
'content', 'content',
'options', 'options',
'previewUrl', 'previewUrl',
'url' 'url'
], ],
'selector' => [ 'selector' => [
'title', 'title',
'children' => [ 'children' => [
'id', 'id',
'title', 'title',
'panelIcon', 'panelIcon',
'hasChildren' 'hasChildren'
], ],
] ]
] ]
]; ];

View file

@ -6,12 +6,12 @@ use Kirby\Cms\SiteBlueprint;
* SiteBlueprint * SiteBlueprint
*/ */
return [ return [
'fields' => [ 'fields' => [
'name' => fn (SiteBlueprint $blueprint) => $blueprint->name(), 'name' => fn (SiteBlueprint $blueprint) => $blueprint->name(),
'options' => fn (SiteBlueprint $blueprint) => $blueprint->options(), 'options' => fn (SiteBlueprint $blueprint) => $blueprint->options(),
'tabs' => fn (SiteBlueprint $blueprint) => $blueprint->tabs(), 'tabs' => fn (SiteBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (SiteBlueprint $blueprint) => $blueprint->title(), 'title' => fn (SiteBlueprint $blueprint) => $blueprint->title(),
], ],
'type' => 'Kirby\Cms\SiteBlueprint', 'type' => 'Kirby\Cms\SiteBlueprint',
'views' => [], 'views' => [],
]; ];

View file

@ -7,92 +7,92 @@ use Kirby\Toolkit\Str;
* System * System
*/ */
return [ return [
'fields' => [ 'fields' => [
'ascii' => fn () => Str::$ascii, 'ascii' => fn () => Str::$ascii,
'authStatus' => fn () => $this->kirby()->auth()->status()->toArray(), 'authStatus' => fn () => $this->kirby()->auth()->status()->toArray(),
'defaultLanguage' => fn () => $this->kirby()->panelLanguage(), 'defaultLanguage' => fn () => $this->kirby()->panelLanguage(),
'isOk' => fn (System $system) => $system->isOk(), 'isOk' => fn (System $system) => $system->isOk(),
'isInstallable' => fn (System $system) => $system->isInstallable(), 'isInstallable' => fn (System $system) => $system->isInstallable(),
'isInstalled' => fn (System $system) => $system->isInstalled(), 'isInstalled' => fn (System $system) => $system->isInstalled(),
'isLocal' => fn (System $system) => $system->isLocal(), 'isLocal' => fn (System $system) => $system->isLocal(),
'multilang' => fn () => $this->kirby()->option('languages', false) !== false, 'multilang' => fn () => $this->kirby()->option('languages', false) !== false,
'languages' => fn () => $this->kirby()->languages(), 'languages' => fn () => $this->kirby()->languages(),
'license' => fn (System $system) => $system->license(), 'license' => fn (System $system) => $system->license(),
'locales' => function () { 'locales' => function () {
$locales = []; $locales = [];
$translations = $this->kirby()->translations(); $translations = $this->kirby()->translations();
foreach ($translations as $translation) { foreach ($translations as $translation) {
$locales[$translation->code()] = $translation->locale(); $locales[$translation->code()] = $translation->locale();
} }
return $locales; return $locales;
}, },
'loginMethods' => fn (System $system) => array_keys($system->loginMethods()), 'loginMethods' => fn (System $system) => array_keys($system->loginMethods()),
'requirements' => fn (System $system) => $system->toArray(), 'requirements' => fn (System $system) => $system->toArray(),
'site' => fn (System $system) => $system->title(), 'site' => fn (System $system) => $system->title(),
'slugs' => fn () => Str::$language, 'slugs' => fn () => Str::$language,
'title' => fn () => $this->site()->title()->value(), 'title' => fn () => $this->site()->title()->value(),
'translation' => function () { 'translation' => function () {
if ($user = $this->user()) { if ($user = $this->user()) {
$translationCode = $user->language(); $translationCode = $user->language();
} else { } else {
$translationCode = $this->kirby()->panelLanguage(); $translationCode = $this->kirby()->panelLanguage();
} }
if ($translation = $this->kirby()->translation($translationCode)) { if ($translation = $this->kirby()->translation($translationCode)) {
return $translation; return $translation;
} else { } else {
return $this->kirby()->translation('en'); return $this->kirby()->translation('en');
} }
}, },
'kirbytext' => fn () => $this->kirby()->option('panel.kirbytext') ?? true, 'kirbytext' => fn () => $this->kirby()->option('panel.kirbytext') ?? true,
'user' => fn () => $this->user(), 'user' => fn () => $this->user(),
'version' => function () { 'version' => function () {
$user = $this->user(); $user = $this->user();
if ($user && $user->role()->permissions()->for('access', 'system') === true) { if ($user && $user->role()->permissions()->for('access', 'system') === true) {
return $this->kirby()->version(); return $this->kirby()->version();
} else { } else {
return null; return null;
} }
} }
], ],
'type' => 'Kirby\Cms\System', 'type' => 'Kirby\Cms\System',
'views' => [ 'views' => [
'login' => [ 'login' => [
'authStatus', 'authStatus',
'isOk', 'isOk',
'isInstallable', 'isInstallable',
'isInstalled', 'isInstalled',
'loginMethods', 'loginMethods',
'title', 'title',
'translation' 'translation'
], ],
'troubleshooting' => [ 'troubleshooting' => [
'isOk', 'isOk',
'isInstallable', 'isInstallable',
'isInstalled', 'isInstalled',
'title', 'title',
'translation', 'translation',
'requirements' 'requirements'
], ],
'panel' => [ 'panel' => [
'ascii', 'ascii',
'defaultLanguage', 'defaultLanguage',
'isOk', 'isOk',
'isInstalled', 'isInstalled',
'isLocal', 'isLocal',
'kirbytext', 'kirbytext',
'languages', 'languages',
'license', 'license',
'locales', 'locales',
'multilang', 'multilang',
'requirements', 'requirements',
'site', 'site',
'slugs', 'slugs',
'title', 'title',
'translation', 'translation',
'user' => 'auth', 'user' => 'auth',
'version' 'version'
] ]
], ],
]; ];

View file

@ -6,19 +6,19 @@ use Kirby\Cms\Translation;
* Translation * Translation
*/ */
return [ return [
'fields' => [ 'fields' => [
'author' => fn (Translation $translation) => $translation->author(), 'author' => fn (Translation $translation) => $translation->author(),
'data' => fn (Translation $translation) => $translation->dataWithFallback(), 'data' => fn (Translation $translation) => $translation->dataWithFallback(),
'direction' => fn (Translation $translation) => $translation->direction(), 'direction' => fn (Translation $translation) => $translation->direction(),
'id' => fn (Translation $translation) => $translation->id(), 'id' => fn (Translation $translation) => $translation->id(),
'name' => fn (Translation $translation) => $translation->name(), 'name' => fn (Translation $translation) => $translation->name(),
], ],
'type' => 'Kirby\Cms\Translation', 'type' => 'Kirby\Cms\Translation',
'views' => [ 'views' => [
'compact' => [ 'compact' => [
'direction', 'direction',
'id', 'id',
'name' 'name'
] ]
] ]
]; ];

View file

@ -7,71 +7,71 @@ use Kirby\Form\Form;
* User * User
*/ */
return [ return [
'default' => fn () => $this->user(), 'default' => fn () => $this->user(),
'fields' => [ 'fields' => [
'avatar' => fn (User $user) => $user->avatar() ? $user->avatar()->crop(512) : null, 'avatar' => fn (User $user) => $user->avatar() ? $user->avatar()->crop(512) : null,
'blueprint' => fn (User $user) => $user->blueprint(), 'blueprint' => fn (User $user) => $user->blueprint(),
'content' => fn (User $user) => Form::for($user)->values(), 'content' => fn (User $user) => Form::for($user)->values(),
'email' => fn (User $user) => $user->email(), 'email' => fn (User $user) => $user->email(),
'files' => fn (User $user) => $user->files()->sorted(), 'files' => fn (User $user) => $user->files()->sorted(),
'id' => fn (User $user) => $user->id(), 'id' => fn (User $user) => $user->id(),
'language' => fn (User $user) => $user->language(), 'language' => fn (User $user) => $user->language(),
'name' => fn (User $user) => $user->name()->value(), 'name' => fn (User $user) => $user->name()->value(),
'next' => fn (User $user) => $user->next(), 'next' => fn (User $user) => $user->next(),
'options' => fn (User $user) => $user->panel()->options(), 'options' => fn (User $user) => $user->panel()->options(),
'panelImage' => fn (User $user) => $user->panel()->image(), 'panelImage' => fn (User $user) => $user->panel()->image(),
'permissions' => fn (User $user) => $user->role()->permissions()->toArray(), 'permissions' => fn (User $user) => $user->role()->permissions()->toArray(),
'prev' => fn (User $user) => $user->prev(), 'prev' => fn (User $user) => $user->prev(),
'role' => fn (User $user) => $user->role(), 'role' => fn (User $user) => $user->role(),
'roles' => fn (User $user) => $user->roles(), 'roles' => fn (User $user) => $user->roles(),
'username' => fn (User $user) => $user->username() 'username' => fn (User $user) => $user->username()
], ],
'type' => 'Kirby\Cms\User', 'type' => 'Kirby\Cms\User',
'views' => [ 'views' => [
'default' => [ 'default' => [
'avatar', 'avatar',
'content', 'content',
'email', 'email',
'id', 'id',
'language', 'language',
'name', 'name',
'next' => 'compact', 'next' => 'compact',
'options', 'options',
'prev' => 'compact', 'prev' => 'compact',
'role', 'role',
'username' 'username'
], ],
'compact' => [ 'compact' => [
'avatar' => 'compact', 'avatar' => 'compact',
'id', 'id',
'email', 'email',
'language', 'language',
'name', 'name',
'role' => 'compact', 'role' => 'compact',
'username' 'username'
], ],
'auth' => [ 'auth' => [
'avatar' => 'compact', 'avatar' => 'compact',
'permissions', 'permissions',
'email', 'email',
'id', 'id',
'name', 'name',
'role', 'role',
'language' 'language'
], ],
'panel' => [ 'panel' => [
'avatar' => 'compact', 'avatar' => 'compact',
'blueprint', 'blueprint',
'content', 'content',
'email', 'email',
'id', 'id',
'language', 'language',
'name', 'name',
'next' => ['id', 'name'], 'next' => ['id', 'name'],
'options', 'options',
'prev' => ['id', 'name'], 'prev' => ['id', 'name'],
'role', 'role',
'username', 'username',
], ],
] ]
]; ];

View file

@ -6,13 +6,13 @@ use Kirby\Cms\UserBlueprint;
* UserBlueprint * UserBlueprint
*/ */
return [ return [
'fields' => [ 'fields' => [
'name' => fn (UserBlueprint $blueprint) => $blueprint->name(), 'name' => fn (UserBlueprint $blueprint) => $blueprint->name(),
'options' => fn (UserBlueprint $blueprint) => $blueprint->options(), 'options' => fn (UserBlueprint $blueprint) => $blueprint->options(),
'tabs' => fn (UserBlueprint $blueprint) => $blueprint->tabs(), 'tabs' => fn (UserBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (UserBlueprint $blueprint) => $blueprint->title(), 'title' => fn (UserBlueprint $blueprint) => $blueprint->title(),
], ],
'type' => 'Kirby\Cms\UserBlueprint', 'type' => 'Kirby\Cms\UserBlueprint',
'views' => [ 'views' => [
], ],
]; ];

View file

@ -4,23 +4,23 @@
* Api Routes Definitions * Api Routes Definitions
*/ */
return function ($kirby) { return function ($kirby) {
$routes = array_merge( $routes = array_merge(
include __DIR__ . '/routes/auth.php', include __DIR__ . '/routes/auth.php',
include __DIR__ . '/routes/pages.php', include __DIR__ . '/routes/pages.php',
include __DIR__ . '/routes/roles.php', include __DIR__ . '/routes/roles.php',
include __DIR__ . '/routes/site.php', include __DIR__ . '/routes/site.php',
include __DIR__ . '/routes/users.php', include __DIR__ . '/routes/users.php',
include __DIR__ . '/routes/files.php', include __DIR__ . '/routes/files.php',
include __DIR__ . '/routes/lock.php', include __DIR__ . '/routes/lock.php',
include __DIR__ . '/routes/system.php', include __DIR__ . '/routes/system.php',
include __DIR__ . '/routes/translations.php' include __DIR__ . '/routes/translations.php'
); );
// only add the language routes if the // only add the language routes if the
// multi language setup is activated // multi language setup is activated
if ($kirby->option('languages', false) !== false) { if ($kirby->option('languages', false) !== false) {
$routes = array_merge($routes, include __DIR__ . '/routes/languages.php'); $routes = array_merge($routes, include __DIR__ . '/routes/languages.php');
} }
return $routes; return $routes;
}; };

View file

@ -7,102 +7,102 @@ use Kirby\Exception\NotFoundException;
* Authentication * Authentication
*/ */
return [ return [
[ [
'pattern' => 'auth', 'pattern' => 'auth',
'method' => 'GET', 'method' => 'GET',
'action' => function () { 'action' => function () {
if ($user = $this->kirby()->auth()->user()) { if ($user = $this->kirby()->auth()->user()) {
return $this->resolve($user)->view('auth'); return $this->resolve($user)->view('auth');
} }
throw new NotFoundException('The user cannot be found'); throw new NotFoundException('The user cannot be found');
} }
], ],
[ [
'pattern' => 'auth/code', 'pattern' => 'auth/code',
'method' => 'POST', 'method' => 'POST',
'auth' => false, 'auth' => false,
'action' => function () { 'action' => function () {
$auth = $this->kirby()->auth(); $auth = $this->kirby()->auth();
// csrf token check // csrf token check
if ($auth->type() === 'session' && $auth->csrf() === false) { if ($auth->type() === 'session' && $auth->csrf() === false) {
throw new InvalidArgumentException('Invalid CSRF token'); throw new InvalidArgumentException('Invalid CSRF token');
} }
$user = $auth->verifyChallenge($this->requestBody('code')); $user = $auth->verifyChallenge($this->requestBody('code'));
return [ return [
'code' => 200, 'code' => 200,
'status' => 'ok', 'status' => 'ok',
'user' => $this->resolve($user)->view('auth')->toArray() 'user' => $this->resolve($user)->view('auth')->toArray()
]; ];
} }
], ],
[ [
'pattern' => 'auth/login', 'pattern' => 'auth/login',
'method' => 'POST', 'method' => 'POST',
'auth' => false, 'auth' => false,
'action' => function () { 'action' => function () {
$auth = $this->kirby()->auth(); $auth = $this->kirby()->auth();
$methods = $this->kirby()->system()->loginMethods(); $methods = $this->kirby()->system()->loginMethods();
// csrf token check // csrf token check
if ($auth->type() === 'session' && $auth->csrf() === false) { if ($auth->type() === 'session' && $auth->csrf() === false) {
throw new InvalidArgumentException('Invalid CSRF token'); throw new InvalidArgumentException('Invalid CSRF token');
} }
$email = $this->requestBody('email'); $email = $this->requestBody('email');
$long = $this->requestBody('long'); $long = $this->requestBody('long');
$password = $this->requestBody('password'); $password = $this->requestBody('password');
if ($password) { if ($password) {
if (isset($methods['password']) !== true) { if (isset($methods['password']) !== true) {
throw new InvalidArgumentException('Login with password is not enabled'); throw new InvalidArgumentException('Login with password is not enabled');
} }
if ( if (
isset($methods['password']['2fa']) === true && isset($methods['password']['2fa']) === true &&
$methods['password']['2fa'] === true $methods['password']['2fa'] === true
) { ) {
$status = $auth->login2fa($email, $password, $long); $status = $auth->login2fa($email, $password, $long);
} else { } else {
$user = $auth->login($email, $password, $long); $user = $auth->login($email, $password, $long);
} }
} else { } else {
if (isset($methods['code']) === true) { if (isset($methods['code']) === true) {
$mode = 'login'; $mode = 'login';
} elseif (isset($methods['password-reset']) === true) { } elseif (isset($methods['password-reset']) === true) {
$mode = 'password-reset'; $mode = 'password-reset';
} else { } else {
throw new InvalidArgumentException('Login without password is not enabled'); throw new InvalidArgumentException('Login without password is not enabled');
} }
$status = $auth->createChallenge($email, $long, $mode); $status = $auth->createChallenge($email, $long, $mode);
} }
if (isset($user)) { if (isset($user)) {
return [ return [
'code' => 200, 'code' => 200,
'status' => 'ok', 'status' => 'ok',
'user' => $this->resolve($user)->view('auth')->toArray() 'user' => $this->resolve($user)->view('auth')->toArray()
]; ];
} else { } else {
return [ return [
'code' => 200, 'code' => 200,
'status' => 'ok', 'status' => 'ok',
'challenge' => $status->challenge() 'challenge' => $status->challenge()
]; ];
} }
} }
], ],
[ [
'pattern' => 'auth/logout', 'pattern' => 'auth/logout',
'method' => 'POST', 'method' => 'POST',
'auth' => false, 'auth' => false,
'action' => function () { 'action' => function () {
$this->kirby()->auth()->logout(); $this->kirby()->auth()->logout();
return true; return true;
} }
], ],
]; ];

View file

@ -8,125 +8,125 @@ $pattern = '(account|pages/[^/]+|site|users/[^/]+)';
*/ */
return [ return [
[ [
'pattern' => $pattern . '/files/(:any)/sections/(:any)', 'pattern' => $pattern . '/files/(:any)/sections/(:any)',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $path, string $filename, string $sectionName) { 'action' => function (string $path, string $filename, string $sectionName) {
if ($section = $this->file($path, $filename)->blueprint()->section($sectionName)) { if ($section = $this->file($path, $filename)->blueprint()->section($sectionName)) {
return $section->toResponse(); return $section->toResponse();
} }
} }
], ],
[ [
'pattern' => $pattern . '/files/(:any)/fields/(:any)/(:all?)', 'pattern' => $pattern . '/files/(:any)/fields/(:any)/(:all?)',
'method' => 'ALL', 'method' => 'ALL',
'action' => function (string $parent, string $filename, string $fieldName, string $path = null) { 'action' => function (string $parent, string $filename, string $fieldName, string $path = null) {
if ($file = $this->file($parent, $filename)) { if ($file = $this->file($parent, $filename)) {
return $this->fieldApi($file, $fieldName, $path); return $this->fieldApi($file, $fieldName, $path);
} }
} }
], ],
[ [
'pattern' => $pattern . '/files', 'pattern' => $pattern . '/files',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $path) { 'action' => function (string $path) {
return $this->parent($path)->files()->sorted(); return $this->parent($path)->files()->sorted();
} }
], ],
[ [
'pattern' => $pattern . '/files', 'pattern' => $pattern . '/files',
'method' => 'POST', 'method' => 'POST',
'action' => function (string $path) { 'action' => function (string $path) {
// move_uploaded_file() not working with unit test // move_uploaded_file() not working with unit test
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
return $this->upload(function ($source, $filename) use ($path) { return $this->upload(function ($source, $filename) use ($path) {
return $this->parent($path)->createFile([ return $this->parent($path)->createFile([
'content' => [ 'content' => [
'sort' => $this->requestBody('sort') 'sort' => $this->requestBody('sort')
], ],
'source' => $source, 'source' => $source,
'template' => $this->requestBody('template'), 'template' => $this->requestBody('template'),
'filename' => $filename 'filename' => $filename
]); ]);
}); });
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} }
], ],
[ [
'pattern' => $pattern . '/files/search', 'pattern' => $pattern . '/files/search',
'method' => 'GET|POST', 'method' => 'GET|POST',
'action' => function (string $path) { 'action' => function (string $path) {
$files = $this->parent($path)->files(); $files = $this->parent($path)->files();
if ($this->requestMethod() === 'GET') { if ($this->requestMethod() === 'GET') {
return $files->search($this->requestQuery('q')); return $files->search($this->requestQuery('q'));
} else { } else {
return $files->query($this->requestBody()); return $files->query($this->requestBody());
} }
} }
], ],
[ [
'pattern' => $pattern . '/files/sort', 'pattern' => $pattern . '/files/sort',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $path) { 'action' => function (string $path) {
return $this->parent($path)->files()->changeSort( return $this->parent($path)->files()->changeSort(
$this->requestBody('files'), $this->requestBody('files'),
$this->requestBody('index') $this->requestBody('index')
); );
} }
], ],
[ [
'pattern' => $pattern . '/files/(:any)', 'pattern' => $pattern . '/files/(:any)',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $path, string $filename) { 'action' => function (string $path, string $filename) {
return $this->file($path, $filename); return $this->file($path, $filename);
} }
], ],
[ [
'pattern' => $pattern . '/files/(:any)', 'pattern' => $pattern . '/files/(:any)',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $path, string $filename) { 'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->update($this->requestBody(), $this->language(), true); return $this->file($path, $filename)->update($this->requestBody(), $this->language(), true);
} }
], ],
[ [
'pattern' => $pattern . '/files/(:any)', 'pattern' => $pattern . '/files/(:any)',
'method' => 'POST', 'method' => 'POST',
'action' => function (string $path, string $filename) { 'action' => function (string $path, string $filename) {
return $this->upload(function ($source) use ($path, $filename) { return $this->upload(function ($source) use ($path, $filename) {
return $this->file($path, $filename)->replace($source); return $this->file($path, $filename)->replace($source);
}); });
} }
], ],
[ [
'pattern' => $pattern . '/files/(:any)', 'pattern' => $pattern . '/files/(:any)',
'method' => 'DELETE', 'method' => 'DELETE',
'action' => function (string $path, string $filename) { 'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->delete(); return $this->file($path, $filename)->delete();
} }
], ],
[ [
'pattern' => $pattern . '/files/(:any)/name', 'pattern' => $pattern . '/files/(:any)/name',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $path, string $filename) { 'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->changeName($this->requestBody('name')); return $this->file($path, $filename)->changeName($this->requestBody('name'));
} }
], ],
[ [
'pattern' => 'files/search', 'pattern' => 'files/search',
'method' => 'GET|POST', 'method' => 'GET|POST',
'action' => function () { 'action' => function () {
$files = $this $files = $this
->site() ->site()
->index(true) ->index(true)
->filter('isReadable', true) ->filter('isReadable', true)
->files(); ->files();
if ($this->requestMethod() === 'GET') { if ($this->requestMethod() === 'GET') {
return $files->search($this->requestQuery('q')); return $files->search($this->requestQuery('q'));
} else { } else {
return $files->query($this->requestBody()); return $files->query($this->requestBody());
} }
} }
], ],
]; ];

View file

@ -4,43 +4,43 @@
* Roles Routes * Roles Routes
*/ */
return [ return [
[ [
'pattern' => 'languages', 'pattern' => 'languages',
'method' => 'GET', 'method' => 'GET',
'action' => function () { 'action' => function () {
return $this->kirby()->languages(); return $this->kirby()->languages();
} }
], ],
[ [
'pattern' => 'languages', 'pattern' => 'languages',
'method' => 'POST', 'method' => 'POST',
'action' => function () { 'action' => function () {
return $this->kirby()->languages()->create($this->requestBody()); return $this->kirby()->languages()->create($this->requestBody());
} }
], ],
[ [
'pattern' => 'languages/(:any)', 'pattern' => 'languages/(:any)',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $code) { 'action' => function (string $code) {
return $this->kirby()->languages()->find($code); return $this->kirby()->languages()->find($code);
} }
], ],
[ [
'pattern' => 'languages/(:any)', 'pattern' => 'languages/(:any)',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $code) { 'action' => function (string $code) {
if ($language = $this->kirby()->languages()->find($code)) { if ($language = $this->kirby()->languages()->find($code)) {
return $language->update($this->requestBody()); return $language->update($this->requestBody());
} }
} }
], ],
[ [
'pattern' => 'languages/(:any)', 'pattern' => 'languages/(:any)',
'method' => 'DELETE', 'method' => 'DELETE',
'action' => function (string $code) { 'action' => function (string $code) {
if ($language = $this->kirby()->languages()->find($code)) { if ($language = $this->kirby()->languages()->find($code)) {
return $language->delete(); return $language->delete();
} }
} }
] ]
]; ];

View file

@ -5,87 +5,40 @@
* Content Lock Routes * Content Lock Routes
*/ */
return [ return [
[ [
'pattern' => '(:all)/lock', 'pattern' => '(:all)/lock',
'method' => 'GET', 'method' => 'PATCH',
/** 'action' => function (string $path) {
* @deprecated 3.6.0 if ($lock = $this->parent($path)->lock()) {
* @todo Remove in 3.7.0 return $lock->create();
*/ }
'action' => function (string $path) { }
deprecated('The `GET (:all)/lock` API endpoint has been deprecated and will be removed in 3.7.0'); ],
[
if ($lock = $this->parent($path)->lock()) { 'pattern' => '(:all)/lock',
return [ 'method' => 'DELETE',
'supported' => true, 'action' => function (string $path) {
'locked' => $lock->get() if ($lock = $this->parent($path)->lock()) {
]; return $lock->remove();
} }
}
return [ ],
'supported' => false, [
'locked' => null 'pattern' => '(:all)/unlock',
]; 'method' => 'PATCH',
} 'action' => function (string $path) {
], if ($lock = $this->parent($path)->lock()) {
[ return $lock->unlock();
'pattern' => '(:all)/lock', }
'method' => 'PATCH', }
'action' => function (string $path) { ],
if ($lock = $this->parent($path)->lock()) { [
return $lock->create(); 'pattern' => '(:all)/unlock',
} 'method' => 'DELETE',
} 'action' => function (string $path) {
], if ($lock = $this->parent($path)->lock()) {
[ return $lock->resolve();
'pattern' => '(:all)/lock', }
'method' => 'DELETE', }
'action' => function (string $path) { ],
if ($lock = $this->parent($path)->lock()) {
return $lock->remove();
}
}
],
[
'pattern' => '(:all)/unlock',
'method' => 'GET',
/**
* @deprecated 3.6.0
* @todo Remove in 3.7.0
*/
'action' => function (string $path) {
deprecated('The `GET (:all)/unlock` API endpoint has been deprecated and will be removed in 3.7.0');
if ($lock = $this->parent($path)->lock()) {
return [
'supported' => true,
'unlocked' => $lock->isUnlocked()
];
}
return [
'supported' => false,
'unlocked' => null
];
}
],
[
'pattern' => '(:all)/unlock',
'method' => 'PATCH',
'action' => function (string $path) {
if ($lock = $this->parent($path)->lock()) {
return $lock->unlock();
}
}
],
[
'pattern' => '(:all)/unlock',
'method' => 'DELETE',
'action' => function (string $path) {
if ($lock = $this->parent($path)->lock()) {
return $lock->resolve();
}
}
],
]; ];

View file

@ -1,132 +1,121 @@
<?php <?php
/** /**
* Page Routes * Page Routes
*/ */
return [ return [
[ [
'pattern' => 'pages/(:any)', 'pattern' => 'pages/(:any)',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id); return $this->page($id);
} }
], ],
[ [
'pattern' => 'pages/(:any)', 'pattern' => 'pages/(:any)',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id)->update($this->requestBody(), $this->language(), true); return $this->page($id)->update($this->requestBody(), $this->language(), true);
} }
], ],
[ [
'pattern' => 'pages/(:any)', 'pattern' => 'pages/(:any)',
'method' => 'DELETE', 'method' => 'DELETE',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id)->delete($this->requestBody('force', false)); return $this->page($id)->delete($this->requestBody('force', false));
} }
], ],
[ [
'pattern' => 'pages/(:any)/blueprint', 'pattern' => 'pages/(:any)/blueprint',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id)->blueprint(); return $this->page($id)->blueprint();
} }
], ],
[ [
'pattern' => [ 'pattern' => 'pages/(:any)/blueprints',
'pages/(:any)/blueprints', 'method' => 'GET',
/** 'action' => function (string $id) {
* @deprecated return $this->page($id)->blueprints($this->requestQuery('section'));
* @todo remove in 3.7.0 }
*/ ],
'pages/(:any)/children/blueprints', [
], 'pattern' => 'pages/(:any)/children',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
// @codeCoverageIgnoreStart return $this->pages($id, $this->requestQuery('status'));
if ($this->route->pattern() === 'pages/([a-zA-Z0-9\.\-_%= \+\@\(\)]+)/children/blueprints') { }
deprecated('`GET pages/(:any)/children/blueprints` API endpoint has been deprecated and will be removed in 3.7.0. Use `GET pages/(:any)/blueprints` instead'); ],
} [
// @codeCoverageIgnoreEnd 'pattern' => 'pages/(:any)/children',
return $this->page($id)->blueprints($this->requestQuery('section')); 'method' => 'POST',
} 'action' => function (string $id) {
], return $this->page($id)->createChild($this->requestBody());
[ }
'pattern' => 'pages/(:any)/children', ],
'method' => 'GET', [
'action' => function (string $id) { 'pattern' => 'pages/(:any)/children/search',
return $this->pages($id, $this->requestQuery('status')); 'method' => 'GET|POST',
} 'action' => function (string $id) {
], return $this->searchPages($id);
[ }
'pattern' => 'pages/(:any)/children', ],
'method' => 'POST', [
'action' => function (string $id) { 'pattern' => 'pages/(:any)/duplicate',
return $this->page($id)->createChild($this->requestBody()); 'method' => 'POST',
} 'action' => function (string $id) {
], return $this->page($id)->duplicate($this->requestBody('slug'), [
[ 'children' => $this->requestBody('children'),
'pattern' => 'pages/(:any)/children/search', 'files' => $this->requestBody('files'),
'method' => 'GET|POST', ]);
'action' => function (string $id) { }
return $this->searchPages($id); ],
} [
], 'pattern' => 'pages/(:any)/slug',
[ 'method' => 'PATCH',
'pattern' => 'pages/(:any)/duplicate', 'action' => function (string $id) {
'method' => 'POST', return $this->page($id)->changeSlug($this->requestBody('slug'));
'action' => function (string $id) { }
return $this->page($id)->duplicate($this->requestBody('slug'), [ ],
'children' => $this->requestBody('children'), [
'files' => $this->requestBody('files'), 'pattern' => 'pages/(:any)/status',
]); 'method' => 'PATCH',
} 'action' => function (string $id) {
], return $this->page($id)->changeStatus($this->requestBody('status'), $this->requestBody('position'));
[ }
'pattern' => 'pages/(:any)/slug', ],
'method' => 'PATCH', [
'action' => function (string $id) { 'pattern' => 'pages/(:any)/template',
return $this->page($id)->changeSlug($this->requestBody('slug')); 'method' => 'PATCH',
} 'action' => function (string $id) {
], return $this->page($id)->changeTemplate($this->requestBody('template'));
[ }
'pattern' => 'pages/(:any)/status', ],
'method' => 'PATCH', [
'action' => function (string $id) { 'pattern' => 'pages/(:any)/title',
return $this->page($id)->changeStatus($this->requestBody('status'), $this->requestBody('position')); 'method' => 'PATCH',
} 'action' => function (string $id) {
], return $this->page($id)->changeTitle($this->requestBody('title'));
[ }
'pattern' => 'pages/(:any)/template', ],
'method' => 'PATCH', [
'action' => function (string $id) { 'pattern' => 'pages/(:any)/sections/(:any)',
return $this->page($id)->changeTemplate($this->requestBody('template')); 'method' => 'GET',
} 'action' => function (string $id, string $sectionName) {
], if ($section = $this->page($id)->blueprint()->section($sectionName)) {
[ return $section->toResponse();
'pattern' => 'pages/(:any)/title', }
'method' => 'PATCH', }
'action' => function (string $id) { ],
return $this->page($id)->changeTitle($this->requestBody('title')); [
} 'pattern' => 'pages/(:any)/fields/(:any)/(:all?)',
], 'method' => 'ALL',
[ 'action' => function (string $id, string $fieldName, string $path = null) {
'pattern' => 'pages/(:any)/sections/(:any)', if ($page = $this->page($id)) {
'method' => 'GET', return $this->fieldApi($page, $fieldName, $path);
'action' => function (string $id, string $sectionName) { }
if ($section = $this->page($id)->blueprint()->section($sectionName)) { }
return $section->toResponse(); ],
}
}
],
[
'pattern' => 'pages/(:any)/fields/(:any)/(:all?)',
'method' => 'ALL',
'action' => function (string $id, string $fieldName, string $path = null) {
if ($page = $this->page($id)) {
return $this->fieldApi($page, $fieldName, $path);
}
}
],
]; ];

View file

@ -4,25 +4,27 @@
* Roles Routes * Roles Routes
*/ */
return [ return [
[ [
'pattern' => 'roles', 'pattern' => 'roles',
'method' => 'GET', 'method' => 'GET',
'action' => function () { 'action' => function () {
switch (get('canBe')) { $kirby = $this->kirby();
case 'changed':
return $this->kirby()->roles()->canBeChanged(); switch ($kirby->request()->get('canBe')) {
case 'created': case 'changed':
return $this->kirby()->roles()->canBeCreated(); return $kirby->roles()->canBeChanged();
default: case 'created':
return $this->kirby()->roles(); return $kirby->roles()->canBeCreated();
} default:
} return $kirby->roles();
], }
[ }
'pattern' => 'roles/(:any)', ],
'method' => 'GET', [
'action' => function (string $name) { 'pattern' => 'roles/(:any)',
return $this->kirby()->roles()->find($name); 'method' => 'GET',
} 'action' => function (string $name) {
] return $this->kirby()->roles()->find($name);
}
]
]; ];

View file

@ -1,115 +1,104 @@
<?php <?php
/** /**
* Site Routes * Site Routes
*/ */
return [ return [
[ [
'pattern' => 'site', 'pattern' => 'site',
'action' => function () { 'action' => function () {
return $this->site(); return $this->site();
} }
], ],
[ [
'pattern' => 'site', 'pattern' => 'site',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function () { 'action' => function () {
return $this->site()->update($this->requestBody(), $this->language(), true); return $this->site()->update($this->requestBody(), $this->language(), true);
} }
], ],
[ [
'pattern' => 'site/children', 'pattern' => 'site/children',
'method' => 'GET', 'method' => 'GET',
'action' => function () { 'action' => function () {
return $this->pages(null, $this->requestQuery('status')); return $this->pages(null, $this->requestQuery('status'));
} }
], ],
[ [
'pattern' => 'site/children', 'pattern' => 'site/children',
'method' => 'POST', 'method' => 'POST',
'action' => function () { 'action' => function () {
return $this->site()->createChild($this->requestBody()); return $this->site()->createChild($this->requestBody());
} }
], ],
[ [
'pattern' => 'site/children/search', 'pattern' => 'site/children/search',
'method' => 'GET|POST', 'method' => 'GET|POST',
'action' => function () { 'action' => function () {
return $this->searchPages(); return $this->searchPages();
} }
], ],
[ [
'pattern' => 'site/blueprint', 'pattern' => 'site/blueprint',
'method' => 'GET', 'method' => 'GET',
'action' => function () { 'action' => function () {
return $this->site()->blueprint(); return $this->site()->blueprint();
} }
], ],
[ [
'pattern' => [ 'pattern' => 'site/blueprints',
'site/blueprints', 'method' => 'GET',
/** 'action' => function () {
* @deprecated return $this->site()->blueprints($this->requestQuery('section'));
* @todo remove in 3.7.0 }
*/ ],
'site/children/blueprints', [
], 'pattern' => 'site/find',
'method' => 'GET', 'method' => 'POST',
'action' => function () { 'action' => function () {
// @codeCoverageIgnoreStart return $this->site()->find(false, ...$this->requestBody());
if ($this->route->pattern() === 'site/children/blueprints') { }
deprecated('`GET site/children/blueprints` API endpoint has been deprecated and will be removed in 3.7.0. Use `GET site/blueprints` instead.'); ],
} [
// @codeCoverageIgnoreEnd 'pattern' => 'site/title',
return $this->site()->blueprints($this->requestQuery('section')); 'method' => 'PATCH',
} 'action' => function () {
], return $this->site()->changeTitle($this->requestBody('title'));
[ }
'pattern' => 'site/find', ],
'method' => 'POST', [
'action' => function () { 'pattern' => 'site/search',
return $this->site()->find(false, ...$this->requestBody()); 'method' => 'GET|POST',
} 'action' => function () {
], $pages = $this
[ ->site()
'pattern' => 'site/title', ->index(true)
'method' => 'PATCH', ->filter('isReadable', true);
'action' => function () {
return $this->site()->changeTitle($this->requestBody('title'));
}
],
[
'pattern' => 'site/search',
'method' => 'GET|POST',
'action' => function () {
$pages = $this
->site()
->index(true)
->filter('isReadable', true);
if ($this->requestMethod() === 'GET') { if ($this->requestMethod() === 'GET') {
return $pages->search($this->requestQuery('q')); return $pages->search($this->requestQuery('q'));
} else { } else {
return $pages->query($this->requestBody()); return $pages->query($this->requestBody());
} }
} }
], ],
[ [
'pattern' => 'site/sections/(:any)', 'pattern' => 'site/sections/(:any)',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $sectionName) { 'action' => function (string $sectionName) {
if ($section = $this->site()->blueprint()->section($sectionName)) { if ($section = $this->site()->blueprint()->section($sectionName)) {
return $section->toResponse(); return $section->toResponse();
} }
} }
], ],
[ [
'pattern' => 'site/fields/(:any)/(:all?)', 'pattern' => 'site/fields/(:any)/(:all?)',
'method' => 'ALL', 'method' => 'ALL',
'action' => function (string $fieldName, string $path = null) { 'action' => function (string $fieldName, string $path = null) {
return $this->fieldApi($this->site(), $fieldName, $path); return $this->fieldApi($this->site(), $fieldName, $path);
} }
] ]
]; ];

View file

@ -8,72 +8,72 @@ use Kirby\Exception\InvalidArgumentException;
*/ */
return [ return [
[ [
'pattern' => 'system', 'pattern' => 'system',
'method' => 'GET', 'method' => 'GET',
'auth' => false, 'auth' => false,
'action' => function () { 'action' => function () {
$system = $this->kirby()->system(); $system = $this->kirby()->system();
if ($this->kirby()->user()) { if ($this->kirby()->user()) {
return $system; return $system;
} else { } else {
if ($system->isOk() === true) { if ($system->isOk() === true) {
$info = $this->resolve($system)->view('login')->toArray(); $info = $this->resolve($system)->view('login')->toArray();
} else { } else {
$info = $this->resolve($system)->view('troubleshooting')->toArray(); $info = $this->resolve($system)->view('troubleshooting')->toArray();
} }
return [ return [
'status' => 'ok', 'status' => 'ok',
'data' => $info, 'data' => $info,
'type' => 'model' 'type' => 'model'
]; ];
} }
} }
], ],
[ [
'pattern' => 'system/register', 'pattern' => 'system/register',
'method' => 'POST', 'method' => 'POST',
'action' => function () { 'action' => function () {
return $this->kirby()->system()->register($this->requestBody('license'), $this->requestBody('email')); return $this->kirby()->system()->register($this->requestBody('license'), $this->requestBody('email'));
} }
], ],
[ [
'pattern' => 'system/install', 'pattern' => 'system/install',
'method' => 'POST', 'method' => 'POST',
'auth' => false, 'auth' => false,
'action' => function () { 'action' => function () {
$system = $this->kirby()->system(); $system = $this->kirby()->system();
$auth = $this->kirby()->auth(); $auth = $this->kirby()->auth();
// csrf token check // csrf token check
if ($auth->type() === 'session' && $auth->csrf() === false) { if ($auth->type() === 'session' && $auth->csrf() === false) {
throw new InvalidArgumentException('Invalid CSRF token'); throw new InvalidArgumentException('Invalid CSRF token');
} }
if ($system->isOk() === false) { if ($system->isOk() === false) {
throw new Exception('The server is not setup correctly'); throw new Exception('The server is not setup correctly');
} }
if ($system->isInstallable() === false) { if ($system->isInstallable() === false) {
throw new Exception('The Panel cannot be installed'); throw new Exception('The Panel cannot be installed');
} }
if ($system->isInstalled() === true) { if ($system->isInstalled() === true) {
throw new Exception('The Panel is already installed'); throw new Exception('The Panel is already installed');
} }
// create the first user // create the first user
$user = $this->users()->create($this->requestBody()); $user = $this->users()->create($this->requestBody());
$token = $user->login($this->requestBody('password')); $token = $user->login($this->requestBody('password'));
return [ return [
'status' => 'ok', 'status' => 'ok',
'token' => $token, 'token' => $token,
'user' => $this->resolve($user)->view('auth')->toArray() 'user' => $this->resolve($user)->view('auth')->toArray()
]; ];
} }
] ]
]; ];

View file

@ -4,21 +4,21 @@
* Translations Routes * Translations Routes
*/ */
return [ return [
[ [
'pattern' => 'translations', 'pattern' => 'translations',
'method' => 'GET', 'method' => 'GET',
'auth' => false, 'auth' => false,
'action' => function () { 'action' => function () {
return $this->kirby()->translations(); return $this->kirby()->translations();
} }
], ],
[ [
'pattern' => 'translations/(:any)', 'pattern' => 'translations/(:any)',
'method' => 'GET', 'method' => 'GET',
'auth' => false, 'auth' => false,
'action' => function (string $code) { 'action' => function (string $code) {
return $this->kirby()->translations()->find($code); return $this->kirby()->translations()->find($code);
} }
] ]
]; ];

View file

@ -6,202 +6,202 @@ use Kirby\Filesystem\F;
* User Routes * User Routes
*/ */
return [ return [
[ [
'pattern' => 'users', 'pattern' => 'users',
'method' => 'GET', 'method' => 'GET',
'action' => function () { 'action' => function () {
return $this->users()->sort('username', 'asc', 'email', 'asc'); return $this->users()->sort('username', 'asc', 'email', 'asc');
} }
], ],
[ [
'pattern' => 'users', 'pattern' => 'users',
'method' => 'POST', 'method' => 'POST',
'action' => function () { 'action' => function () {
return $this->users()->create($this->requestBody()); return $this->users()->create($this->requestBody());
} }
], ],
[ [
'pattern' => 'users/search', 'pattern' => 'users/search',
'method' => 'GET|POST', 'method' => 'GET|POST',
'action' => function () { 'action' => function () {
if ($this->requestMethod() === 'GET') { if ($this->requestMethod() === 'GET') {
return $this->users()->search($this->requestQuery('q')); return $this->users()->search($this->requestQuery('q'));
} else { } else {
return $this->users()->query($this->requestBody()); return $this->users()->query($this->requestBody());
} }
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)', '(account)',
'users/(:any)', 'users/(:any)',
], ],
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id); return $this->user($id);
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)', '(account)',
'users/(:any)', 'users/(:any)',
], ],
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->update($this->requestBody(), $this->language(), true); return $this->user($id)->update($this->requestBody(), $this->language(), true);
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)', '(account)',
'users/(:any)', 'users/(:any)',
], ],
'method' => 'DELETE', 'method' => 'DELETE',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->delete(); return $this->user($id)->delete();
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/avatar', '(account)/avatar',
'users/(:any)/avatar', 'users/(:any)/avatar',
], ],
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->avatar(); return $this->user($id)->avatar();
} }
], ],
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
[ [
'pattern' => [ 'pattern' => [
'(account)/avatar', '(account)/avatar',
'users/(:any)/avatar', 'users/(:any)/avatar',
], ],
'method' => 'POST', 'method' => 'POST',
'action' => function (string $id) { 'action' => function (string $id) {
if ($avatar = $this->user($id)->avatar()) { if ($avatar = $this->user($id)->avatar()) {
$avatar->delete(); $avatar->delete();
} }
return $this->upload(function ($source, $filename) use ($id) { return $this->upload(function ($source, $filename) use ($id) {
return $this->user($id)->createFile([ return $this->user($id)->createFile([
'filename' => 'profile.' . F::extension($filename), 'filename' => 'profile.' . F::extension($filename),
'template' => 'avatar', 'template' => 'avatar',
'source' => $source 'source' => $source
]); ]);
}, $single = true); }, $single = true);
} }
], ],
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
[ [
'pattern' => [ 'pattern' => [
'(account)/avatar', '(account)/avatar',
'users/(:any)/avatar', 'users/(:any)/avatar',
], ],
'method' => 'DELETE', 'method' => 'DELETE',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->avatar()->delete(); return $this->user($id)->avatar()->delete();
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/blueprint', '(account)/blueprint',
'users/(:any)/blueprint', 'users/(:any)/blueprint',
], ],
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->blueprint(); return $this->user($id)->blueprint();
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/blueprints', '(account)/blueprints',
'users/(:any)/blueprints', 'users/(:any)/blueprints',
], ],
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->blueprints($this->requestQuery('section')); return $this->user($id)->blueprints($this->requestQuery('section'));
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/email', '(account)/email',
'users/(:any)/email', 'users/(:any)/email',
], ],
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->changeEmail($this->requestBody('email')); return $this->user($id)->changeEmail($this->requestBody('email'));
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/language', '(account)/language',
'users/(:any)/language', 'users/(:any)/language',
], ],
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->changeLanguage($this->requestBody('language')); return $this->user($id)->changeLanguage($this->requestBody('language'));
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/name', '(account)/name',
'users/(:any)/name', 'users/(:any)/name',
], ],
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->changeName($this->requestBody('name')); return $this->user($id)->changeName($this->requestBody('name'));
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/password', '(account)/password',
'users/(:any)/password', 'users/(:any)/password',
], ],
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->changePassword($this->requestBody('password')); return $this->user($id)->changePassword($this->requestBody('password'));
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/role', '(account)/role',
'users/(:any)/role', 'users/(:any)/role',
], ],
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->changeRole($this->requestBody('role')); return $this->user($id)->changeRole($this->requestBody('role'));
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/roles', '(account)/roles',
'users/(:any)/roles', 'users/(:any)/roles',
], ],
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->roles(); return $this->user($id)->roles();
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/sections/(:any)', '(account)/sections/(:any)',
'users/(:any)/sections/(:any)', 'users/(:any)/sections/(:any)',
], ],
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id, string $sectionName) { 'action' => function (string $id, string $sectionName) {
if ($section = $this->user($id)->blueprint()->section($sectionName)) { if ($section = $this->user($id)->blueprint()->section($sectionName)) {
return $section->toResponse(); return $section->toResponse();
} }
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/fields/(:any)/(:all?)', '(account)/fields/(:any)/(:all?)',
'users/(:any)/fields/(:any)/(:all?)', 'users/(:any)/fields/(:any)/(:all?)',
], ],
'method' => 'ALL', 'method' => 'ALL',
'action' => function (string $id, string $fieldName, string $path = null) { 'action' => function (string $id, string $fieldName, string $path = null) {
return $this->fieldApi($this->user($id), $fieldName, $path); return $this->fieldApi($this->user($id), $fieldName, $path);
} }
], ],
]; ];

View file

@ -1,12 +1,14 @@
<?php <?php
use Kirby\Toolkit\I18n;
return function () { return function () {
return [ return [
'icon' => 'account', 'icon' => 'account',
'label' => t('view.account'), 'label' => I18n::translate('view.account'),
'search' => 'users', 'search' => 'users',
'dialogs' => require __DIR__ . '/account/dialogs.php', 'dialogs' => require __DIR__ . '/account/dialogs.php',
'dropdowns' => require __DIR__ . '/account/dropdowns.php', 'dropdowns' => require __DIR__ . '/account/dropdowns.php',
'views' => require __DIR__ . '/account/views.php' 'views' => require __DIR__ . '/account/views.php'
]; ];
}; };

View file

@ -4,67 +4,67 @@ $dialogs = require __DIR__ . '/../users/dialogs.php';
return [ return [
// change email // change email
'account.changeEmail' => [ 'account.changeEmail' => [
'pattern' => '(account)/changeEmail', 'pattern' => '(account)/changeEmail',
'load' => $dialogs['user.changeEmail']['load'], 'load' => $dialogs['user.changeEmail']['load'],
'submit' => $dialogs['user.changeEmail']['submit'], 'submit' => $dialogs['user.changeEmail']['submit'],
], ],
// change language // change language
'account.changeLanguage' => [ 'account.changeLanguage' => [
'pattern' => '(account)/changeLanguage', 'pattern' => '(account)/changeLanguage',
'load' => $dialogs['user.changeLanguage']['load'], 'load' => $dialogs['user.changeLanguage']['load'],
'submit' => $dialogs['user.changeLanguage']['submit'], 'submit' => $dialogs['user.changeLanguage']['submit'],
], ],
// change name // change name
'account.changeName' => [ 'account.changeName' => [
'pattern' => '(account)/changeName', 'pattern' => '(account)/changeName',
'load' => $dialogs['user.changeName']['load'], 'load' => $dialogs['user.changeName']['load'],
'submit' => $dialogs['user.changeName']['submit'], 'submit' => $dialogs['user.changeName']['submit'],
], ],
// change password // change password
'account.changePassword' => [ 'account.changePassword' => [
'pattern' => '(account)/changePassword', 'pattern' => '(account)/changePassword',
'load' => $dialogs['user.changePassword']['load'], 'load' => $dialogs['user.changePassword']['load'],
'submit' => $dialogs['user.changePassword']['submit'], 'submit' => $dialogs['user.changePassword']['submit'],
], ],
// change role // change role
'account.changeRole' => [ 'account.changeRole' => [
'pattern' => '(account)/changeRole', 'pattern' => '(account)/changeRole',
'load' => $dialogs['user.changeRole']['load'], 'load' => $dialogs['user.changeRole']['load'],
'submit' => $dialogs['user.changeRole']['submit'], 'submit' => $dialogs['user.changeRole']['submit'],
], ],
// delete // delete
'account.delete' => [ 'account.delete' => [
'pattern' => '(account)/delete', 'pattern' => '(account)/delete',
'load' => $dialogs['user.delete']['load'], 'load' => $dialogs['user.delete']['load'],
'submit' => $dialogs['user.delete']['submit'], 'submit' => $dialogs['user.delete']['submit'],
], ],
// change file name // change file name
'account.file.changeName' => [ 'account.file.changeName' => [
'pattern' => '(account)/files/(:any)/changeName', 'pattern' => '(account)/files/(:any)/changeName',
'load' => $dialogs['user.file.changeName']['load'], 'load' => $dialogs['user.file.changeName']['load'],
'submit' => $dialogs['user.file.changeName']['submit'], 'submit' => $dialogs['user.file.changeName']['submit'],
], ],
// change file sort // change file sort
'account.file.changeSort' => [ 'account.file.changeSort' => [
'pattern' => '(account)/files/(:any)/changeSort', 'pattern' => '(account)/files/(:any)/changeSort',
'load' => $dialogs['user.file.changeSort']['load'], 'load' => $dialogs['user.file.changeSort']['load'],
'submit' => $dialogs['user.file.changeSort']['submit'], 'submit' => $dialogs['user.file.changeSort']['submit'],
], ],
// delete // delete
'account.file.delete' => [ 'account.file.delete' => [
'pattern' => '(account)/files/(:any)/delete', 'pattern' => '(account)/files/(:any)/delete',
'load' => $dialogs['user.file.delete']['load'], 'load' => $dialogs['user.file.delete']['load'],
'submit' => $dialogs['user.file.delete']['submit'], 'submit' => $dialogs['user.file.delete']['submit'],
], ],
]; ];

View file

@ -3,12 +3,12 @@
$dropdowns = require __DIR__ . '/../users/dropdowns.php'; $dropdowns = require __DIR__ . '/../users/dropdowns.php';
return [ return [
'account' => [ 'account' => [
'pattern' => '(account)', 'pattern' => '(account)',
'options' => $dropdowns['user']['options'] 'options' => $dropdowns['user']['options']
], ],
'account.file' => [ 'account.file' => [
'pattern' => '(account)/files/(:any)', 'pattern' => '(account)/files/(:any)',
'options' => $dropdowns['user.file']['options'] 'options' => $dropdowns['user.file']['options']
], ],
]; ];

View file

@ -1,34 +1,24 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Cms\Find; use Kirby\Cms\Find;
use Kirby\Panel\Panel;
return [ return [
'account' => [ 'account' => [
'pattern' => 'account', 'pattern' => 'account',
'action' => fn () => [ 'action' => fn () => [
'component' => 'k-account-view', 'component' => 'k-account-view',
'props' => kirby()->user()->panel()->props(), 'props' => App::instance()->user()->panel()->props(),
], ],
], ],
'account.file' => [ 'account.file' => [
'pattern' => 'account/files/(:any)', 'pattern' => 'account/files/(:any)',
'action' => function (string $filename) { 'action' => function (string $filename) {
return Find::file('account', $filename)->panel()->view(); return Find::file('account', $filename)->panel()->view();
} }
], ],
'account.logout' => [ 'account.password' => [
'pattern' => 'logout', 'pattern' => 'reset-password',
'auth' => false, 'action' => fn () => ['component' => 'k-reset-password-view']
'action' => function () { ]
if ($user = kirby()->user()) {
$user->logout();
}
Panel::go('login');
},
],
'account.password' => [
'pattern' => 'reset-password',
'action' => fn () => ['component' => 'k-reset-password-view']
]
]; ];

View file

@ -4,6 +4,7 @@ use Kirby\Cms\Find;
use Kirby\Panel\Field; use Kirby\Panel\Field;
use Kirby\Panel\Panel; use Kirby\Panel\Panel;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
/** /**
* Shared file dialogs * Shared file dialogs
@ -13,119 +14,119 @@ use Kirby\Toolkit\Escape;
* the appropriate routes in the areas. * the appropriate routes in the areas.
*/ */
return [ return [
'changeName' => [ 'changeName' => [
'load' => function (string $path, string $filename) { 'load' => function (string $path, string $filename) {
$file = Find::file($path, $filename); $file = Find::file($path, $filename);
return [ return [
'component' => 'k-form-dialog', 'component' => 'k-form-dialog',
'props' => [ 'props' => [
'fields' => [ 'fields' => [
'name' => [ 'name' => [
'label' => t('name'), 'label' => I18n::translate('name'),
'type' => 'slug', 'type' => 'slug',
'required' => true, 'required' => true,
'icon' => 'title', 'icon' => 'title',
'allow' => '@._-', 'allow' => '@._-',
'after' => '.' . $file->extension(), 'after' => '.' . $file->extension(),
'preselect' => true 'preselect' => true
] ]
], ],
'submitButton' => t('rename'), 'submitButton' => I18n::translate('rename'),
'value' => [ 'value' => [
'name' => $file->name(), 'name' => $file->name(),
] ]
] ]
]; ];
}, },
'submit' => function (string $path, string $filename) { 'submit' => function (string $path, string $filename) {
$file = Find::file($path, $filename); $file = Find::file($path, $filename);
$renamed = $file->changeName(get('name')); $renamed = $file->changeName($file->kirby()->request()->get('name'));
$oldUrl = $file->panel()->url(true); $oldUrl = $file->panel()->url(true);
$newUrl = $renamed->panel()->url(true); $newUrl = $renamed->panel()->url(true);
$response = [ $response = [
'event' => 'file.changeName', 'event' => 'file.changeName',
'dispatch' => [ 'dispatch' => [
'content/move' => [ 'content/move' => [
$oldUrl, $oldUrl,
$newUrl $newUrl
] ]
], ],
]; ];
// check for a necessary redirect after the filename has changed // check for a necessary redirect after the filename has changed
if (Panel::referrer() === $oldUrl && $oldUrl !== $newUrl) { if (Panel::referrer() === $oldUrl && $oldUrl !== $newUrl) {
$response['redirect'] = $newUrl; $response['redirect'] = $newUrl;
} }
return $response; return $response;
} }
], ],
'changeSort' => [ 'changeSort' => [
'load' => function (string $path, string $filename) { 'load' => function (string $path, string $filename) {
$file = Find::file($path, $filename); $file = Find::file($path, $filename);
return [ return [
'component' => 'k-form-dialog', 'component' => 'k-form-dialog',
'props' => [ 'props' => [
'fields' => [ 'fields' => [
'position' => Field::filePosition($file) 'position' => Field::filePosition($file)
], ],
'submitButton' => t('change'), 'submitButton' => I18n::translate('change'),
'value' => [ 'value' => [
'position' => $file->sort()->isEmpty() ? $file->siblings(false)->count() + 1 : $file->sort()->toInt(), 'position' => $file->sort()->isEmpty() ? $file->siblings(false)->count() + 1 : $file->sort()->toInt(),
] ]
] ]
]; ];
}, },
'submit' => function (string $path, string $filename) { 'submit' => function (string $path, string $filename) {
$file = Find::file($path, $filename); $file = Find::file($path, $filename);
$files = $file->siblings()->sorted(); $files = $file->siblings()->sorted();
$ids = $files->keys(); $ids = $files->keys();
$newIndex = (int)(get('position')) - 1; $newIndex = (int)($file->kirby()->request()->get('position')) - 1;
$oldIndex = $files->indexOf($file); $oldIndex = $files->indexOf($file);
array_splice($ids, $oldIndex, 1); array_splice($ids, $oldIndex, 1);
array_splice($ids, $newIndex, 0, $file->id()); array_splice($ids, $newIndex, 0, $file->id());
$files->changeSort($ids); $files->changeSort($ids);
return [ return [
'event' => 'file.sort', 'event' => 'file.sort',
]; ];
} }
], ],
'delete' => [ 'delete' => [
'load' => function (string $path, string $filename) { 'load' => function (string $path, string $filename) {
$file = Find::file($path, $filename); $file = Find::file($path, $filename);
return [ return [
'component' => 'k-remove-dialog', 'component' => 'k-remove-dialog',
'props' => [ 'props' => [
'text' => tt('file.delete.confirm', [ 'text' => I18n::template('file.delete.confirm', [
'filename' => Escape::html($file->filename()) 'filename' => Escape::html($file->filename())
]), ]),
] ]
]; ];
}, },
'submit' => function (string $path, string $filename) { 'submit' => function (string $path, string $filename) {
$file = Find::file($path, $filename); $file = Find::file($path, $filename);
$redirect = false; $redirect = false;
$referrer = Panel::referrer(); $referrer = Panel::referrer();
$url = $file->panel()->url(true); $url = $file->panel()->url(true);
$file->delete(); $file->delete();
// redirect to the parent model URL // redirect to the parent model URL
// if the dialog has been opened in the file view // if the dialog has been opened in the file view
if ($referrer === $url) { if ($referrer === $url) {
$redirect = $file->parent()->panel()->url(true); $redirect = $file->parent()->panel()->url(true);
} }
return [ return [
'event' => 'file.delete', 'event' => 'file.delete',
'dispatch' => ['content/remove' => [$url]], 'dispatch' => ['content/remove' => [$url]],
'redirect' => $redirect 'redirect' => $redirect
]; ];
} }
], ],
]; ];

View file

@ -3,7 +3,7 @@
use Kirby\Cms\Find; use Kirby\Cms\Find;
return [ return [
'file' => function (string $parent, string $filename) { 'file' => function (string $parent, string $filename) {
return Find::file($parent, $filename)->panel()->dropdown(); return Find::file($parent, $filename)->panel()->dropdown();
} }
]; ];

View file

@ -1,39 +1,40 @@
<?php <?php
use Kirby\Panel\Panel; use Kirby\Panel\Panel;
use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'settings', 'icon' => 'settings',
'label' => t('view.installation'), 'label' => I18n::translate('view.installation'),
'views' => [ 'views' => [
'installation' => [ 'installation' => [
'pattern' => 'installation', 'pattern' => 'installation',
'auth' => false, 'auth' => false,
'action' => function () use ($kirby) { 'action' => function () use ($kirby) {
$system = $kirby->system(); $system = $kirby->system();
return [ return [
'component' => 'k-installation-view', 'component' => 'k-installation-view',
'props' => [ 'props' => [
'isInstallable' => $system->isInstallable(), 'isInstallable' => $system->isInstallable(),
'isInstalled' => $system->isInstalled(), 'isInstalled' => $system->isInstalled(),
'isOk' => $system->isOk(), 'isOk' => $system->isOk(),
'requirements' => $system->status(), 'requirements' => $system->status(),
'translations' => $kirby->translations()->values(function ($translation) { 'translations' => $kirby->translations()->values(function ($translation) {
return [ return [
'text' => $translation->name(), 'text' => $translation->name(),
'value' => $translation->code(), 'value' => $translation->code(),
]; ];
}), }),
] ]
]; ];
} }
], ],
'installation.fallback' => [ 'installation.fallback' => [
'pattern' => '(:all)', 'pattern' => '(:all)',
'auth' => false, 'auth' => false,
'action' => fn () => Panel::go('installation') 'action' => fn () => Panel::go('installation')
] ]
] ]
]; ];
}; };

View file

@ -1,11 +1,13 @@
<?php <?php
use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'globe', 'icon' => 'globe',
'label' => t('view.languages'), 'label' => I18n::translate('view.languages'),
'menu' => true, 'menu' => true,
'dialogs' => require __DIR__ . '/languages/dialogs.php', 'dialogs' => require __DIR__ . '/languages/dialogs.php',
'views' => require __DIR__ . '/languages/views.php' 'views' => require __DIR__ . '/languages/views.php'
]; ];
}; };

View file

@ -1,149 +1,155 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Cms\Find; use Kirby\Cms\Find;
use Kirby\Panel\Field; use Kirby\Panel\Field;
use Kirby\Toolkit\A; use Kirby\Toolkit\A;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
$languageDialogFields = [ $languageDialogFields = [
'name' => [ 'name' => [
'label' => t('language.name'), 'label' => I18n::translate('language.name'),
'type' => 'text', 'type' => 'text',
'required' => true, 'required' => true,
'icon' => 'title' 'icon' => 'title'
], ],
'code' => [ 'code' => [
'label' => t('language.code'), 'label' => I18n::translate('language.code'),
'type' => 'text', 'type' => 'text',
'required' => true, 'required' => true,
'counter' => false, 'counter' => false,
'icon' => 'globe', 'icon' => 'globe',
'width' => '1/2' 'width' => '1/2'
], ],
'direction' => [ 'direction' => [
'label' => t('language.direction'), 'label' => I18n::translate('language.direction'),
'type' => 'select', 'type' => 'select',
'required' => true, 'required' => true,
'empty' => false, 'empty' => false,
'options' => [ 'options' => [
['value' => 'ltr', 'text' => t('language.direction.ltr')], ['value' => 'ltr', 'text' => I18n::translate('language.direction.ltr')],
['value' => 'rtl', 'text' => t('language.direction.rtl')] ['value' => 'rtl', 'text' => I18n::translate('language.direction.rtl')]
], ],
'width' => '1/2' 'width' => '1/2'
], ],
'locale' => [ 'locale' => [
'label' => t('language.locale'), 'label' => I18n::translate('language.locale'),
'type' => 'text', 'type' => 'text',
], ],
]; ];
return [ return [
// create language // create language
'language.create' => [ 'language.create' => [
'pattern' => 'languages/create', 'pattern' => 'languages/create',
'load' => function () use ($languageDialogFields) { 'load' => function () use ($languageDialogFields) {
return [ return [
'component' => 'k-language-dialog', 'component' => 'k-language-dialog',
'props' => [ 'props' => [
'fields' => $languageDialogFields, 'fields' => $languageDialogFields,
'submitButton' => t('language.create'), 'submitButton' => I18n::translate('language.create'),
'value' => [ 'value' => [
'code' => '', 'code' => '',
'direction' => 'ltr', 'direction' => 'ltr',
'locale' => '', 'locale' => '',
'name' => '', 'name' => '',
] ]
] ]
]; ];
}, },
'submit' => function () { 'submit' => function () {
kirby()->languages()->create([ $kirby = App::instance();
'code' => get('code'),
'direction' => get('direction'),
'locale' => get('locale'),
'name' => get('name'),
]);
return [
'event' => 'language.create'
];
}
],
// delete language $data = $kirby->request()->get([
'language.delete' => [ 'code',
'pattern' => 'languages/(:any)/delete', 'direction',
'load' => function (string $id) { 'locale',
$language = Find::language($id); 'name'
return [ ]);
'component' => 'k-remove-dialog', $kirby->languages()->create($data);
'props' => [
'text' => tt('language.delete.confirm', [
'name' => Escape::html($language->name())
])
]
];
},
'submit' => function (string $id) {
Find::language($id)->delete();
return [
'event' => 'language.delete',
];
}
],
// update language return [
'language.update' => [ 'event' => 'language.create'
'pattern' => 'languages/(:any)/update', ];
'load' => function (string $id) use ($languageDialogFields) { }
$language = Find::language($id); ],
$fields = $languageDialogFields;
$locale = $language->locale();
// use the first locale key if there's only one // delete language
if (count($locale) === 1) { 'language.delete' => [
$locale = A::first($locale); 'pattern' => 'languages/(:any)/delete',
} 'load' => function (string $id) {
$language = Find::language($id);
return [
'component' => 'k-remove-dialog',
'props' => [
'text' => I18n::template('language.delete.confirm', [
'name' => Escape::html($language->name())
])
]
];
},
'submit' => function (string $id) {
Find::language($id)->delete();
return [
'event' => 'language.delete',
];
}
],
// the code of an existing language cannot be changed // update language
$fields['code']['disabled'] = true; 'language.update' => [
'pattern' => 'languages/(:any)/update',
'load' => function (string $id) use ($languageDialogFields) {
$language = Find::language($id);
$fields = $languageDialogFields;
$locale = $language->locale();
// if the locale settings is more complex than just a // use the first locale key if there's only one
// single string, the text field won't do it anymore. if (count($locale) === 1) {
// Changes can only be made in the language file and $locale = A::first($locale);
// we display a warning box instead. }
if (is_array($locale) === true) {
$fields['locale'] = [
'label' => $fields['locale']['label'],
'type' => 'info',
'text' => t('language.locale.warning')
];
}
return [ // the code of an existing language cannot be changed
'component' => 'k-language-dialog', $fields['code']['disabled'] = true;
'props' => [
'fields' => $fields, // if the locale settings is more complex than just a
'submitButton' => t('save'), // single string, the text field won't do it anymore.
'value' => [ // Changes can only be made in the language file and
'code' => $language->code(), // we display a warning box instead.
'direction' => $language->direction(), if (is_array($locale) === true) {
'locale' => $locale, $fields['locale'] = [
'name' => $language->name(), 'label' => $fields['locale']['label'],
'rules' => $language->rules(), 'type' => 'info',
] 'text' => I18n::translate('language.locale.warning')
] ];
]; }
},
'submit' => function (string $id) { return [
$language = Find::language($id)->update([ 'component' => 'k-language-dialog',
'direction' => get('direction'), 'props' => [
'locale' => get('locale'), 'fields' => $fields,
'name' => get('name'), 'submitButton' => I18n::translate('save'),
]); 'value' => [
return [ 'code' => $language->code(),
'event' => 'language.update' 'direction' => $language->direction(),
]; 'locale' => $locale,
} 'name' => $language->name(),
], 'rules' => $language->rules(),
]
]
];
},
'submit' => function (string $id) {
$kirby = App::instance();
$data = $kirby->request()->get(['direction', 'locale', 'name']);
$language = Find::language($id)->update($data);
return [
'event' => 'language.update'
];
}
],
]; ];

View file

@ -1,24 +1,25 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
return [ return [
'languages' => [ 'languages' => [
'pattern' => 'languages', 'pattern' => 'languages',
'action' => function () { 'action' => function () {
$kirby = kirby(); $kirby = App::instance();
return [ return [
'component' => 'k-languages-view', 'component' => 'k-languages-view',
'props' => [ 'props' => [
'languages' => $kirby->languages()->values(fn ($language) => [ 'languages' => $kirby->languages()->values(fn ($language) => [
'default' => $language->isDefault(), 'default' => $language->isDefault(),
'id' => $language->code(), 'id' => $language->code(),
'info' => Escape::html($language->code()), 'info' => Escape::html($language->code()),
'text' => Escape::html($language->name()), 'text' => Escape::html($language->name()),
]) ])
] ]
]; ];
} }
], ],
]; ];

View file

@ -1,43 +1,44 @@
<?php <?php
use Kirby\Panel\Panel; use Kirby\Panel\Panel;
use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'user', 'icon' => 'user',
'label' => t('login'), 'label' => I18n::translate('login'),
'views' => [ 'views' => [
'login' => [ 'login' => [
'pattern' => 'login', 'pattern' => 'login',
'auth' => false, 'auth' => false,
'action' => function () use ($kirby) { 'action' => function () use ($kirby) {
$system = $kirby->system(); $system = $kirby->system();
$status = $kirby->auth()->status(); $status = $kirby->auth()->status();
return [ return [
'component' => 'k-login-view', 'component' => 'k-login-view',
'props' => [ 'props' => [
'methods' => array_keys($system->loginMethods()), 'methods' => array_keys($system->loginMethods()),
'pending' => [ 'pending' => [
'email' => $status->email(), 'email' => $status->email(),
'challenge' => $status->challenge() 'challenge' => $status->challenge()
] ]
], ],
]; ];
} }
], ],
'login.fallback' => [ 'login.fallback' => [
'pattern' => '(:all)', 'pattern' => '(:all)',
'auth' => false, 'auth' => false,
'action' => function ($path) use ($kirby) { 'action' => function ($path) use ($kirby) {
/** /**
* Store the current path in the session * Store the current path in the session
* Once the user is logged in, the path will * Once the user is logged in, the path will
* be used to redirect to that view again * be used to redirect to that view again
*/ */
$kirby->session()->set('panel.path', $path); $kirby->session()->set('panel.path', $path);
Panel::go('login'); Panel::go('login');
} }
] ]
] ]
]; ];
}; };

View file

@ -0,0 +1,21 @@
<?php
use Kirby\Panel\Panel;
use Kirby\Toolkit\I18n;
return function ($kirby) {
return [
'icon' => 'user',
'label' => I18n::translate('logout'),
'views' => [
'logout' => [
'pattern' => 'logout',
'auth' => false,
'action' => function () use ($kirby) {
$kirby->auth()->logout();
Panel::go('login');
},
]
]
];
};

View file

@ -1,17 +1,18 @@
<?php <?php
use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'breadcrumbLabel' => function () use ($kirby) { 'breadcrumbLabel' => function () use ($kirby) {
return $kirby->site()->title()->or(t('view.site'))->toString(); return $kirby->site()->title()->or(I18n::translate('view.site'))->toString();
}, },
'icon' => 'home', 'icon' => 'home',
'label' => $kirby->site()->blueprint()->title() ?? t('view.site'), 'label' => $kirby->site()->blueprint()->title() ?? I18n::translate('view.site'),
'menu' => true, 'menu' => true,
'dialogs' => require __DIR__ . '/site/dialogs.php', 'dialogs' => require __DIR__ . '/site/dialogs.php',
'dropdowns' => require __DIR__ . '/site/dropdowns.php', 'dropdowns' => require __DIR__ . '/site/dropdowns.php',
'searches' => require __DIR__ . '/site/searches.php', 'searches' => require __DIR__ . '/site/searches.php',
'views' => require __DIR__ . '/site/views.php', 'views' => require __DIR__ . '/site/views.php',
]; ];
}; };

File diff suppressed because it is too large Load diff

View file

@ -5,22 +5,22 @@ use Kirby\Panel\Dropdown;
$files = require __DIR__ . '/../files/dropdowns.php'; $files = require __DIR__ . '/../files/dropdowns.php';
return [ return [
'changes' => [ 'changes' => [
'pattern' => 'changes', 'pattern' => 'changes',
'options' => fn () => Dropdown::changes() 'options' => fn () => Dropdown::changes()
], ],
'page' => [ 'page' => [
'pattern' => 'pages/(:any)', 'pattern' => 'pages/(:any)',
'options' => function (string $path) { 'options' => function (string $path) {
return Find::page($path)->panel()->dropdown(); return Find::page($path)->panel()->dropdown();
} }
], ],
'page.file' => [ 'page.file' => [
'pattern' => '(pages/.*?)/files/(:any)', 'pattern' => '(pages/.*?)/files/(:any)',
'options' => $files['file'] 'options' => $files['file']
], ],
'site.file' => [ 'site.file' => [
'pattern' => '(site)/files/(:any)', 'pattern' => '(site)/files/(:any)',
'options' => $files['file'] 'options' => $files['file']
] ]
]; ];

View file

@ -1,55 +1,57 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
return [ return [
'pages' => [ 'pages' => [
'label' => t('pages'), 'label' => I18n::translate('pages'),
'icon' => 'page', 'icon' => 'page',
'query' => function (string $query = null) { 'query' => function (string $query = null) {
$pages = site() $pages = App::instance()->site()
->index(true) ->index(true)
->search($query) ->search($query)
->filter('isReadable', true) ->filter('isReadable', true)
->limit(10); ->limit(10);
$results = []; $results = [];
foreach ($pages as $page) { foreach ($pages as $page) {
$results[] = [ $results[] = [
'image' => $page->panel()->image(), 'image' => $page->panel()->image(),
'text' => Escape::html($page->title()->value()), 'text' => Escape::html($page->title()->value()),
'link' => $page->panel()->url(true), 'link' => $page->panel()->url(true),
'info' => Escape::html($page->id()) 'info' => Escape::html($page->id())
]; ];
} }
return $results; return $results;
} }
], ],
'files' => [ 'files' => [
'label' => t('files'), 'label' => I18n::translate('files'),
'icon' => 'image', 'icon' => 'image',
'query' => function (string $query = null) { 'query' => function (string $query = null) {
$files = site() $files = App::instance()->site()
->index(true) ->index(true)
->filter('isReadable', true) ->filter('isReadable', true)
->files() ->files()
->search($query) ->search($query)
->limit(10); ->limit(10);
$results = []; $results = [];
foreach ($files as $file) { foreach ($files as $file) {
$results[] = [ $results[] = [
'image' => $file->panel()->image(), 'image' => $file->panel()->image(),
'text' => Escape::html($file->filename()), 'text' => Escape::html($file->filename()),
'link' => $file->panel()->url(true), 'link' => $file->panel()->url(true),
'info' => Escape::html($file->id()) 'info' => Escape::html($file->id())
]; ];
} }
return $results; return $results;
} }
] ]
]; ];

View file

@ -1,26 +1,27 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Cms\Find; use Kirby\Cms\Find;
return [ return [
'page' => [ 'page' => [
'pattern' => 'pages/(:any)', 'pattern' => 'pages/(:any)',
'action' => fn (string $path) => Find::page($path)->panel()->view() 'action' => fn (string $path) => Find::page($path)->panel()->view()
], ],
'page.file' => [ 'page.file' => [
'pattern' => 'pages/(:any)/files/(:any)', 'pattern' => 'pages/(:any)/files/(:any)',
'action' => function (string $id, string $filename) { 'action' => function (string $id, string $filename) {
return Find::file('pages/' . $id, $filename)->panel()->view(); return Find::file('pages/' . $id, $filename)->panel()->view();
} }
], ],
'site' => [ 'site' => [
'pattern' => 'site', 'pattern' => 'site',
'action' => fn () => site()->panel()->view() 'action' => fn () => App::instance()->site()->panel()->view()
], ],
'site.file' => [ 'site.file' => [
'pattern' => 'site/files/(:any)', 'pattern' => 'site/files/(:any)',
'action' => function (string $filename) { 'action' => function (string $filename) {
return Find::file('site', $filename)->panel()->view(); return Find::file('site', $filename)->panel()->view();
} }
], ],
]; ];

View file

@ -1,11 +1,13 @@
<?php <?php
use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'settings', 'icon' => 'settings',
'label' => t('view.system'), 'label' => I18n::translate('view.system'),
'menu' => true, 'menu' => true,
'dialogs' => require __DIR__ . '/system/dialogs.php', 'dialogs' => require __DIR__ . '/system/dialogs.php',
'views' => require __DIR__ . '/system/views.php' 'views' => require __DIR__ . '/system/views.php'
]; ];
}; };

View file

@ -1,43 +1,86 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Panel\Field; use Kirby\Panel\Field;
use Kirby\Toolkit\I18n;
return [ return [
// license registration // license key
'registration' => [ 'license' => [
'load' => function () { 'load' => function () {
return [ $license = App::instance()->system()->license();
'component' => 'k-form-dialog',
'props' => [ // @codeCoverageIgnoreStart
'fields' => [ // the system is registered but the license
'license' => [ // key is only visible for admins
'label' => t('license.register.label'), if ($license === true) {
'type' => 'text', $license = 'Kirby 3';
'required' => true, }
'counter' => false, // @codeCoverageIgnoreEnd
'placeholder' => 'K3-',
'help' => t('license.register.help') return [
], 'component' => 'k-form-dialog',
'email' => Field::email([ 'props' => [
'required' => true 'size' => 'medium',
]) 'fields' => [
], 'license' => [
'submitButton' => t('license.register'), 'type' => 'info',
'value' => [ 'label' => I18n::translate('license'),
'license' => null, 'text' => $license ? $license : I18n::translate('license.unregistered.label'),
'email' => null 'theme' => $license ? 'code' : 'negative',
] 'help' => $license ?
] // @codeCoverageIgnoreStart
]; '<a href="https://hub.getkirby.com">' . I18n::translate('license.manage') . ' &rarr;</a>' :
}, // @codeCoverageIgnoreEnd
'submit' => function () { '<a href="https://getkirby.com/buy">' . I18n::translate('license.buy') . ' &rarr;</a>'
// @codeCoverageIgnoreStart ]
kirby()->system()->register(get('license'), get('email')); ],
return [ 'submitButton' => false,
'event' => 'system.register', 'cancelButton' => false,
'message' => t('license.register.success') ]
]; ];
// @codeCoverageIgnoreEnd }
} ],
], // license registration
'registration' => [
'load' => function () {
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'license' => [
'label' => I18n::translate('license.register.label'),
'type' => 'text',
'required' => true,
'counter' => false,
'placeholder' => 'K3-',
'help' => I18n::translate('license.register.help')
],
'email' => Field::email([
'required' => true
])
],
'submitButton' => I18n::translate('license.register'),
'value' => [
'license' => null,
'email' => null
]
]
];
},
'submit' => function () {
// @codeCoverageIgnoreStart
$kirby = App::instance();
$kirby->system()->register(
$kirby->request()->get('license'),
$kirby->request()->get('email')
);
return [
'event' => 'system.register',
'message' => I18n::translate('license.register.success')
];
// @codeCoverageIgnoreEnd
}
],
]; ];

View file

@ -1,47 +1,55 @@
<?php <?php
use Kirby\Http\Server; use Kirby\Cms\App;
return [ return [
'system' => [ 'system' => [
'pattern' => 'system', 'pattern' => 'system',
'action' => function () { 'action' => function () {
$kirby = kirby(); $kirby = App::instance();
$system = $kirby->system(); $system = $kirby->system();
$license = $system->license(); $license = $system->license();
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
if ($license === true) { if ($license === true) {
// valid license, but user is not admin // valid license, but user is not admin
$license = 'Kirby 3'; $license = 'Kirby 3';
} elseif ($license === false) { } elseif ($license === false) {
// no valid license // no valid license
$license = null; $license = null;
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
$plugins = $system->plugins()->values(function ($plugin) { $plugins = $system->plugins()->values(function ($plugin) {
return [ return [
'author' => $plugin->authorsNames(), 'author' => $plugin->authorsNames(),
'license' => $plugin->license(), 'license' => $plugin->license(),
'link' => $plugin->link(), 'name' => [
'name' => $plugin->name(), 'text' => $plugin->name(),
'version' => $plugin->version(), 'href' => $plugin->link(),
]; ],
}); 'version' => $plugin->version(),
];
});
return [ return [
'component' => 'k-system-view', 'component' => 'k-system-view',
'props' => [ 'props' => [
'debug' => $kirby->option('debug', false), 'debug' => $kirby->option('debug', false),
'license' => $license, 'license' => $license,
'plugins' => $plugins, 'plugins' => $plugins,
'php' => phpversion(), 'php' => phpversion(),
'server' => $system->serverSoftware(), 'server' => $system->serverSoftware(),
'https' => Server::https(), 'https' => $kirby->environment()->https(),
'version' => $kirby->version(), 'version' => $kirby->version(),
] 'urls' => [
]; 'content' => $system->exposedFileUrl('content'),
} 'git' => $system->exposedFileUrl('git'),
], 'kirby' => $system->exposedFileUrl('kirby'),
'site' => $system->exposedFileUrl('site')
]
]
];
}
],
]; ];

View file

@ -1,14 +1,16 @@
<?php <?php
use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'users', 'icon' => 'users',
'label' => t('view.users'), 'label' => I18n::translate('view.users'),
'search' => 'users', 'search' => 'users',
'menu' => true, 'menu' => true,
'dialogs' => require __DIR__ . '/users/dialogs.php', 'dialogs' => require __DIR__ . '/users/dialogs.php',
'dropdowns' => require __DIR__ . '/users/dropdowns.php', 'dropdowns' => require __DIR__ . '/users/dropdowns.php',
'searches' => require __DIR__ . '/users/searches.php', 'searches' => require __DIR__ . '/users/searches.php',
'views' => require __DIR__ . '/users/views.php' 'views' => require __DIR__ . '/users/views.php'
]; ];
}; };

View file

@ -1,295 +1,311 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Cms\Find; use Kirby\Cms\Find;
use Kirby\Cms\UserRules; use Kirby\Cms\UserRules;
use Kirby\Exception\InvalidArgumentException; use Kirby\Exception\InvalidArgumentException;
use Kirby\Panel\Field; use Kirby\Panel\Field;
use Kirby\Panel\Panel; use Kirby\Panel\Panel;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
$files = require __DIR__ . '/../files/dialogs.php'; $files = require __DIR__ . '/../files/dialogs.php';
return [ return [
// create // create
'user.create' => [ 'user.create' => [
'pattern' => 'users/create', 'pattern' => 'users/create',
'load' => function () { 'load' => function () {
$kirby = kirby(); $kirby = App::instance();
return [ return [
'component' => 'k-form-dialog', 'component' => 'k-form-dialog',
'props' => [ 'props' => [
'fields' => [ 'fields' => [
'name' => Field::username(), 'name' => Field::username(),
'email' => Field::email([ 'email' => Field::email([
'link' => false, 'link' => false,
'required' => true 'required' => true
]), ]),
'password' => Field::password(), 'password' => Field::password(),
'translation' => Field::translation([ 'translation' => Field::translation([
'required' => true 'required' => true
]), ]),
'role' => Field::role([ 'role' => Field::role([
'required' => true 'required' => true
]) ])
], ],
'submitButton' => t('create'), 'submitButton' => I18n::translate('create'),
'value' => [ 'value' => [
'name' => '', 'name' => '',
'email' => '', 'email' => '',
'password' => '', 'password' => '',
'translation' => $kirby->panelLanguage(), 'translation' => $kirby->panelLanguage(),
'role' => $kirby->user()->role()->name() 'role' => $kirby->user()->role()->name()
] ]
] ]
]; ];
}, },
'submit' => function () { 'submit' => function () {
kirby()->users()->create([ $kirby = App::instance();
'name' => get('name'),
'email' => get('email'),
'password' => get('password'),
'language' => get('translation'),
'role' => get('role')
]);
return [
'event' => 'user.create'
];
}
],
// change email $kirby->users()->create([
'user.changeEmail' => [ 'name' => $kirby->request()->get('name'),
'pattern' => 'users/(:any)/changeEmail', 'email' => $kirby->request()->get('email'),
'load' => function (string $id) { 'password' => $kirby->request()->get('password'),
$user = Find::user($id); 'language' => $kirby->request()->get('translation'),
'role' => $kirby->request()->get('role')
]);
return [ return [
'component' => 'k-form-dialog', 'event' => 'user.create'
'props' => [ ];
'fields' => [ }
'email' => [ ],
'label' => t('email'),
'required' => true,
'type' => 'email',
'preselect' => true
]
],
'submitButton' => t('change'),
'value' => [
'email' => $user->email()
]
]
];
},
'submit' => function (string $id) {
Find::user($id)->changeEmail(get('email'));
return [
'event' => 'user.changeEmail'
];
}
],
// change language // change email
'user.changeLanguage' => [ 'user.changeEmail' => [
'pattern' => 'users/(:any)/changeLanguage', 'pattern' => 'users/(:any)/changeEmail',
'load' => function (string $id) { 'load' => function (string $id) {
$user = Find::user($id); $user = Find::user($id);
return [ return [
'component' => 'k-form-dialog', 'component' => 'k-form-dialog',
'props' => [ 'props' => [
'fields' => [ 'fields' => [
'translation' => Field::translation(['required' => true]) 'email' => [
], 'label' => I18n::translate('email'),
'submitButton' => t('change'), 'required' => true,
'value' => [ 'type' => 'email',
'translation' => $user->language() 'preselect' => true
] ]
] ],
]; 'submitButton' => I18n::translate('change'),
}, 'value' => [
'submit' => function (string $id) { 'email' => $user->email()
Find::user($id)->changeLanguage(get('translation')); ]
]
];
},
'submit' => function (string $id) {
$request = App::instance()->request();
return [ Find::user($id)->changeEmail($request->get('email'));
'event' => 'user.changeLanguage',
'reload' => [
'globals' => '$translation'
]
];
}
],
// change name return [
'user.changeName' => [ 'event' => 'user.changeEmail'
'pattern' => 'users/(:any)/changeName', ];
'load' => function (string $id) { }
$user = Find::user($id); ],
return [ // change language
'component' => 'k-form-dialog', 'user.changeLanguage' => [
'props' => [ 'pattern' => 'users/(:any)/changeLanguage',
'fields' => [ 'load' => function (string $id) {
'name' => Field::username([ $user = Find::user($id);
'preselect' => true
])
],
'submitButton' => t('rename'),
'value' => [
'name' => $user->name()->value()
]
]
];
},
'submit' => function (string $id) {
Find::user($id)->changeName(get('name'));
return [ return [
'event' => 'user.changeName' 'component' => 'k-form-dialog',
]; 'props' => [
} 'fields' => [
], 'translation' => Field::translation(['required' => true])
],
'submitButton' => I18n::translate('change'),
'value' => [
'translation' => $user->language()
]
]
];
},
'submit' => function (string $id) {
$request = App::instance()->request();
// change password Find::user($id)->changeLanguage($request->get('translation'));
'user.changePassword' => [
'pattern' => 'users/(:any)/changePassword',
'load' => function (string $id) {
$user = Find::user($id);
return [ return [
'component' => 'k-form-dialog', 'event' => 'user.changeLanguage',
'props' => [ 'reload' => [
'fields' => [ 'globals' => '$translation'
'password' => Field::password([ ]
'label' => t('user.changePassword.new'), ];
]), }
'passwordConfirmation' => Field::password([ ],
'label' => t('user.changePassword.new.confirm'),
])
],
'submitButton' => t('change'),
]
];
},
'submit' => function (string $id) {
$user = Find::user($id);
$password = get('password');
$passwordConfirmation = get('passwordConfirmation');
// validate the password // change name
UserRules::validPassword($user, $password ?? ''); 'user.changeName' => [
'pattern' => 'users/(:any)/changeName',
'load' => function (string $id) {
$user = Find::user($id);
// compare passwords return [
if ($password !== $passwordConfirmation) { 'component' => 'k-form-dialog',
throw new InvalidArgumentException([ 'props' => [
'key' => 'user.password.notSame' 'fields' => [
]); 'name' => Field::username([
} 'preselect' => true
])
],
'submitButton' => I18n::translate('rename'),
'value' => [
'name' => $user->name()->value()
]
]
];
},
'submit' => function (string $id) {
$request = App::instance()->request();
// change password if everything's fine Find::user($id)->changeName($request->get('name'));
$user->changePassword($password);
return [ return [
'event' => 'user.changePassword' 'event' => 'user.changeName'
]; ];
} }
], ],
// change role // change password
'user.changeRole' => [ 'user.changePassword' => [
'pattern' => 'users/(:any)/changeRole', 'pattern' => 'users/(:any)/changePassword',
'load' => function (string $id) { 'load' => function (string $id) {
$user = Find::user($id); $user = Find::user($id);
return [ return [
'component' => 'k-form-dialog', 'component' => 'k-form-dialog',
'props' => [ 'props' => [
'fields' => [ 'fields' => [
'role' => Field::role([ 'password' => Field::password([
'label' => t('user.changeRole.select'), 'label' => I18n::translate('user.changePassword.new'),
'required' => true, ]),
]) 'passwordConfirmation' => Field::password([
], 'label' => I18n::translate('user.changePassword.new.confirm'),
'submitButton' => t('user.changeRole'), ])
'value' => [ ],
'role' => $user->role()->name() 'submitButton' => I18n::translate('change'),
] ]
] ];
]; },
}, 'submit' => function (string $id) {
'submit' => function (string $id) { $request = App::instance()->request();
$user = Find::user($id)->changeRole(get('role'));
return [ $user = Find::user($id);
'event' => 'user.changeRole', $password = $request->get('password');
'user' => $user->toArray() $passwordConfirmation = $request->get('passwordConfirmation');
];
}
],
// delete // validate the password
'user.delete' => [ UserRules::validPassword($user, $password ?? '');
'pattern' => 'users/(:any)/delete',
'load' => function (string $id) {
$user = Find::user($id);
$i18nPrefix = $user->isLoggedIn() ? 'account' : 'user';
return [ // compare passwords
'component' => 'k-remove-dialog', if ($password !== $passwordConfirmation) {
'props' => [ throw new InvalidArgumentException([
'text' => tt($i18nPrefix . '.delete.confirm', [ 'key' => 'user.password.notSame'
'email' => Escape::html($user->email()) ]);
]) }
]
];
},
'submit' => function (string $id) {
$user = Find::user($id);
$redirect = false;
$referrer = Panel::referrer();
$url = $user->panel()->url(true);
$user->delete(); // change password if everything's fine
$user->changePassword($password);
// redirect to the users view return [
// if the dialog has been opened in the user view 'event' => 'user.changePassword'
if ($referrer === $url) { ];
$redirect = '/users'; }
} ],
// logout the user if they deleted themselves // change role
if ($user->isLoggedIn()) { 'user.changeRole' => [
$redirect = '/logout'; 'pattern' => 'users/(:any)/changeRole',
} 'load' => function (string $id) {
$user = Find::user($id);
return [ return [
'event' => 'user.delete', 'component' => 'k-form-dialog',
'dispatch' => ['content/remove' => [$url]], 'props' => [
'redirect' => $redirect 'fields' => [
]; 'role' => Field::role([
} 'label' => I18n::translate('user.changeRole.select'),
], 'required' => true,
])
],
'submitButton' => I18n::translate('user.changeRole'),
'value' => [
'role' => $user->role()->name()
]
]
];
},
'submit' => function (string $id) {
$request = App::instance()->request();
// change file name $user = Find::user($id)->changeRole($request->get('role'));
'user.file.changeName' => [
'pattern' => '(users/.*?)/files/(:any)/changeName',
'load' => $files['changeName']['load'],
'submit' => $files['changeName']['submit'],
],
// change file sort return [
'user.file.changeSort' => [ 'event' => 'user.changeRole',
'pattern' => '(users/.*?)/files/(:any)/changeSort', 'user' => $user->toArray()
'load' => $files['changeSort']['load'], ];
'submit' => $files['changeSort']['submit'], }
], ],
// delete file // delete
'user.file.delete' => [ 'user.delete' => [
'pattern' => '(users/.*?)/files/(:any)/delete', 'pattern' => 'users/(:any)/delete',
'load' => $files['delete']['load'], 'load' => function (string $id) {
'submit' => $files['delete']['submit'], $user = Find::user($id);
] $i18nPrefix = $user->isLoggedIn() ? 'account' : 'user';
return [
'component' => 'k-remove-dialog',
'props' => [
'text' => I18n::template($i18nPrefix . '.delete.confirm', [
'email' => Escape::html($user->email())
])
]
];
},
'submit' => function (string $id) {
$user = Find::user($id);
$redirect = false;
$referrer = Panel::referrer();
$url = $user->panel()->url(true);
$user->delete();
// redirect to the users view
// if the dialog has been opened in the user view
if ($referrer === $url) {
$redirect = '/users';
}
// logout the user if they deleted themselves
if ($user->isLoggedIn()) {
$redirect = '/logout';
}
return [
'event' => 'user.delete',
'dispatch' => ['content/remove' => [$url]],
'redirect' => $redirect
];
}
],
// change file name
'user.file.changeName' => [
'pattern' => '(users/.*?)/files/(:any)/changeName',
'load' => $files['changeName']['load'],
'submit' => $files['changeName']['submit'],
],
// change file sort
'user.file.changeSort' => [
'pattern' => '(users/.*?)/files/(:any)/changeSort',
'load' => $files['changeSort']['load'],
'submit' => $files['changeSort']['submit'],
],
// delete file
'user.file.delete' => [
'pattern' => '(users/.*?)/files/(:any)/delete',
'load' => $files['delete']['load'],
'submit' => $files['delete']['submit'],
]
]; ];

View file

@ -5,14 +5,14 @@ use Kirby\Cms\Find;
$files = require __DIR__ . '/../files/dropdowns.php'; $files = require __DIR__ . '/../files/dropdowns.php';
return [ return [
'user' => [ 'user' => [
'pattern' => 'users/(:any)', 'pattern' => 'users/(:any)',
'options' => function (string $id) { 'options' => function (string $id) {
return Find::user($id)->panel()->dropdown(); return Find::user($id)->panel()->dropdown();
} }
], ],
'user.file' => [ 'user.file' => [
'pattern' => '(users/.*?)/files/(:any)', 'pattern' => '(users/.*?)/files/(:any)',
'options' => $files['file'] 'options' => $files['file']
] ]
]; ];

View file

@ -1,25 +1,27 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
return [ return [
'users' => [ 'users' => [
'label' => t('users'), 'label' => I18n::translate('users'),
'icon' => 'users', 'icon' => 'users',
'query' => function (string $query = null) { 'query' => function (string $query = null) {
$users = kirby()->users()->search($query)->limit(10); $users = App::instance()->users()->search($query)->limit(10);
$results = []; $results = [];
foreach ($users as $user) { foreach ($users as $user) {
$results[] = [ $results[] = [
'image' => $user->panel()->image(), 'image' => $user->panel()->image(),
'text' => Escape::html($user->username()), 'text' => Escape::html($user->username()),
'link' => $user->panel()->url(true), 'link' => $user->panel()->url(true),
'info' => Escape::html($user->role()->title()) 'info' => Escape::html($user->role()->title())
]; ];
} }
return $results; return $results;
} }
] ]
]; ];

View file

@ -1,65 +1,66 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Cms\Find; use Kirby\Cms\Find;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
return [ return [
'users' => [ 'users' => [
'pattern' => 'users', 'pattern' => 'users',
'action' => function () { 'action' => function () {
$kirby = kirby(); $kirby = App::instance();
$role = get('role'); $role = $kirby->request()->get('role');
$roles = $kirby->roles()->toArray(fn ($role) => [ $roles = $kirby->roles()->toArray(fn ($role) => [
'id' => $role->id(), 'id' => $role->id(),
'title' => $role->title(), 'title' => $role->title(),
]); ]);
return [ return [
'component' => 'k-users-view', 'component' => 'k-users-view',
'props' => [ 'props' => [
'role' => function () use ($kirby, $roles, $role) { 'role' => function () use ($kirby, $roles, $role) {
if ($role) { if ($role) {
return $roles[$role] ?? null; return $roles[$role] ?? null;
} }
}, },
'roles' => array_values($roles), 'roles' => array_values($roles),
'users' => function () use ($kirby, $role) { 'users' => function () use ($kirby, $role) {
$users = $kirby->users(); $users = $kirby->users();
if (empty($role) === false) { if (empty($role) === false) {
$users = $users->role($role); $users = $users->role($role);
} }
$users = $users->paginate([ $users = $users->paginate([
'limit' => 20, 'limit' => 20,
'page' => get('page') 'page' => $kirby->request()->get('page')
]); ]);
return [ return [
'data' => $users->values(fn ($user) => [ 'data' => $users->values(fn ($user) => [
'id' => $user->id(), 'id' => $user->id(),
'image' => $user->panel()->image(), 'image' => $user->panel()->image(),
'info' => Escape::html($user->role()->title()), 'info' => Escape::html($user->role()->title()),
'link' => $user->panel()->url(true), 'link' => $user->panel()->url(true),
'text' => Escape::html($user->username()) 'text' => Escape::html($user->username())
]), ]),
'pagination' => $users->pagination()->toArray() 'pagination' => $users->pagination()->toArray()
]; ];
}, },
] ]
]; ];
} }
], ],
'user' => [ 'user' => [
'pattern' => 'users/(:any)', 'pattern' => 'users/(:any)',
'action' => function (string $id) { 'action' => function (string $id) {
return Find::user($id)->panel()->view(); return Find::user($id)->panel()->view();
} }
], ],
'user.file' => [ 'user.file' => [
'pattern' => 'users/(:any)/files/(:any)', 'pattern' => 'users/(:any)/files/(:any)',
'action' => function (string $id, string $filename) { 'action' => function (string $id, string $filename) {
return Find::file('users/' . $id, $filename)->panel()->view(); return Find::file('users/' . $id, $filename)->panel()->view();
} }
], ],
]; ];

View file

@ -5,6 +5,7 @@ fields:
images: images:
label: field.blocks.gallery.images.label label: field.blocks.gallery.images.label
type: files type: files
query: model.images
multiple: true multiple: true
layout: cards layout: cards
size: tiny size: tiny

View file

@ -9,17 +9,17 @@ $ratio = $block->ratio()->or('auto');
$src = null; $src = null;
if ($block->location() == 'web') { if ($block->location() == 'web') {
$src = $block->src()->esc(); $src = $block->src()->esc();
} elseif ($image = $block->image()->toFile()) { } elseif ($image = $block->image()->toFile()) {
$alt = $alt ?? $image->alt(); $alt = $alt ?? $image->alt();
$src = $image->url(); $src = $image->url();
} }
?> ?>
<?php if ($src): ?> <?php if ($src): ?>
<figure<?= attr(['data-ratio' => $ratio, 'data-crop' => $crop], ' ') ?>> <figure<?= Html::attr(['data-ratio' => $ratio, 'data-crop' => $crop], null, ' ') ?>>
<?php if ($link->isNotEmpty()): ?> <?php if ($link->isNotEmpty()): ?>
<a href="<?= esc($link->toUrl()) ?>"> <a href="<?= Str::esc($link->toUrl()) ?>">
<img src="<?= $src ?>" alt="<?= $alt->esc() ?>"> <img src="<?= $src ?>" alt="<?= $alt->esc() ?>">
</a> </a>
<?php else: ?> <?php else: ?>

View file

@ -13,6 +13,7 @@ fields:
image: image:
label: field.blocks.image.name label: field.blocks.image.name
type: files type: files
query: model.images
multiple: false multiple: false
image: image:
back: black back: black

View file

@ -1,5 +1,9 @@
<?php /** @var \Kirby\Cms\Block $block */ ?> <?php
<?php if ($video = video($block->url())): ?> use Kirby\Cms\Html;
/** @var \Kirby\Cms\Block $block */
?>
<?php if ($video = Html::video($block->url())): ?>
<figure> <figure>
<?= $video ?> <?= $video ?>
<?php if ($block->caption()->isNotEmpty()): ?> <?php if ($block->caption()->isNotEmpty()): ?>

View file

@ -4,12 +4,12 @@ use Kirby\Cms\App;
use Kirby\Cms\Collection; use Kirby\Cms\Collection;
use Kirby\Cms\File; use Kirby\Cms\File;
use Kirby\Cms\FileVersion; use Kirby\Cms\FileVersion;
use Kirby\Cms\Helpers;
use Kirby\Cms\Template; use Kirby\Cms\Template;
use Kirby\Data\Data; use Kirby\Data\Data;
use Kirby\Email\PHPMailer as Emailer; use Kirby\Email\PHPMailer as Emailer;
use Kirby\Filesystem\F; use Kirby\Filesystem\F;
use Kirby\Filesystem\Filename; use Kirby\Filesystem\Filename;
use Kirby\Http\Server;
use Kirby\Http\Uri; use Kirby\Http\Uri;
use Kirby\Http\Url; use Kirby\Http\Url;
use Kirby\Image\Darkroom; use Kirby\Image\Darkroom;
@ -21,380 +21,394 @@ use Kirby\Toolkit\Tpl as Snippet;
return [ return [
/** /**
* Used by the `css()` helper * Used by the `css()` helper
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string $url Relative or absolute URL * @param string $url Relative or absolute URL
* @param string|array $options An array of attributes for the link tag or a media attribute string * @param string|array $options An array of attributes for the link tag or a media attribute string
*/ */
'css' => fn (App $kirby, string $url, $options = null): string => $url, 'css' => fn (App $kirby, string $url, $options = null): string => $url,
/** /**
* Object and variable dumper * Object and variable dumper
* to help with debugging. * to help with debugging.
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param mixed $variable * @param mixed $variable
* @param bool $echo * @param bool $echo
* @return string * @return string
*/ *
'dump' => function (App $kirby, $variable, bool $echo = true) { * @deprecated 3.7.0 Disable `dump()` via `KIRBY_HELPER_DUMP` instead and create your own function
if (Server::cli() === true) { * @todo move to `Helpers::dump()`, remove component in 3.8.0
$output = print_r($variable, true) . PHP_EOL; */
} else { 'dump' => function (App $kirby, $variable, bool $echo = true) {
$output = '<pre>' . print_r($variable, true) . '</pre>'; if ($kirby->environment()->cli() === true) {
} $output = print_r($variable, true) . PHP_EOL;
} else {
$output = '<pre>' . print_r($variable, true) . '</pre>';
}
if ($echo === true) { if ($echo === true) {
echo $output; echo $output;
} }
return $output; return $output;
}, },
/** /**
* Add your own email provider * Add your own email provider
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param array $props * @param array $props
* @param bool $debug * @param bool $debug
*/ */
'email' => function (App $kirby, array $props = [], bool $debug = false) { 'email' => function (App $kirby, array $props = [], bool $debug = false) {
return new Emailer($props, $debug); return new Emailer($props, $debug);
}, },
/** /**
* Modify URLs for file objects * Modify URLs for file objects
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param \Kirby\Cms\File $file The original file object * @param \Kirby\Cms\File $file The original file object
* @return string * @return string
*/ */
'file::url' => function (App $kirby, File $file): string { 'file::url' => function (App $kirby, File $file): string {
return $file->mediaUrl(); return $file->mediaUrl();
}, },
/** /**
* Adapt file characteristics * Adapt file characteristics
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param \Kirby\Cms\File|\Kirby\Filesystem\Asset $file The file object * @param \Kirby\Cms\File|\Kirby\Filesystem\Asset $file The file object
* @param array $options All thumb options (width, height, crop, blur, grayscale) * @param array $options All thumb options (width, height, crop, blur, grayscale)
* @return \Kirby\Cms\File|\Kirby\Cms\FileVersion|\Kirby\Filesystem\Asset * @return \Kirby\Cms\File|\Kirby\Cms\FileVersion|\Kirby\Filesystem\Asset
*/ */
'file::version' => function (App $kirby, $file, array $options = []) { 'file::version' => function (App $kirby, $file, array $options = []) {
// if file is not resizable, return // if file is not resizable, return
if ($file->isResizable() === false) { if ($file->isResizable() === false) {
return $file; return $file;
} }
// create url and root // create url and root
$mediaRoot = dirname($file->mediaRoot()); $mediaRoot = dirname($file->mediaRoot());
$template = $mediaRoot . '/{{ name }}{{ attributes }}.{{ extension }}'; $template = $mediaRoot . '/{{ name }}{{ attributes }}.{{ extension }}';
$thumbRoot = (new Filename($file->root(), $template, $options))->toString(); $thumbRoot = (new Filename($file->root(), $template, $options))->toString();
$thumbName = basename($thumbRoot); $thumbName = basename($thumbRoot);
// check if the thumb already exists // check if the thumb already exists
if (file_exists($thumbRoot) === false) { if (file_exists($thumbRoot) === false) {
// if not, create job file
$job = $mediaRoot . '/.jobs/' . $thumbName . '.json';
// if not, create job file try {
$job = $mediaRoot . '/.jobs/' . $thumbName . '.json'; Data::write($job, array_merge($options, [
'filename' => $file->filename()
]));
} catch (Throwable $e) {
// if thumb doesn't exist yet and job file cannot
// be created, return
return $file;
}
}
try { return new FileVersion([
Data::write($job, array_merge($options, [ 'modifications' => $options,
'filename' => $file->filename() 'original' => $file,
])); 'root' => $thumbRoot,
} catch (Throwable $e) { 'url' => dirname($file->mediaUrl()) . '/' . $thumbName,
// if thumb doesn't exist yet and job file cannot ]);
// be created, return },
return $file;
}
}
return new FileVersion([ /**
'modifications' => $options, * Used by the `js()` helper
'original' => $file, *
'root' => $thumbRoot, * @param \Kirby\Cms\App $kirby Kirby instance
'url' => dirname($file->mediaUrl()) . '/' . $thumbName, * @param string $url Relative or absolute URL
]); * @param string|array $options An array of attributes for the link tag or a media attribute string
}, */
'js' => fn (App $kirby, string $url, $options = null): string => $url,
/** /**
* Used by the `js()` helper * Add your own Markdown parser
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string $url Relative or absolute URL * @param string $text Text to parse
* @param string|array $options An array of attributes for the link tag or a media attribute string * @param array $options Markdown options
*/ * @param bool $inline Whether to wrap the text in `<p>` tags (deprecated: set via $options['inline'] instead)
'js' => fn (App $kirby, string $url, $options = null): string => $url, * @return string
* @todo remove $inline parameter in in 3.8.0
*/
'markdown' => function (App $kirby, string $text = null, array $options = [], bool $inline = false): string {
static $markdown;
static $config;
/** // warning for deprecated fourth parameter
* Add your own Markdown parser if (func_num_args() === 4 && isset($options['inline']) === false) {
* // @codeCoverageIgnoreStart
* @param \Kirby\Cms\App $kirby Kirby instance Helpers::deprecated('markdown component: the $inline parameter is deprecated and will be removed in Kirby 3.8.0. Use $options[\'inline\'] instead.');
* @param string $text Text to parse // @codeCoverageIgnoreEnd
* @param array $options Markdown options }
* @param bool $inline Whether to wrap the text in `<p>` tags (deprecated: set via $options['inline'] instead)
* @return string
* @todo add deprecation warning for $inline parameter in 3.7.0
* @todo remove $inline parameter in in 3.8.0
*/
'markdown' => function (App $kirby, string $text = null, array $options = [], bool $inline = false): string {
static $markdown;
static $config;
// support for the deprecated fourth argument // support for the deprecated fourth argument
$options['inline'] ??= $inline; $options['inline'] ??= $inline;
// if the config options have changed or the component is called for the first time, // if the config options have changed or the component is called for the first time,
// (re-)initialize the parser object // (re-)initialize the parser object
if ($config !== $options) { if ($config !== $options) {
$markdown = new Markdown($options); $markdown = new Markdown($options);
$config = $options; $config = $options;
} }
return $markdown->parse($text, $options['inline']); return $markdown->parse($text, $options['inline'] ?? false);
}, },
/** /**
* Add your own search engine * Add your own search engine
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param \Kirby\Cms\Collection $collection Collection of searchable models * @param \Kirby\Cms\Collection $collection Collection of searchable models
* @param string $query * @param string $query
* @param mixed $params * @param mixed $params
* @return \Kirby\Cms\Collection|bool * @return \Kirby\Cms\Collection|bool
*/ */
'search' => function (App $kirby, Collection $collection, string $query = null, $params = []) { 'search' => function (App $kirby, Collection $collection, string $query = null, $params = []) {
if (empty(trim($query)) === true) { if (empty(trim($query ?? '')) === true) {
return $collection->limit(0); return $collection->limit(0);
} }
if (is_string($params) === true) { if (is_string($params) === true) {
$params = ['fields' => Str::split($params, '|')]; $params = ['fields' => Str::split($params, '|')];
} }
$defaults = [ $defaults = [
'fields' => [], 'fields' => [],
'minlength' => 2, 'minlength' => 2,
'score' => [], 'score' => [],
'words' => false, 'words' => false,
]; ];
$options = array_merge($defaults, $params); $options = array_merge($defaults, $params);
$collection = clone $collection; $collection = clone $collection;
$searchWords = preg_replace('/(\s)/u', ',', $query); $searchWords = preg_replace('/(\s)/u', ',', $query);
$searchWords = Str::split($searchWords, ',', $options['minlength']); $searchWords = Str::split($searchWords, ',', $options['minlength']);
$lowerQuery = Str::lower($query); $lowerQuery = Str::lower($query);
$exactQuery = $options['words'] ? '(\b' . preg_quote($query) . '\b)' : preg_quote($query); $exactQuery = $options['words'] ? '(\b' . preg_quote($query) . '\b)' : preg_quote($query);
if (empty($options['stopwords']) === false) { if (empty($options['stopwords']) === false) {
$searchWords = array_diff($searchWords, $options['stopwords']); $searchWords = array_diff($searchWords, $options['stopwords']);
} }
$searchWords = array_map(function ($value) use ($options) { $searchWords = array_map(function ($value) use ($options) {
return $options['words'] ? '\b' . preg_quote($value) . '\b' : preg_quote($value); return $options['words'] ? '\b' . preg_quote($value) . '\b' : preg_quote($value);
}, $searchWords); }, $searchWords);
$preg = '!(' . implode('|', $searchWords) . ')!i'; $preg = '!(' . implode('|', $searchWords) . ')!i';
$results = $collection->filter(function ($item) use ($query, $preg, $options, $lowerQuery, $exactQuery) { $results = $collection->filter(function ($item) use ($query, $preg, $options, $lowerQuery, $exactQuery) {
$data = $item->content()->toArray(); $data = $item->content()->toArray();
$keys = array_keys($data); $keys = array_keys($data);
$keys[] = 'id'; $keys[] = 'id';
if (is_a($item, 'Kirby\Cms\User') === true) { if (is_a($item, 'Kirby\Cms\User') === true) {
$keys[] = 'name'; $keys[] = 'name';
$keys[] = 'email'; $keys[] = 'email';
$keys[] = 'role'; $keys[] = 'role';
} elseif (is_a($item, 'Kirby\Cms\Page') === true) { } elseif (is_a($item, 'Kirby\Cms\Page') === true) {
// apply the default score for pages // apply the default score for pages
$options['score'] = array_merge([ $options['score'] = array_merge([
'id' => 64, 'id' => 64,
'title' => 64, 'title' => 64,
], $options['score']); ], $options['score']);
} }
if (empty($options['fields']) === false) { if (empty($options['fields']) === false) {
$fields = array_map('strtolower', $options['fields']); $fields = array_map('strtolower', $options['fields']);
$keys = array_intersect($keys, $fields); $keys = array_intersect($keys, $fields);
} }
$item->searchHits = 0; $item->searchHits = 0;
$item->searchScore = 0; $item->searchScore = 0;
foreach ($keys as $key) { foreach ($keys as $key) {
$score = $options['score'][$key] ?? 1; $score = $options['score'][$key] ?? 1;
$value = $data[$key] ?? (string)$item->$key(); $value = $data[$key] ?? (string)$item->$key();
$lowerValue = Str::lower($value); $lowerValue = Str::lower($value);
// check for exact matches // check for exact matches
if ($lowerQuery == $lowerValue) { if ($lowerQuery == $lowerValue) {
$item->searchScore += 16 * $score; $item->searchScore += 16 * $score;
$item->searchHits += 1; $item->searchHits += 1;
// check for exact beginning matches // check for exact beginning matches
} elseif ($options['words'] === false && Str::startsWith($lowerValue, $lowerQuery) === true) { } elseif ($options['words'] === false && Str::startsWith($lowerValue, $lowerQuery) === true) {
$item->searchScore += 8 * $score; $item->searchScore += 8 * $score;
$item->searchHits += 1; $item->searchHits += 1;
// check for exact query matches // check for exact query matches
} elseif ($matches = preg_match_all('!' . $exactQuery . '!i', $value, $r)) { } elseif ($matches = preg_match_all('!' . $exactQuery . '!i', $value, $r)) {
$item->searchScore += 2 * $score; $item->searchScore += 2 * $score;
$item->searchHits += $matches; $item->searchHits += $matches;
} }
// check for any match // check for any match
if ($matches = preg_match_all($preg, $value, $r)) { if ($matches = preg_match_all($preg, $value, $r)) {
$item->searchHits += $matches; $item->searchHits += $matches;
$item->searchScore += $matches * $score; $item->searchScore += $matches * $score;
} }
} }
return $item->searchHits > 0; return $item->searchHits > 0;
}); });
return $results->sort('searchScore', 'desc'); return $results->sort('searchScore', 'desc');
}, },
/** /**
* Add your own SmartyPants parser * Add your own SmartyPants parser
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string $text Text to parse * @param string $text Text to parse
* @param array $options SmartyPants options * @param array $options SmartyPants options
* @return string * @return string
*/ */
'smartypants' => function (App $kirby, string $text = null, array $options = []): string { 'smartypants' => function (App $kirby, string $text = null, array $options = []): string {
static $smartypants; static $smartypants;
static $config; static $config;
// if the config options have changed or the component is called for the first time, // if the config options have changed or the component is called for the first time,
// (re-)initialize the parser object // (re-)initialize the parser object
if ($config !== $options) { if ($config !== $options) {
$smartypants = new Smartypants($options); $smartypants = new Smartypants($options);
$config = $options; $config = $options;
} }
return $smartypants->parse($text); return $smartypants->parse($text);
}, },
/** /**
* Add your own snippet loader * Add your own snippet loader
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string|array $name Snippet name * @param string|array $name Snippet name
* @param array $data Data array for the snippet * @param array $data Data array for the snippet
* @return string|null * @return string|null
*/ */
'snippet' => function (App $kirby, $name, array $data = []): ?string { 'snippet' => function (App $kirby, $name, array $data = []): ?string {
$snippets = A::wrap($name); $snippets = A::wrap($name);
foreach ($snippets as $name) { foreach ($snippets as $name) {
$name = (string)$name; $name = (string)$name;
$file = $kirby->root('snippets') . '/' . $name . '.php'; $file = $kirby->root('snippets') . '/' . $name . '.php';
if (file_exists($file) === false) { if (file_exists($file) === false) {
$file = $kirby->extensions('snippets')[$name] ?? null; $file = $kirby->extensions('snippets')[$name] ?? null;
} }
if ($file) { if ($file) {
break; break;
} }
} }
return Snippet::load($file, $data); return Snippet::load($file, $data);
}, },
/** /**
* Add your own template engine * Add your own template engine
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string $name Template name * @param string $name Template name
* @param string $type Extension type * @param string $type Extension type
* @param string $defaultType Default extension type * @param string $defaultType Default extension type
* @return \Kirby\Cms\Template * @return \Kirby\Cms\Template
*/ */
'template' => function (App $kirby, string $name, string $type = 'html', string $defaultType = 'html') { 'template' => function (App $kirby, string $name, string $type = 'html', string $defaultType = 'html') {
return new Template($name, $type, $defaultType); return new Template($name, $type, $defaultType);
}, },
/** /**
* Add your own thumb generator * Add your own thumb generator
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string $src Root of the original file * @param string $src Root of the original file
* @param string $dst Template string for the root to the desired destination * @param string $dst Template string for the root to the desired destination
* @param array $options All thumb options that should be applied: `width`, `height`, `crop`, `blur`, `grayscale` * @param array $options All thumb options that should be applied: `width`, `height`, `crop`, `blur`, `grayscale`
* @return string * @return string
*/ */
'thumb' => function (App $kirby, string $src, string $dst, array $options): string { 'thumb' => function (App $kirby, string $src, string $dst, array $options): string {
$darkroom = Darkroom::factory( $darkroom = Darkroom::factory(
option('thumbs.driver', 'gd'), $kirby->option('thumbs.driver', 'gd'),
option('thumbs', []) $kirby->option('thumbs', [])
); );
$options = $darkroom->preprocess($src, $options); $options = $darkroom->preprocess($src, $options);
$root = (new Filename($src, $dst, $options))->toString(); $root = (new Filename($src, $dst, $options))->toString();
F::copy($src, $root, true); F::copy($src, $root, true);
$darkroom->process($root, $options); $darkroom->process($root, $options);
return $root; return $root;
}, },
/** /**
* Modify all URLs * Modify all URLs
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string|null $path URL path * @param string|null $path URL path
* @param array|string|null $options Array of options for the Uri class * @param array|string|null $options Array of options for the Uri class
* @return string * @return string
*/ */
'url' => function (App $kirby, string $path = null, $options = null): string { 'url' => function (App $kirby, string $path = null, $options = null): string {
$language = null; $language = null;
// get language from simple string option // get language from simple string option
if (is_string($options) === true) { if (is_string($options) === true) {
$language = $options; $language = $options;
$options = null; $options = null;
} }
// get language from array // get language from array
if (is_array($options) === true && isset($options['language']) === true) { if (is_array($options) === true && isset($options['language']) === true) {
$language = $options['language']; $language = $options['language'];
unset($options['language']); unset($options['language']);
} }
// get a language url for the linked page, if the page can be found // get a language url for the linked page, if the page can be found
if ($kirby->multilang() === true) { if ($kirby->multilang() === true) {
$parts = Str::split($path, '#'); $parts = Str::split($path, '#');
if ($page = page($parts[0] ?? null)) { if ($parts[0] ?? null) {
$path = $page->url($language); $page = $kirby->site()->find($parts[0]);
} else {
$page = $kirby->site()->page();
}
if (isset($parts[1]) === true) { if ($page) {
$path .= '#' . $parts[1]; $path = $page->url($language);
}
}
}
// keep relative urls if (isset($parts[1]) === true) {
if ( $path .= '#' . $parts[1];
$path !== null && }
(substr($path, 0, 2) === './' || substr($path, 0, 3) === '../') }
) { }
return $path;
}
$url = Url::makeAbsolute($path, $kirby->url()); // keep relative urls
if (
$path !== null &&
(substr($path, 0, 2) === './' || substr($path, 0, 3) === '../')
) {
return $path;
}
if ($options === null) { $url = Url::makeAbsolute($path, $kirby->url());
return $url;
}
return (new Uri($url, $options))->toString(); if ($options === null) {
}, return $url;
}
return (new Uri($url, $options))->toString();
},
]; ];

View file

@ -4,58 +4,58 @@ use Kirby\Toolkit\A;
use Kirby\Toolkit\Str; use Kirby\Toolkit\Str;
return [ return [
'mixins' => ['min', 'options'], 'mixins' => ['min', 'options'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'before' => null, 'before' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
/** /**
* Arranges the checkboxes in the given number of columns * Arranges the checkboxes in the given number of columns
*/ */
'columns' => function (int $columns = 1) { 'columns' => function (int $columns = 1) {
return $columns; return $columns;
}, },
/** /**
* Default value for the field, which will be used when a page/file/user is created * Default value for the field, which will be used when a page/file/user is created
*/ */
'default' => function ($default = null) { 'default' => function ($default = null) {
return Str::split($default, ','); return Str::split($default, ',');
}, },
/** /**
* Maximum number of checked boxes * Maximum number of checked boxes
*/ */
'max' => function (int $max = null) { 'max' => function (int $max = null) {
return $max; return $max;
}, },
/** /**
* Minimum number of checked boxes * Minimum number of checked boxes
*/ */
'min' => function (int $min = null) { 'min' => function (int $min = null) {
return $min; return $min;
}, },
'value' => function ($value = null) { 'value' => function ($value = null) {
return Str::split($value, ','); return Str::split($value, ',');
}, },
], ],
'computed' => [ 'computed' => [
'default' => function () { 'default' => function () {
return $this->sanitizeOptions($this->default); return $this->sanitizeOptions($this->default);
}, },
'value' => function () { 'value' => function () {
return $this->sanitizeOptions($this->value); return $this->sanitizeOptions($this->value);
}, },
], ],
'save' => function ($value): string { 'save' => function ($value): string {
return A::join($value, ', '); return A::join($value, ', ');
}, },
'validations' => [ 'validations' => [
'options', 'options',
'max', 'max',
'min' 'min'
] ]
]; ];

View file

@ -7,148 +7,148 @@ use Kirby\Toolkit\I18n;
use Kirby\Toolkit\Str; use Kirby\Toolkit\Str;
return [ return [
'mixins' => ['datetime'], 'mixins' => ['datetime'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'placeholder' => null, 'placeholder' => null,
/** /**
* Activate/deactivate the dropdown calendar * Activate/deactivate the dropdown calendar
*/ */
'calendar' => function (bool $calendar = true) { 'calendar' => function (bool $calendar = true) {
return $calendar; return $calendar;
}, },
/** /**
* Default date when a new page/file/user gets created * Default date when a new page/file/user gets created
*/ */
'default' => function (string $default = null): string { 'default' => function (string $default = null): string {
return $this->toDatetime($default) ?? ''; return $this->toDatetime($default) ?? '';
}, },
/** /**
* Custom format (dayjs tokens: `DD`, `MM`, `YYYY`) that is * Custom format (dayjs tokens: `DD`, `MM`, `YYYY`) that is
* used to display the field in the Panel * used to display the field in the Panel
*/ */
'display' => function ($display = 'YYYY-MM-DD') { 'display' => function ($display = 'YYYY-MM-DD') {
return I18n::translate($display, $display); return I18n::translate($display, $display);
}, },
/** /**
* Changes the calendar icon to something custom * Changes the calendar icon to something custom
*/ */
'icon' => function (string $icon = 'calendar') { 'icon' => function (string $icon = 'calendar') {
return $icon; return $icon;
}, },
/** /**
* Latest date, which can be selected/saved (Y-m-d) * Latest date, which can be selected/saved (Y-m-d)
*/ */
'max' => function (string $max = null): ?string { 'max' => function (string $max = null): ?string {
return Date::optional($max); return Date::optional($max);
}, },
/** /**
* Earliest date, which can be selected/saved (Y-m-d) * Earliest date, which can be selected/saved (Y-m-d)
*/ */
'min' => function (string $min = null): ?string { 'min' => function (string $min = null): ?string {
return Date::optional($min); return Date::optional($min);
}, },
/** /**
* Round to the nearest: sub-options for `unit` (day) and `size` (1) * Round to the nearest: sub-options for `unit` (day) and `size` (1)
*/ */
'step' => function ($step = null) { 'step' => function ($step = null) {
return $step; return $step;
}, },
/** /**
* Pass `true` or an array of time field options to show the time selector. * Pass `true` or an array of time field options to show the time selector.
*/ */
'time' => function ($time = false) { 'time' => function ($time = false) {
return $time; return $time;
}, },
/** /**
* Must be a parseable date string * Must be a parseable date string
*/ */
'value' => function ($value = null) { 'value' => function ($value = null) {
return $value; return $value;
} }
], ],
'computed' => [ 'computed' => [
'display' => function () { 'display' => function () {
if ($this->display) { if ($this->display) {
return Str::upper($this->display); return Str::upper($this->display);
} }
}, },
'format' => function () { 'format' => function () {
return $this->props['format'] ?? ($this->time === false ? 'Y-m-d' : 'Y-m-d H:i:s'); return $this->props['format'] ?? ($this->time === false ? 'Y-m-d' : 'Y-m-d H:i:s');
}, },
'time' => function () { 'time' => function () {
if ($this->time === false) { if ($this->time === false) {
return false; return false;
} }
$props = is_array($this->time) ? $this->time : []; $props = is_array($this->time) ? $this->time : [];
$props['model'] = $this->model(); $props['model'] = $this->model();
$field = new Field('time', $props); $field = new Field('time', $props);
return $field->toArray(); return $field->toArray();
}, },
'step' => function () { 'step' => function () {
if ($this->time === false || empty($this->time['step']) === true) { if ($this->time === false || empty($this->time['step']) === true) {
return Date::stepConfig($this->step, [ return Date::stepConfig($this->step, [
'size' => 1, 'size' => 1,
'unit' => 'day' 'unit' => 'day'
]); ]);
} }
return Date::stepConfig($this->time['step'], [ return Date::stepConfig($this->time['step'], [
'size' => 5, 'size' => 5,
'unit' => 'minute' 'unit' => 'minute'
]); ]);
}, },
'value' => function (): string { 'value' => function (): string {
return $this->toDatetime($this->value) ?? ''; return $this->toDatetime($this->value) ?? '';
}, },
], ],
'validations' => [ 'validations' => [
'date', 'date',
'minMax' => function ($value) { 'minMax' => function ($value) {
if (!$value = Date::optional($value)) { if (!$value = Date::optional($value)) {
return true; return true;
} }
$min = Date::optional($this->min); $min = Date::optional($this->min);
$max = Date::optional($this->max); $max = Date::optional($this->max);
$format = $this->time === false ? 'd.m.Y' : 'd.m.Y H:i'; $format = $this->time === false ? 'd.m.Y' : 'd.m.Y H:i';
if ($min && $max && $value->isBetween($min, $max) === false) { if ($min && $max && $value->isBetween($min, $max) === false) {
throw new Exception([ throw new Exception([
'key' => 'validation.date.between', 'key' => 'validation.date.between',
'data' => [ 'data' => [
'min' => $min->format($format), 'min' => $min->format($format),
'max' => $min->format($format) 'max' => $min->format($format)
] ]
]); ]);
} elseif ($min && $value->isMin($min) === false) { } elseif ($min && $value->isMin($min) === false) {
throw new Exception([ throw new Exception([
'key' => 'validation.date.after', 'key' => 'validation.date.after',
'data' => [ 'data' => [
'date' => $min->format($format), 'date' => $min->format($format),
] ]
]); ]);
} elseif ($max && $value->isMax($max) === false) { } elseif ($max && $value->isMax($max) === false) {
throw new Exception([ throw new Exception([
'key' => 'validation.date.before', 'key' => 'validation.date.before',
'data' => [ 'data' => [
'date' => $max->format($format), 'date' => $max->format($format),
] ]
]); ]);
} }
return true; return true;
}, },
] ]
]; ];

View file

@ -3,38 +3,38 @@
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'extends' => 'text', 'extends' => 'text',
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'converter' => null, 'converter' => null,
'counter' => null, 'counter' => null,
/** /**
* Sets the HTML5 autocomplete mode for the input * Sets the HTML5 autocomplete mode for the input
*/ */
'autocomplete' => function (string $autocomplete = 'email') { 'autocomplete' => function (string $autocomplete = 'email') {
return $autocomplete; return $autocomplete;
}, },
/** /**
* Changes the email icon to something custom * Changes the email icon to something custom
*/ */
'icon' => function (string $icon = 'email') { 'icon' => function (string $icon = 'email') {
return $icon; return $icon;
}, },
/** /**
* Custom placeholder text, when the field is empty. * Custom placeholder text, when the field is empty.
*/ */
'placeholder' => function ($value = null) { 'placeholder' => function ($value = null) {
return I18n::translate($value, $value) ?? I18n::translate('email.placeholder'); return I18n::translate($value, $value) ?? I18n::translate('email.placeholder');
} }
], ],
'validations' => [ 'validations' => [
'minlength', 'minlength',
'maxlength', 'maxlength',
'email' 'email'
] ]
]; ];

View file

@ -4,128 +4,128 @@ use Kirby\Data\Data;
use Kirby\Toolkit\A; use Kirby\Toolkit\A;
return [ return [
'mixins' => [ 'mixins' => [
'filepicker', 'filepicker',
'layout', 'layout',
'min', 'min',
'picker', 'picker',
'upload' 'upload'
], ],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'before' => null, 'before' => null,
'autofocus' => null, 'autofocus' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
/** /**
* Sets the file(s), which are selected by default when a new page is created * Sets the file(s), which are selected by default when a new page is created
*/ */
'default' => function ($default = null) { 'default' => function ($default = null) {
return $default; return $default;
}, },
'value' => function ($value = null) { 'value' => function ($value = null) {
return $value; return $value;
} }
], ],
'computed' => [ 'computed' => [
'parentModel' => function () { 'parentModel' => function () {
if (is_string($this->parent) === true && $model = $this->model()->query($this->parent, 'Kirby\Cms\Model')) { if (is_string($this->parent) === true && $model = $this->model()->query($this->parent, 'Kirby\Cms\Model')) {
return $model; return $model;
} }
return $this->model(); return $this->model();
}, },
'parent' => function () { 'parent' => function () {
return $this->parentModel->apiUrl(true); return $this->parentModel->apiUrl(true);
}, },
'query' => function () { 'query' => function () {
return $this->query ?? $this->parentModel::CLASS_ALIAS . '.files'; return $this->query ?? $this->parentModel::CLASS_ALIAS . '.files';
}, },
'default' => function () { 'default' => function () {
return $this->toFiles($this->default); return $this->toFiles($this->default);
}, },
'value' => function () { 'value' => function () {
return $this->toFiles($this->value); return $this->toFiles($this->value);
}, },
], ],
'methods' => [ 'methods' => [
'fileResponse' => function ($file) { 'fileResponse' => function ($file) {
return $file->panel()->pickerData([ return $file->panel()->pickerData([
'image' => $this->image, 'image' => $this->image,
'info' => $this->info ?? false, 'info' => $this->info ?? false,
'layout' => $this->layout, 'layout' => $this->layout,
'model' => $this->model(), 'model' => $this->model(),
'text' => $this->text, 'text' => $this->text,
]); ]);
}, },
'toFiles' => function ($value = null) { 'toFiles' => function ($value = null) {
$files = []; $files = [];
foreach (Data::decode($value, 'yaml') as $id) { foreach (Data::decode($value, 'yaml') as $id) {
if (is_array($id) === true) { if (is_array($id) === true) {
$id = $id['id'] ?? null; $id = $id['id'] ?? null;
} }
if ($id !== null && ($file = $this->kirby()->file($id, $this->model()))) { if ($id !== null && ($file = $this->kirby()->file($id, $this->model()))) {
$files[] = $this->fileResponse($file); $files[] = $this->fileResponse($file);
} }
} }
return $files; return $files;
} }
], ],
'api' => function () { 'api' => function () {
return [ return [
[ [
'pattern' => '/', 'pattern' => '/',
'action' => function () { 'action' => function () {
$field = $this->field(); $field = $this->field();
return $field->filepicker([ return $field->filepicker([
'image' => $field->image(), 'image' => $field->image(),
'info' => $field->info(), 'info' => $field->info(),
'layout' => $field->layout(), 'layout' => $field->layout(),
'limit' => $field->limit(), 'limit' => $field->limit(),
'page' => $this->requestQuery('page'), 'page' => $this->requestQuery('page'),
'query' => $field->query(), 'query' => $field->query(),
'search' => $this->requestQuery('search'), 'search' => $this->requestQuery('search'),
'text' => $field->text() 'text' => $field->text()
]); ]);
} }
], ],
[ [
'pattern' => 'upload', 'pattern' => 'upload',
'method' => 'POST', 'method' => 'POST',
'action' => function () { 'action' => function () {
$field = $this->field(); $field = $this->field();
$uploads = $field->uploads(); $uploads = $field->uploads();
// move_uploaded_file() not working with unit test // move_uploaded_file() not working with unit test
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
return $field->upload($this, $uploads, function ($file, $parent) use ($field) { return $field->upload($this, $uploads, function ($file, $parent) use ($field) {
return $file->panel()->pickerData([ return $file->panel()->pickerData([
'image' => $field->image(), 'image' => $field->image(),
'info' => $field->info(), 'info' => $field->info(),
'layout' => $field->layout(), 'layout' => $field->layout(),
'model' => $field->model(), 'model' => $field->model(),
'text' => $field->text(), 'text' => $field->text(),
]); ]);
}); });
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} }
] ]
]; ];
}, },
'save' => function ($value = null) { 'save' => function ($value = null) {
return A::pluck($value, 'uuid'); return A::pluck($value, 'uuid');
}, },
'validations' => [ 'validations' => [
'max', 'max',
'min' 'min'
] ]
]; ];

View file

@ -1,5 +1,5 @@
<?php <?php
return [ return [
'save' => false 'save' => false
]; ];

View file

@ -1,26 +1,26 @@
<?php <?php
return [ return [
'save' => false, 'save' => false,
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'autofocus' => null, 'autofocus' => null,
'before' => null, 'before' => null,
'default' => null, 'default' => null,
'disabled' => null, 'disabled' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
'required' => null, 'required' => null,
'translate' => null, 'translate' => null,
/** /**
* If `false`, the prepended number will be hidden * If `false`, the prepended number will be hidden
*/ */
'numbered' => function (bool $numbered = true) { 'numbered' => function (bool $numbered = true) {
return $numbered; return $numbered;
} }
] ]
]; ];

View file

@ -3,42 +3,42 @@
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'autofocus' => null, 'autofocus' => null,
'before' => null, 'before' => null,
'default' => null, 'default' => null,
'disabled' => null, 'disabled' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
'required' => null, 'required' => null,
'translate' => null, 'translate' => null,
/** /**
* Text to be displayed * Text to be displayed
*/ */
'text' => function ($value = null) { 'text' => function ($value = null) {
return I18n::translate($value, $value); return I18n::translate($value, $value);
}, },
/** /**
* Change the design of the info box * Change the design of the info box
*/ */
'theme' => function (string $theme = null) { 'theme' => function (string $theme = null) {
return $theme; return $theme;
} }
], ],
'computed' => [ 'computed' => [
'text' => function () { 'text' => function () {
if ($text = $this->text) { if ($text = $this->text) {
$text = $this->model()->toSafeString($text); $text = $this->model()->toSafeString($text);
$text = $this->kirby()->kirbytext($text); $text = $this->kirby()->kirbytext($text);
return $text; return $text;
} }
} }
], ],
'save' => false, 'save' => false,
]; ];

View file

@ -1,5 +1,5 @@
<?php <?php
return [ return [
'save' => false 'save' => false
]; ];

View file

@ -1,17 +1,17 @@
<?php <?php
return [ return [
'props' => [ 'props' => [
/** /**
* Sets the allowed HTML formats. Available formats: `bold`, `italic`, `underline`, `strike`, `code`, `link`. Activate them all by passing `true`. Deactivate them all by passing `false` * Sets the allowed HTML formats. Available formats: `bold`, `italic`, `underline`, `strike`, `code`, `link`. Activate them all by passing `true`. Deactivate them all by passing `false`
*/ */
'marks' => function ($marks = true) { 'marks' => function ($marks = true) {
return $marks; return $marks;
} }
], ],
'computed' => [ 'computed' => [
'value' => function () { 'value' => function () {
return trim($this->value ?? ''); return trim($this->value ?? '');
} }
] ]
]; ];

View file

@ -3,33 +3,33 @@
use Kirby\Toolkit\Date; use Kirby\Toolkit\Date;
return [ return [
'props' => [ 'props' => [
/** /**
* Defines a custom format that is used when the field is saved * Defines a custom format that is used when the field is saved
*/ */
'format' => function (string $format = null) { 'format' => function (string $format = null) {
return $format; return $format;
} }
], ],
'methods' => [ 'methods' => [
'toDatetime' => function ($value, string $format = 'Y-m-d H:i:s') { 'toDatetime' => function ($value, string $format = 'Y-m-d H:i:s') {
if ($date = Date::optional($value)) { if ($date = Date::optional($value)) {
if ($this->step) { if ($this->step) {
$step = Date::stepConfig($this->step); $step = Date::stepConfig($this->step);
$date->round($step['unit'], $step['size']); $date->round($step['unit'], $step['size']);
} }
return $date->format($format); return $date->format($format);
} }
return null; return null;
} }
], ],
'save' => function ($value) { 'save' => function ($value) {
if ($date = Date::optional($value)) { if ($date = Date::optional($value)) {
return $date->format($this->format); return $date->format($this->format);
} }
return ''; return '';
}, },
]; ];

View file

@ -3,12 +3,12 @@
use Kirby\Cms\FilePicker; use Kirby\Cms\FilePicker;
return [ return [
'methods' => [ 'methods' => [
'filepicker' => function (array $params = []) { 'filepicker' => function (array $params = []) {
// fetch the parent model // fetch the parent model
$params['model'] = $this->model(); $params['model'] = $this->model();
return (new FilePicker($params))->toArray(); return (new FilePicker($params))->toArray();
} }
] ]
]; ];

View file

@ -1,21 +1,21 @@
<?php <?php
return [ return [
'props' => [ 'props' => [
/** /**
* Changes the layout of the selected entries. * Changes the layout of the selected entries.
* Available layouts: `list`, `cardlets`, `cards` * Available layouts: `list`, `cardlets`, `cards`
*/ */
'layout' => function (string $layout = 'list') { 'layout' => function (string $layout = 'list') {
$layouts = ['list', 'cardlets', 'cards']; $layouts = ['list', 'cardlets', 'cards'];
return in_array($layout, $layouts) ? $layout : 'list'; return in_array($layout, $layouts) ? $layout : 'list';
}, },
/** /**
* Layout size for cards: `tiny`, `small`, `medium`, `large` or `huge` * Layout size for cards: `tiny`, `small`, `medium`, `large` or `huge`
*/ */
'size' => function (string $size = 'auto') { 'size' => function (string $size = 'auto') {
return $size; return $size;
}, },
] ]
]; ];

View file

@ -1,22 +1,22 @@
<?php <?php
return [ return [
'computed' => [ 'computed' => [
'min' => function () { 'min' => function () {
// set min to at least 1, if required // set min to at least 1, if required
if ($this->required === true) { if ($this->required === true) {
return $this->min ?? 1; return $this->min ?? 1;
} }
return $this->min; return $this->min;
}, },
'required' => function () { 'required' => function () {
// set required to true if min is set // set required to true if min is set
if ($this->min) { if ($this->min) {
return true; return true;
} }
return $this->required; return $this->required;
} }
] ]
]; ];

View file

@ -3,46 +3,46 @@
use Kirby\Form\Options; use Kirby\Form\Options;
return [ return [
'props' => [ 'props' => [
/** /**
* API settings for options requests. This will only take affect when `options` is set to `api`. * API settings for options requests. This will only take affect when `options` is set to `api`.
*/ */
'api' => function ($api = null) { 'api' => function ($api = null) {
return $api; return $api;
}, },
/** /**
* An array with options * An array with options
*/ */
'options' => function ($options = []) { 'options' => function ($options = []) {
return $options; return $options;
}, },
/** /**
* Query settings for options queries. This will only take affect when `options` is set to `query`. * Query settings for options queries. This will only take affect when `options` is set to `query`.
*/ */
'query' => function ($query = null) { 'query' => function ($query = null) {
return $query; return $query;
}, },
], ],
'computed' => [ 'computed' => [
'options' => function (): array { 'options' => function (): array {
return $this->getOptions(); return $this->getOptions();
} }
], ],
'methods' => [ 'methods' => [
'getOptions' => function () { 'getOptions' => function () {
return Options::factory( return Options::factory(
$this->options(), $this->options(),
$this->props, $this->props,
$this->model() $this->model()
); );
}, },
'sanitizeOption' => function ($option) { 'sanitizeOption' => function ($option) {
$allowed = array_column($this->options(), 'value'); $allowed = array_column($this->options(), 'value');
return in_array($option, $allowed, true) === true ? $option : null; return in_array($option, $allowed, true) === true ? $option : null;
}, },
'sanitizeOptions' => function ($options) { 'sanitizeOptions' => function ($options) {
$allowed = array_column($this->options(), 'value'); $allowed = array_column($this->options(), 'value');
return array_intersect($options, $allowed); return array_intersect($options, $allowed);
}, },
] ]
]; ];

View file

@ -3,12 +3,12 @@
use Kirby\Cms\PagePicker; use Kirby\Cms\PagePicker;
return [ return [
'methods' => [ 'methods' => [
'pagepicker' => function (array $params = []) { 'pagepicker' => function (array $params = []) {
// inject the current model // inject the current model
$params['model'] = $this->model(); $params['model'] = $this->model();
return (new PagePicker($params))->toArray(); return (new PagePicker($params))->toArray();
} }
] ]
]; ];

View file

@ -3,76 +3,76 @@
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'props' => [ 'props' => [
/** /**
* The placeholder text if none have been selected yet * The placeholder text if none have been selected yet
*/ */
'empty' => function ($empty = null) { 'empty' => function ($empty = null) {
return I18n::translate($empty, $empty); return I18n::translate($empty, $empty);
}, },
/** /**
* Image settings for each item * Image settings for each item
*/ */
'image' => function ($image = null) { 'image' => function ($image = null) {
return $image; return $image;
}, },
/** /**
* Info text for each item * Info text for each item
*/ */
'info' => function (string $info = null) { 'info' => function (string $info = null) {
return $info; return $info;
}, },
/** /**
* Whether each item should be clickable * Whether each item should be clickable
*/ */
'link' => function (bool $link = true) { 'link' => function (bool $link = true) {
return $link; return $link;
}, },
/** /**
* The minimum number of required selected * The minimum number of required selected
*/ */
'min' => function (int $min = null) { 'min' => function (int $min = null) {
return $min; return $min;
}, },
/** /**
* The maximum number of allowed selected * The maximum number of allowed selected
*/ */
'max' => function (int $max = null) { 'max' => function (int $max = null) {
return $max; return $max;
}, },
/** /**
* If `false`, only a single one can be selected * If `false`, only a single one can be selected
*/ */
'multiple' => function (bool $multiple = true) { 'multiple' => function (bool $multiple = true) {
return $multiple; return $multiple;
}, },
/** /**
* Query for the items to be included in the picker * Query for the items to be included in the picker
*/ */
'query' => function (string $query = null) { 'query' => function (string $query = null) {
return $query; return $query;
}, },
/** /**
* Enable/disable the search field in the picker * Enable/disable the search field in the picker
*/ */
'search' => function (bool $search = true) { 'search' => function (bool $search = true) {
return $search; return $search;
}, },
/** /**
* Main text for each item * Main text for each item
*/ */
'text' => function (string $text = null) { 'text' => function (string $text = null) {
return $text; return $text;
}, },
], ],
]; ];

View file

@ -5,69 +5,69 @@ use Kirby\Cms\File;
use Kirby\Exception\Exception; use Kirby\Exception\Exception;
return [ return [
'props' => [ 'props' => [
/** /**
* Sets the upload options for linked files (since 3.2.0) * Sets the upload options for linked files (since 3.2.0)
*/ */
'uploads' => function ($uploads = []) { 'uploads' => function ($uploads = []) {
if ($uploads === false) { if ($uploads === false) {
return false; return false;
} }
if (is_string($uploads) === true) { if (is_string($uploads) === true) {
$uploads = ['template' => $uploads]; $uploads = ['template' => $uploads];
} }
if (is_array($uploads) === false) { if (is_array($uploads) === false) {
$uploads = []; $uploads = [];
} }
$template = $uploads['template'] ?? null; $template = $uploads['template'] ?? null;
if ($template) { if ($template) {
$file = new File([ $file = new File([
'filename' => 'tmp', 'filename' => 'tmp',
'parent' => $this->model(), 'parent' => $this->model(),
'template' => $template 'template' => $template
]); ]);
$uploads['accept'] = $file->blueprint()->acceptMime(); $uploads['accept'] = $file->blueprint()->acceptMime();
} else { } else {
$uploads['accept'] = '*'; $uploads['accept'] = '*';
} }
return $uploads; return $uploads;
}, },
], ],
'methods' => [ 'methods' => [
'upload' => function (Api $api, $params, Closure $map) { 'upload' => function (Api $api, $params, Closure $map) {
if ($params === false) { if ($params === false) {
throw new Exception('Uploads are disabled for this field'); throw new Exception('Uploads are disabled for this field');
} }
if ($parentQuery = ($params['parent'] ?? null)) { if ($parentQuery = ($params['parent'] ?? null)) {
$parent = $this->model()->query($parentQuery); $parent = $this->model()->query($parentQuery);
} else { } else {
$parent = $this->model(); $parent = $this->model();
} }
if (is_a($parent, 'Kirby\Cms\File') === true) { if (is_a($parent, 'Kirby\Cms\File') === true) {
$parent = $parent->parent(); $parent = $parent->parent();
} }
return $api->upload(function ($source, $filename) use ($parent, $params, $map) { return $api->upload(function ($source, $filename) use ($parent, $params, $map) {
$file = $parent->createFile([ $file = $parent->createFile([
'source' => $source, 'source' => $source,
'template' => $params['template'] ?? null, 'template' => $params['template'] ?? null,
'filename' => $filename, 'filename' => $filename,
]); ]);
if (is_a($file, 'Kirby\Cms\File') === false) { if (is_a($file, 'Kirby\Cms\File') === false) {
throw new Exception('The file could not be uploaded'); throw new Exception('The file could not be uploaded');
} }
return $map($file, $parent); return $map($file, $parent);
}); });
} }
] ]
]; ];

View file

@ -3,11 +3,11 @@
use Kirby\Cms\UserPicker; use Kirby\Cms\UserPicker;
return [ return [
'methods' => [ 'methods' => [
'userpicker' => function (array $params = []) { 'userpicker' => function (array $params = []) {
$params['model'] = $this->model(); $params['model'] = $this->model();
return (new UserPicker($params))->toArray(); return (new UserPicker($params))->toArray();
} }
] ]
]; ];

View file

@ -1,32 +1,32 @@
<?php <?php
return [ return [
'extends' => 'tags', 'extends' => 'tags',
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'accept' => null, 'accept' => null,
/** /**
* Custom icon to replace the arrow down. * Custom icon to replace the arrow down.
*/ */
'icon' => function (string $icon = null) { 'icon' => function (string $icon = null) {
return $icon; return $icon;
}, },
/** /**
* Enable/disable the search in the dropdown * Enable/disable the search in the dropdown
* Also limit displayed items (display: 20) * Also limit displayed items (display: 20)
* and set minimum number of characters to search (min: 3) * and set minimum number of characters to search (min: 3)
*/ */
'search' => function ($search = true) { 'search' => function ($search = true) {
return $search; return $search;
}, },
/** /**
* If `true`, selected entries will be sorted * If `true`, selected entries will be sorted
* according to their position in the dropdown * according to their position in the dropdown
*/ */
'sort' => function (bool $sort = false) { 'sort' => function (bool $sort = false) {
return $sort; return $sort;
}, },
] ]
]; ];

View file

@ -3,46 +3,46 @@
use Kirby\Toolkit\Str; use Kirby\Toolkit\Str;
return [ return [
'props' => [ 'props' => [
/** /**
* Default number that will be saved when a new page/user/file is created * Default number that will be saved when a new page/user/file is created
*/ */
'default' => function ($default = null) { 'default' => function ($default = null) {
return $this->toNumber($default); return $this->toNumber($default);
}, },
/** /**
* The lowest allowed number * The lowest allowed number
*/ */
'min' => function (float $min = null) { 'min' => function (float $min = null) {
return $min; return $min;
}, },
/** /**
* The highest allowed number * The highest allowed number
*/ */
'max' => function (float $max = null) { 'max' => function (float $max = null) {
return $max; return $max;
}, },
/** /**
* Allowed incremental steps between numbers (i.e `0.5`) * Allowed incremental steps between numbers (i.e `0.5`)
*/ */
'step' => function ($step = null) { 'step' => function ($step = null) {
return $this->toNumber($step); return $this->toNumber($step);
}, },
'value' => function ($value = null) { 'value' => function ($value = null) {
return $this->toNumber($value); return $this->toNumber($value);
} }
], ],
'methods' => [ 'methods' => [
'toNumber' => function ($value) { 'toNumber' => function ($value) {
if ($this->isEmpty($value) === true) { if ($this->isEmpty($value) === true) {
return null; return null;
} }
return is_float($value) === true ? $value : (float)Str::float($value); return is_float($value) === true ? $value : (float)Str::float($value);
} }
], ],
'validations' => [ 'validations' => [
'min', 'min',
'max' 'max'
] ]
]; ];

View file

@ -1,110 +1,111 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Data\Data; use Kirby\Data\Data;
use Kirby\Toolkit\A; use Kirby\Toolkit\A;
return [ return [
'mixins' => [ 'mixins' => [
'layout', 'layout',
'min', 'min',
'pagepicker', 'pagepicker',
'picker', 'picker',
], ],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'autofocus' => null, 'autofocus' => null,
'before' => null, 'before' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
/** /**
* Default selected page(s) when a new page/file/user is created * Default selected page(s) when a new page/file/user is created
*/ */
'default' => function ($default = null) { 'default' => function ($default = null) {
return $this->toPages($default); return $this->toPages($default);
}, },
/** /**
* Optional query to select a specific set of pages * Optional query to select a specific set of pages
*/ */
'query' => function (string $query = null) { 'query' => function (string $query = null) {
return $query; return $query;
}, },
/** /**
* Optionally include subpages of pages * Optionally include subpages of pages
*/ */
'subpages' => function (bool $subpages = true) { 'subpages' => function (bool $subpages = true) {
return $subpages; return $subpages;
}, },
'value' => function ($value = null) { 'value' => function ($value = null) {
return $this->toPages($value); return $this->toPages($value);
}, },
], ],
'computed' => [ 'computed' => [
/** /**
* Unset inherited computed * Unset inherited computed
*/ */
'default' => null 'default' => null
], ],
'methods' => [ 'methods' => [
'pageResponse' => function ($page) { 'pageResponse' => function ($page) {
return $page->panel()->pickerData([ return $page->panel()->pickerData([
'image' => $this->image, 'image' => $this->image,
'info' => $this->info, 'info' => $this->info,
'layout' => $this->layout, 'layout' => $this->layout,
'text' => $this->text, 'text' => $this->text,
]); ]);
}, },
'toPages' => function ($value = null) { 'toPages' => function ($value = null) {
$pages = []; $pages = [];
$kirby = kirby(); $kirby = App::instance();
foreach (Data::decode($value, 'yaml') as $id) { foreach (Data::decode($value, 'yaml') as $id) {
if (is_array($id) === true) { if (is_array($id) === true) {
$id = $id['id'] ?? null; $id = $id['id'] ?? null;
} }
if ($id !== null && ($page = $kirby->page($id))) { if ($id !== null && ($page = $kirby->page($id))) {
$pages[] = $this->pageResponse($page); $pages[] = $this->pageResponse($page);
} }
} }
return $pages; return $pages;
} }
], ],
'api' => function () { 'api' => function () {
return [ return [
[ [
'pattern' => '/', 'pattern' => '/',
'action' => function () { 'action' => function () {
$field = $this->field(); $field = $this->field();
return $field->pagepicker([ return $field->pagepicker([
'image' => $field->image(), 'image' => $field->image(),
'info' => $field->info(), 'info' => $field->info(),
'layout' => $field->layout(), 'layout' => $field->layout(),
'limit' => $field->limit(), 'limit' => $field->limit(),
'page' => $this->requestQuery('page'), 'page' => $this->requestQuery('page'),
'parent' => $this->requestQuery('parent'), 'parent' => $this->requestQuery('parent'),
'query' => $field->query(), 'query' => $field->query(),
'search' => $this->requestQuery('search'), 'search' => $this->requestQuery('search'),
'subpages' => $field->subpages(), 'subpages' => $field->subpages(),
'text' => $field->text() 'text' => $field->text()
]); ]);
} }
] ]
]; ];
}, },
'save' => function ($value = null) { 'save' => function ($value = null) {
return A::pluck($value, 'id'); return A::pluck($value, 'id');
}, },
'validations' => [ 'validations' => [
'max', 'max',
'min' 'min'
] ]
]; ];

View file

@ -1,29 +1,29 @@
<?php <?php
return [ return [
'mixins' => ['options'], 'mixins' => ['options'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'before' => null, 'before' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
/** /**
* Arranges the radio buttons in the given number of columns * Arranges the radio buttons in the given number of columns
*/ */
'columns' => function (int $columns = 1) { 'columns' => function (int $columns = 1) {
return $columns; return $columns;
}, },
], ],
'computed' => [ 'computed' => [
'default' => function () { 'default' => function () {
return $this->sanitizeOption($this->default); return $this->sanitizeOption($this->default);
}, },
'value' => function () { 'value' => function () {
return $this->sanitizeOption($this->value) ?? ''; return $this->sanitizeOption($this->value) ?? '';
} }
] ]
]; ];

View file

@ -1,24 +1,24 @@
<?php <?php
return [ return [
'extends' => 'number', 'extends' => 'number',
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'placeholder' => null, 'placeholder' => null,
/** /**
* The maximum value on the slider * The maximum value on the slider
*/ */
'max' => function (float $max = 100) { 'max' => function (float $max = 100) {
return $max; return $max;
}, },
/** /**
* Enables/disables the tooltip and set the before and after values * Enables/disables the tooltip and set the before and after values
*/ */
'tooltip' => function ($tooltip = true) { 'tooltip' => function ($tooltip = true) {
return $tooltip; return $tooltip;
}, },
] ]
]; ];

View file

@ -1,24 +1,24 @@
<?php <?php
return [ return [
'extends' => 'radio', 'extends' => 'radio',
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'columns' => null, 'columns' => null,
/** /**
* Custom icon to replace the arrow down. * Custom icon to replace the arrow down.
*/ */
'icon' => function (string $icon = null) { 'icon' => function (string $icon = null) {
return $icon; return $icon;
}, },
/** /**
* Custom placeholder string for empty option. * Custom placeholder string for empty option.
*/ */
'placeholder' => function (string $placeholder = '—') { 'placeholder' => function (string $placeholder = '—') {
return $placeholder; return $placeholder;
}, },
] ]
]; ];

View file

@ -2,54 +2,54 @@
return [ return [
'extends' => 'text', 'extends' => 'text',
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'converter' => null, 'converter' => null,
'counter' => null, 'counter' => null,
'spellcheck' => null, 'spellcheck' => null,
/** /**
* Set of characters allowed in the slug * Set of characters allowed in the slug
*/ */
'allow' => function (string $allow = '') { 'allow' => function (string $allow = '') {
return $allow; return $allow;
}, },
/** /**
* Changes the link icon * Changes the link icon
*/ */
'icon' => function (string $icon = 'url') { 'icon' => function (string $icon = 'url') {
return $icon; return $icon;
}, },
/** /**
* Set prefix for the help text * Set prefix for the help text
*/ */
'path' => function (string $path = null) { 'path' => function (string $path = null) {
return $path; return $path;
}, },
/** /**
* Name of another field that should be used to * Name of another field that should be used to
* automatically update this field's value * automatically update this field's value
*/ */
'sync' => function (string $sync = null) { 'sync' => function (string $sync = null) {
return $sync; return $sync;
}, },
/** /**
* Set to object with keys `field` and `text` to add * Set to object with keys `field` and `text` to add
* button to generate from another field * button to generate from another field
*/ */
'wizard' => function ($wizard = false) { 'wizard' => function ($wizard = false) {
return $wizard; return $wizard;
} }
], ],
'validations' => [ 'validations' => [
'minlength', 'minlength',
'maxlength' 'maxlength'
], ],
]; ];

View file

@ -5,189 +5,199 @@ use Kirby\Form\Form;
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'mixins' => ['min'], 'mixins' => ['min'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'before' => null, 'before' => null,
'autofocus' => null, 'autofocus' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
/** /**
* Optional columns definition to only show selected fields in the structure table. * Optional columns definition to only show selected fields in the structure table.
*/ */
'columns' => function (array $columns = []) { 'columns' => function (array $columns = []) {
// lower case all keys, because field names will // lower case all keys, because field names will
// be lowercase as well. // be lowercase as well.
return array_change_key_case($columns); return array_change_key_case($columns);
}, },
/** /**
* Toggles duplicating rows for the structure * Toggles duplicating rows for the structure
*/ */
'duplicate' => function (bool $duplicate = true) { 'duplicate' => function (bool $duplicate = true) {
return $duplicate; return $duplicate;
}, },
/** /**
* The placeholder text if no items have been added yet * The placeholder text if no items have been added yet
*/ */
'empty' => function ($empty = null) { 'empty' => function ($empty = null) {
return I18n::translate($empty, $empty); return I18n::translate($empty, $empty);
}, },
/** /**
* Set the default rows for the structure * Set the default rows for the structure
*/ */
'default' => function (array $default = null) { 'default' => function (array $default = null) {
return $default; return $default;
}, },
/** /**
* Fields setup for the structure form. Works just like fields in regular forms. * Fields setup for the structure form. Works just like fields in regular forms.
*/ */
'fields' => function (array $fields) { 'fields' => function (array $fields) {
return $fields; return $fields;
}, },
/** /**
* The number of entries that will be displayed on a single page. Afterwards pagination kicks in. * The number of entries that will be displayed on a single page. Afterwards pagination kicks in.
*/ */
'limit' => function (int $limit = null) { 'limit' => function (int $limit = null) {
return $limit; return $limit;
}, },
/** /**
* Maximum allowed entries in the structure. Afterwards the "Add" button will be switched off. * Maximum allowed entries in the structure. Afterwards the "Add" button will be switched off.
*/ */
'max' => function (int $max = null) { 'max' => function (int $max = null) {
return $max; return $max;
}, },
/** /**
* Minimum required entries in the structure * Minimum required entries in the structure
*/ */
'min' => function (int $min = null) { 'min' => function (int $min = null) {
return $min; return $min;
}, },
/** /**
* Toggles adding to the top or bottom of the list * Toggles adding to the top or bottom of the list
*/ */
'prepend' => function (bool $prepend = null) { 'prepend' => function (bool $prepend = null) {
return $prepend; return $prepend;
}, },
/** /**
* Toggles drag & drop sorting * Toggles drag & drop sorting
*/ */
'sortable' => function (bool $sortable = null) { 'sortable' => function (bool $sortable = null) {
return $sortable; return $sortable;
}, },
/** /**
* Sorts the entries by the given field and order (i.e. `title desc`) * Sorts the entries by the given field and order (i.e. `title desc`)
* Drag & drop is disabled in this case * Drag & drop is disabled in this case
*/ */
'sortBy' => function (string $sort = null) { 'sortBy' => function (string $sort = null) {
return $sort; return $sort;
} }
], ],
'computed' => [ 'computed' => [
'default' => function () { 'default' => function () {
return $this->rows($this->default); return $this->rows($this->default);
}, },
'value' => function () { 'value' => function () {
return $this->rows($this->value); return $this->rows($this->value);
}, },
'fields' => function () { 'fields' => function () {
if (empty($this->fields) === true) { if (empty($this->fields) === true) {
throw new Exception('Please provide some fields for the structure'); throw new Exception('Please provide some fields for the structure');
} }
return $this->form()->fields()->toArray(); return $this->form()->fields()->toArray();
}, },
'columns' => function () { 'columns' => function () {
$columns = []; $columns = [];
$mobile = 0;
if (empty($this->columns)) { if (empty($this->columns)) {
foreach ($this->fields as $field) { foreach ($this->fields as $field) {
// Skip hidden and unsaveable fields
// They should never be included as column
if ($field['type'] === 'hidden' || $field['saveable'] === false) {
continue;
}
// Skip hidden and unsaveable fields $columns[$field['name']] = [
// They should never be included as column 'type' => $field['type'],
if ($field['type'] === 'hidden' || $field['saveable'] === false) { 'label' => $field['label'] ?? $field['name']
continue; ];
} }
} else {
foreach ($this->columns as $columnName => $columnProps) {
if (is_array($columnProps) === false) {
$columnProps = [];
}
$columns[$field['name']] = [ $field = $this->fields[$columnName] ?? null;
'type' => $field['type'],
'label' => $field['label'] ?? $field['name']
];
}
} else {
foreach ($this->columns as $columnName => $columnProps) {
if (is_array($columnProps) === false) {
$columnProps = [];
}
$field = $this->fields[$columnName] ?? null; if (empty($field) === true || $field['saveable'] === false) {
continue;
}
if (empty($field) === true || $field['saveable'] === false) { if (($columnProps['mobile'] ?? false) === true) {
continue; $mobile++;
} }
$columns[$columnName] = array_merge($columnProps, [ $columns[$columnName] = array_merge($columnProps, [
'type' => $field['type'], 'type' => $field['type'],
'label' => $field['label'] ?? $field['name'] 'label' => $field['label'] ?? $field['name']
]); ]);
} }
} }
return $columns; // make the first column visible on mobile
} // if no other mobile columns are defined
], if ($mobile === 0) {
'methods' => [ $columns[array_key_first($columns)]['mobile'] = true;
'rows' => function ($value) { }
$rows = Data::decode($value, 'yaml');
$value = [];
foreach ($rows as $index => $row) { return $columns;
if (is_array($row) === false) { }
continue; ],
} 'methods' => [
'rows' => function ($value) {
$rows = Data::decode($value, 'yaml');
$value = [];
$value[] = $this->form($row)->values(); foreach ($rows as $index => $row) {
} if (is_array($row) === false) {
continue;
}
return $value; $value[] = $this->form($row)->values();
}, }
'form' => function (array $values = []) {
return new Form([
'fields' => $this->attrs['fields'],
'values' => $values,
'model' => $this->model
]);
},
],
'api' => function () {
return [
[
'pattern' => 'validate',
'method' => 'ALL',
'action' => function () {
return array_values($this->field()->form($this->requestBody())->errors());
}
]
];
},
'save' => function ($value) {
$data = [];
foreach ($value as $row) { return $value;
$data[] = $this->form($row)->content(); },
} 'form' => function (array $values = []) {
return new Form([
'fields' => $this->attrs['fields'],
'values' => $values,
'model' => $this->model
]);
},
],
'api' => function () {
return [
[
'pattern' => 'validate',
'method' => 'ALL',
'action' => function () {
return array_values($this->field()->form($this->requestBody())->errors());
}
]
];
},
'save' => function ($value) {
$data = [];
return $data; foreach ($value as $row) {
}, $data[] = $this->form($row)->content();
'validations' => [ }
'min',
'max' return $data;
] },
'validations' => [
'min',
'max'
]
]; ];

View file

@ -5,99 +5,98 @@ use Kirby\Toolkit\Str;
use Kirby\Toolkit\V; use Kirby\Toolkit\V;
return [ return [
'mixins' => ['min', 'options'], 'mixins' => ['min', 'options'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'before' => null, 'before' => null,
'placeholder' => null, 'placeholder' => null,
/** /**
* If set to `all`, any type of input is accepted. If set to `options` only the predefined options are accepted as input. * If set to `all`, any type of input is accepted. If set to `options` only the predefined options are accepted as input.
*/ */
'accept' => function ($value = 'all') { 'accept' => function ($value = 'all') {
return V::in($value, ['all', 'options']) ? $value : 'all'; return V::in($value, ['all', 'options']) ? $value : 'all';
}, },
/** /**
* Changes the tag icon * Changes the tag icon
*/ */
'icon' => function ($icon = 'tag') { 'icon' => function ($icon = 'tag') {
return $icon; return $icon;
}, },
/** /**
* Set to `list` to display each tag with 100% width, * Set to `list` to display each tag with 100% width,
* otherwise the tags are displayed inline * otherwise the tags are displayed inline
*/ */
'layout' => function (?string $layout = null) { 'layout' => function (?string $layout = null) {
return $layout; return $layout;
}, },
/** /**
* Minimum number of required entries/tags * Minimum number of required entries/tags
*/ */
'min' => function (int $min = null) { 'min' => function (int $min = null) {
return $min; return $min;
}, },
/** /**
* Maximum number of allowed entries/tags * Maximum number of allowed entries/tags
*/ */
'max' => function (int $max = null) { 'max' => function (int $max = null) {
return $max; return $max;
}, },
/** /**
* Custom tags separator, which will be used to store tags in the content file * Custom tags separator, which will be used to store tags in the content file
*/ */
'separator' => function (string $separator = ',') { 'separator' => function (string $separator = ',') {
return $separator; return $separator;
}, },
], ],
'computed' => [ 'computed' => [
'default' => function (): array { 'default' => function (): array {
return $this->toTags($this->default); return $this->toTags($this->default);
}, },
'value' => function (): array { 'value' => function (): array {
return $this->toTags($this->value); return $this->toTags($this->value);
} }
], ],
'methods' => [ 'methods' => [
'toTags' => function ($value) { 'toTags' => function ($value) {
if (is_null($value) === true) { if (is_null($value) === true) {
return []; return [];
} }
$options = $this->options(); $options = $this->options();
// transform into value-text objects // transform into value-text objects
return array_map(function ($option) use ($options) { return array_map(function ($option) use ($options) {
// already a valid object
if (is_array($option) === true && isset($option['value'], $option['text']) === true) {
return $option;
}
// already a valid object $index = array_search($option, array_column($options, 'value'));
if (is_array($option) === true && isset($option['value'], $option['text']) === true) {
return $option;
}
$index = array_search($option, array_column($options, 'value')); if ($index !== false) {
return $options[$index];
}
if ($index !== false) { return [
return $options[$index]; 'value' => $option,
} 'text' => $option,
];
return [ }, Str::split($value, $this->separator()));
'value' => $option, }
'text' => $option, ],
]; 'save' => function (array $value = null): string {
}, Str::split($value, $this->separator())); return A::join(
} A::pluck($value, 'value'),
], $this->separator() . ' '
'save' => function (array $value = null): string { );
return A::join( },
A::pluck($value, 'value'), 'validations' => [
$this->separator() . ' ' 'min',
); 'max'
}, ]
'validations' => [
'min',
'max'
]
]; ];

View file

@ -1,27 +1,27 @@
<?php <?php
return [ return [
'extends' => 'text', 'extends' => 'text',
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'converter' => null, 'converter' => null,
'counter' => null, 'counter' => null,
'spellcheck' => null, 'spellcheck' => null,
/** /**
* Sets the HTML5 autocomplete attribute * Sets the HTML5 autocomplete attribute
*/ */
'autocomplete' => function (string $autocomplete = 'tel') { 'autocomplete' => function (string $autocomplete = 'tel') {
return $autocomplete; return $autocomplete;
}, },
/** /**
* Changes the phone icon * Changes the phone icon
*/ */
'icon' => function (string $icon = 'phone') { 'icon' => function (string $icon = 'phone') {
return $icon; return $icon;
} }
] ]
]; ];

View file

@ -4,100 +4,99 @@ use Kirby\Exception\InvalidArgumentException;
use Kirby\Toolkit\Str; use Kirby\Toolkit\Str;
return [ return [
'props' => [ 'props' => [
/** /**
* The field value will be converted with the selected converter before the value gets saved. Available converters: `lower`, `upper`, `ucfirst`, `slug` * The field value will be converted with the selected converter before the value gets saved. Available converters: `lower`, `upper`, `ucfirst`, `slug`
*/ */
'converter' => function ($value = null) { 'converter' => function ($value = null) {
if ($value !== null && in_array($value, array_keys($this->converters())) === false) { if ($value !== null && in_array($value, array_keys($this->converters())) === false) {
throw new InvalidArgumentException([ throw new InvalidArgumentException([
'key' => 'field.converter.invalid', 'key' => 'field.converter.invalid',
'data' => ['converter' => $value] 'data' => ['converter' => $value]
]); ]);
} }
return $value; return $value;
}, },
/** /**
* Shows or hides the character counter in the top right corner * Shows or hides the character counter in the top right corner
*/ */
'counter' => function (bool $counter = true) { 'counter' => function (bool $counter = true) {
return $counter; return $counter;
}, },
/** /**
* Maximum number of allowed characters * Maximum number of allowed characters
*/ */
'maxlength' => function (int $maxlength = null) { 'maxlength' => function (int $maxlength = null) {
return $maxlength; return $maxlength;
}, },
/** /**
* Minimum number of required characters * Minimum number of required characters
*/ */
'minlength' => function (int $minlength = null) { 'minlength' => function (int $minlength = null) {
return $minlength; return $minlength;
}, },
/** /**
* A regular expression, which will be used to validate the input * A regular expression, which will be used to validate the input
*/ */
'pattern' => function (string $pattern = null) { 'pattern' => function (string $pattern = null) {
return $pattern; return $pattern;
}, },
/** /**
* If `false`, spellcheck will be switched off * If `false`, spellcheck will be switched off
*/ */
'spellcheck' => function (bool $spellcheck = false) { 'spellcheck' => function (bool $spellcheck = false) {
return $spellcheck; return $spellcheck;
}, },
], ],
'computed' => [ 'computed' => [
'default' => function () { 'default' => function () {
return $this->convert($this->default); return $this->convert($this->default);
}, },
'value' => function () { 'value' => function () {
return (string)$this->convert($this->value); return (string)$this->convert($this->value);
} }
], ],
'methods' => [ 'methods' => [
'convert' => function ($value) { 'convert' => function ($value) {
if ($this->converter() === null) { if ($this->converter() === null) {
return $value; return $value;
} }
$value = trim($value); $converter = $this->converters()[$this->converter()];
$converter = $this->converters()[$this->converter()];
if (is_array($value) === true) { if (is_array($value) === true) {
return array_map($converter, $value); return array_map($converter, $value);
} }
return call_user_func($converter, $value); return call_user_func($converter, trim($value ?? ''));
}, },
'converters' => function (): array { 'converters' => function (): array {
return [ return [
'lower' => function ($value) { 'lower' => function ($value) {
return Str::lower($value); return Str::lower($value);
}, },
'slug' => function ($value) { 'slug' => function ($value) {
return Str::slug($value); return Str::slug($value);
}, },
'ucfirst' => function ($value) { 'ucfirst' => function ($value) {
return Str::ucfirst($value); return Str::ucfirst($value);
}, },
'upper' => function ($value) { 'upper' => function ($value) {
return Str::upper($value); return Str::upper($value);
}, },
]; ];
}, },
], ],
'validations' => [ 'validations' => [
'minlength', 'minlength',
'maxlength', 'maxlength',
'pattern' 'pattern'
] ]
]; ];

View file

@ -1,123 +1,123 @@
<?php <?php
return [ return [
'mixins' => ['filepicker', 'upload'], 'mixins' => ['filepicker', 'upload'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'before' => null, 'before' => null,
/** /**
* Enables/disables the format buttons. Can either be `true`/`false` or a list of allowed buttons. Available buttons: `headlines`, `italic`, `bold`, `link`, `email`, `file`, `code`, `ul`, `ol` (as well as `|` for a divider) * Enables/disables the format buttons. Can either be `true`/`false` or a list of allowed buttons. Available buttons: `headlines`, `italic`, `bold`, `link`, `email`, `file`, `code`, `ul`, `ol` (as well as `|` for a divider)
*/ */
'buttons' => function ($buttons = true) { 'buttons' => function ($buttons = true) {
return $buttons; return $buttons;
}, },
/** /**
* Enables/disables the character counter in the top right corner * Enables/disables the character counter in the top right corner
*/ */
'counter' => function (bool $counter = true) { 'counter' => function (bool $counter = true) {
return $counter; return $counter;
}, },
/** /**
* Sets the default text when a new page/file/user is created * Sets the default text when a new page/file/user is created
*/ */
'default' => function (string $default = null) { 'default' => function (string $default = null) {
return trim($default ?? ''); return trim($default ?? '');
}, },
/** /**
* Sets the options for the files picker * Sets the options for the files picker
*/ */
'files' => function ($files = []) { 'files' => function ($files = []) {
if (is_string($files) === true) { if (is_string($files) === true) {
return ['query' => $files]; return ['query' => $files];
} }
if (is_array($files) === false) { if (is_array($files) === false) {
$files = []; $files = [];
} }
return $files; return $files;
}, },
/** /**
* Sets the font family (sans or monospace) * Sets the font family (sans or monospace)
*/ */
'font' => function (string $font = null) { 'font' => function (string $font = null) {
return $font === 'monospace' ? 'monospace' : 'sans-serif'; return $font === 'monospace' ? 'monospace' : 'sans-serif';
}, },
/** /**
* Maximum number of allowed characters * Maximum number of allowed characters
*/ */
'maxlength' => function (int $maxlength = null) { 'maxlength' => function (int $maxlength = null) {
return $maxlength; return $maxlength;
}, },
/** /**
* Minimum number of required characters * Minimum number of required characters
*/ */
'minlength' => function (int $minlength = null) { 'minlength' => function (int $minlength = null) {
return $minlength; return $minlength;
}, },
/** /**
* Changes the size of the textarea. Available sizes: `small`, `medium`, `large`, `huge` * Changes the size of the textarea. Available sizes: `small`, `medium`, `large`, `huge`
*/ */
'size' => function (string $size = null) { 'size' => function (string $size = null) {
return $size; return $size;
}, },
/** /**
* If `false`, spellcheck will be switched off * If `false`, spellcheck will be switched off
*/ */
'spellcheck' => function (bool $spellcheck = true) { 'spellcheck' => function (bool $spellcheck = true) {
return $spellcheck; return $spellcheck;
}, },
'value' => function (string $value = null) { 'value' => function (string $value = null) {
return trim($value ?? ''); return trim($value ?? '');
} }
], ],
'api' => function () { 'api' => function () {
return [ return [
[ [
'pattern' => 'files', 'pattern' => 'files',
'action' => function () { 'action' => function () {
$params = array_merge($this->field()->files(), [ $params = array_merge($this->field()->files(), [
'page' => $this->requestQuery('page'), 'page' => $this->requestQuery('page'),
'search' => $this->requestQuery('search') 'search' => $this->requestQuery('search')
]); ]);
return $this->field()->filepicker($params); return $this->field()->filepicker($params);
} }
], ],
[ [
'pattern' => 'upload', 'pattern' => 'upload',
'method' => 'POST', 'method' => 'POST',
'action' => function () { 'action' => function () {
$field = $this->field(); $field = $this->field();
$uploads = $field->uploads(); $uploads = $field->uploads();
return $this->field()->upload($this, $uploads, function ($file, $parent) use ($field) { return $this->field()->upload($this, $uploads, function ($file, $parent) use ($field) {
$absolute = $field->model()->is($parent) === false; $absolute = $field->model()->is($parent) === false;
return [ return [
'filename' => $file->filename(), 'filename' => $file->filename(),
'dragText' => $file->panel()->dragText('auto', $absolute), 'dragText' => $file->panel()->dragText('auto', $absolute),
]; ];
}); });
} }
] ]
]; ];
}, },
'validations' => [ 'validations' => [
'minlength', 'minlength',
'maxlength' 'maxlength'
] ]
]; ];

View file

@ -5,122 +5,122 @@ use Kirby\Toolkit\Date;
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'mixins' => ['datetime'], 'mixins' => ['datetime'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'placeholder' => null, 'placeholder' => null,
/** /**
* Sets the default time when a new page/file/user is created * Sets the default time when a new page/file/user is created
*/ */
'default' => function ($default = null): ?string { 'default' => function ($default = null): ?string {
return $default; return $default;
}, },
/** /**
* Custom format (dayjs tokens: `HH`, `hh`, `mm`, `ss`, `a`) that is * Custom format (dayjs tokens: `HH`, `hh`, `mm`, `ss`, `a`) that is
* used to display the field in the Panel * used to display the field in the Panel
*/ */
'display' => function ($display = null) { 'display' => function ($display = null) {
return I18n::translate($display, $display); return I18n::translate($display, $display);
}, },
/** /**
* Changes the clock icon * Changes the clock icon
*/ */
'icon' => function (string $icon = 'clock') { 'icon' => function (string $icon = 'clock') {
return $icon; return $icon;
}, },
/** /**
* Latest time, which can be selected/saved (H:i or H:i:s) * Latest time, which can be selected/saved (H:i or H:i:s)
*/ */
'max' => function (string $max = null): ?string { 'max' => function (string $max = null): ?string {
return Date::optional($max); return Date::optional($max);
}, },
/** /**
* Earliest time, which can be selected/saved (H:i or H:i:s) * Earliest time, which can be selected/saved (H:i or H:i:s)
*/ */
'min' => function (string $min = null): ?string { 'min' => function (string $min = null): ?string {
return Date::optional($min); return Date::optional($min);
}, },
/** /**
* `12` or `24` hour notation. If `12`, an AM/PM selector will be shown. * `12` or `24` hour notation. If `12`, an AM/PM selector will be shown.
* If `display` is defined, that option will take priority. * If `display` is defined, that option will take priority.
*/ */
'notation' => function (int $value = 24) { 'notation' => function (int $value = 24) {
return $value === 24 ? 24 : 12; return $value === 24 ? 24 : 12;
}, },
/** /**
* Round to the nearest: sub-options for `unit` (minute) and `size` (5) * Round to the nearest: sub-options for `unit` (minute) and `size` (5)
*/ */
'step' => function ($step = null) { 'step' => function ($step = null) {
return Date::stepConfig($step, [ return Date::stepConfig($step, [
'size' => 5, 'size' => 5,
'unit' => 'minute', 'unit' => 'minute',
]); ]);
}, },
'value' => function ($value = null): ?string { 'value' => function ($value = null): ?string {
return $value; return $value;
} }
], ],
'computed' => [ 'computed' => [
'display' => function () { 'display' => function () {
if ($this->display) { if ($this->display) {
return $this->display; return $this->display;
} }
return $this->notation === 24 ? 'HH:mm' : 'hh:mm a'; return $this->notation === 24 ? 'HH:mm' : 'hh:mm a';
}, },
'default' => function (): string { 'default' => function (): string {
return $this->toDatetime($this->default, 'H:i:s') ?? ''; return $this->toDatetime($this->default, 'H:i:s') ?? '';
}, },
'format' => function () { 'format' => function () {
return $this->props['format'] ?? 'H:i:s'; return $this->props['format'] ?? 'H:i:s';
}, },
'value' => function (): ?string { 'value' => function (): ?string {
return $this->toDatetime($this->value, 'H:i:s') ?? ''; return $this->toDatetime($this->value, 'H:i:s') ?? '';
} }
], ],
'validations' => [ 'validations' => [
'time', 'time',
'minMax' => function ($value) { 'minMax' => function ($value) {
if (!$value = Date::optional($value)) { if (!$value = Date::optional($value)) {
return true; return true;
} }
$min = Date::optional($this->min); $min = Date::optional($this->min);
$max = Date::optional($this->max); $max = Date::optional($this->max);
$format = 'H:i:s'; $format = 'H:i:s';
if ($min && $max && $value->isBetween($min, $max) === false) { if ($min && $max && $value->isBetween($min, $max) === false) {
throw new Exception([ throw new Exception([
'key' => 'validation.time.between', 'key' => 'validation.time.between',
'data' => [ 'data' => [
'min' => $min->format($format), 'min' => $min->format($format),
'max' => $min->format($format) 'max' => $min->format($format)
] ]
]); ]);
} elseif ($min && $value->isMin($min) === false) { } elseif ($min && $value->isMin($min) === false) {
throw new Exception([ throw new Exception([
'key' => 'validation.time.after', 'key' => 'validation.time.after',
'data' => [ 'data' => [
'time' => $min->format($format), 'time' => $min->format($format),
] ]
]); ]);
} elseif ($max && $value->isMax($max) === false) { } elseif ($max && $value->isMax($max) === false) {
throw new Exception([ throw new Exception([
'key' => 'validation.time.before', 'key' => 'validation.time.before',
'data' => [ 'data' => [
'time' => $max->format($format), 'time' => $max->format($format),
] ]
]); ]);
} }
return true; return true;
}, },
] ]
]; ];

View file

@ -5,69 +5,69 @@ use Kirby\Toolkit\A;
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'placeholder' => null, 'placeholder' => null,
/** /**
* Default value which will be saved when a new page/user/file is created * Default value which will be saved when a new page/user/file is created
*/ */
'default' => function ($default = null) { 'default' => function ($default = null) {
return $this->default = $default; return $this->default = $default;
}, },
/** /**
* Sets the text next to the toggle. The text can be a string or an array of two options. The first one is the negative text and the second one the positive. The text will automatically switch when the toggle is triggered. * Sets the text next to the toggle. The text can be a string or an array of two options. The first one is the negative text and the second one the positive. The text will automatically switch when the toggle is triggered.
*/ */
'text' => function ($value = null) { 'text' => function ($value = null) {
$model = $this->model(); $model = $this->model();
if (is_array($value) === true) { if (is_array($value) === true) {
if (A::isAssociative($value) === true) { if (A::isAssociative($value) === true) {
return $model->toSafeString(I18n::translate($value, $value)); return $model->toSafeString(I18n::translate($value, $value));
} }
foreach ($value as $key => $val) { foreach ($value as $key => $val) {
$value[$key] = $model->toSafeString(I18n::translate($val, $val)); $value[$key] = $model->toSafeString(I18n::translate($val, $val));
} }
return $value; return $value;
} }
if (empty($value) === false) { if (empty($value) === false) {
return $model->toSafeString(I18n::translate($value, $value)); return $model->toSafeString(I18n::translate($value, $value));
} }
return $value; return $value;
}, },
], ],
'computed' => [ 'computed' => [
'default' => function () { 'default' => function () {
return $this->toBool($this->default); return $this->toBool($this->default);
}, },
'value' => function () { 'value' => function () {
if ($this->props['value'] === null) { if ($this->props['value'] === null) {
return $this->default(); return $this->default();
} else { } else {
return $this->toBool($this->props['value']); return $this->toBool($this->props['value']);
} }
} }
], ],
'methods' => [ 'methods' => [
'toBool' => function ($value) { 'toBool' => function ($value) {
return in_array($value, [true, 'true', 1, '1', 'on'], true) === true; return in_array($value, [true, 'true', 1, '1', 'on'], true) === true;
} }
], ],
'save' => function (): string { 'save' => function (): string {
return $this->value() === true ? 'true' : 'false'; return $this->value() === true ? 'true' : 'false';
}, },
'validations' => [ 'validations' => [
'boolean', 'boolean',
'required' => function ($value) { 'required' => function ($value) {
if ($this->isRequired() && ($value === false || $this->isEmpty($value))) { if ($this->isRequired() && ($value === false || $this->isEmpty($value))) {
throw new InvalidArgumentException(I18n::translate('field.required')); throw new InvalidArgumentException(I18n::translate('field.required'));
} }
}, },
] ]
]; ];

View file

@ -0,0 +1,41 @@
<?php
return [
'mixins' => ['options'],
'props' => [
/**
* Unset inherited props
*/
'after' => null,
'before' => null,
'icon' => null,
'placeholder' => null,
/**
* Toggles will automatically span the full width of the field. With the grow option, you can disable this behaviour for a more compact layout.
*/
'grow' => function (bool $grow = true) {
return $grow;
},
/**
* If `false` all labels will be hidden for icon-only toggles.
*/
'labels' => function (bool $labels = true) {
return $labels;
},
/**
* A toggle can be deactivated on click. If reset is `false` deactivating a toggle is no longer possible.
*/
'reset' => function (bool $reset = true) {
return $reset;
}
],
'computed' => [
'default' => function () {
return $this->sanitizeOption($this->default);
},
'value' => function () {
return $this->sanitizeOption($this->value) ?? '';
},
]
];

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