Upgrade to 3.7.1

This commit is contained in:
Bastian Allgeier
2022-07-12 13:33:21 +02:00
parent 7931eb5e47
commit 1ad1eaf387
377 changed files with 63981 additions and 63824 deletions

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
indent_style = space
[*.php]
indent_size = 4 indent_size = 4
insert_final_newline = true
[*.yml]
indent_style = space
[*.md]
trim_trailing_whitespace = false

View File

@@ -5,31 +5,31 @@
* 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,110 +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.7.0.2", "version": "3.7.1",
"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"
}, },
"require": { "require": {
"php": ">=7.4.0 <8.2.0", "php": ">=7.4.0 <8.2.0",
"ext-SimpleXML": "*", "ext-SimpleXML": "*",
"ext-ctype": "*", "ext-ctype": "*",
"ext-curl": "*", "ext-curl": "*",
"ext-dom": "*", "ext-dom": "*",
"ext-filter": "*", "ext-filter": "*",
"ext-hash": "*", "ext-hash": "*",
"ext-iconv": "*", "ext-iconv": "*",
"ext-json": "*", "ext-json": "*",
"ext-libxml": "*", "ext-libxml": "*",
"ext-mbstring": "*", "ext-mbstring": "*",
"ext-openssl": "*", "ext-openssl": "*",
"claviska/simpleimage": "3.6.5", "claviska/simpleimage": "3.6.5",
"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.10.0", "laminas/laminas-escaper": "2.10.0",
"michelf/php-smartypants": "1.8.1", "michelf/php-smartypants": "1.8.1",
"phpmailer/phpmailer": "6.6.3", "phpmailer/phpmailer": "6.6.3",
"symfony/polyfill-intl-idn": "1.26.0", "symfony/polyfill-intl-idn": "1.26.0",
"symfony/polyfill-mbstring": "1.26.0" "symfony/polyfill-mbstring": "1.26.0"
}, },
"replace": { "replace": {
"symfony/polyfill-php72": "*" "symfony/polyfill-php72": "*"
}, },
"suggest": { "suggest": {
"ext-PDO": "Support for using databases", "ext-PDO": "Support for using databases",
"ext-apcu": "Support for the Apcu cache driver", "ext-apcu": "Support for the Apcu cache driver",
"ext-exif": "Support for exif information from images", "ext-exif": "Support for exif information from images",
"ext-fileinfo": "Improved mime type detection for files", "ext-fileinfo": "Improved mime type detection for files",
"ext-intl": "Improved i18n number formatting", "ext-intl": "Improved i18n number formatting",
"ext-memcached": "Support for the Memcached cache driver", "ext-memcached": "Support for the Memcached cache driver",
"ext-zip": "Support for ZIP archive file functions", "ext-zip": "Support for ZIP archive file functions",
"ext-zlib": "Sanitization and validation for svgz files" "ext-zlib": "Sanitization and validation for svgz files"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Kirby\\": "src/" "Kirby\\": "src/"
}, },
"classmap": [ "classmap": [
"dependencies/" "dependencies/"
], ],
"files": [ "files": [
"config/setup.php", "config/setup.php",
"config/helpers.php" "config/helpers.php"
] ]
}, },
"config": { "config": {
"allow-plugins": { "allow-plugins": {
"getkirby/composer-installer": true "getkirby/composer-installer": true
}, },
"optimize-autoloader": true, "optimize-autoloader": true,
"platform": { "platform": {
"php": "7.4.0" "php": "7.4.0"
}, },
"platform-check": false "platform-check": false
}, },
"extra": { "extra": {
"unused": [ "unused": [
"symfony/polyfill-intl-idn" "symfony/polyfill-intl-idn"
] ]
}, },
"scripts": { "scripts": {
"post-update-cmd": "curl -o cacert.pem https://curl.se/ca/cacert.pem", "post-update-cmd": "curl -o cacert.pem https://curl.se/ca/cacert.pem",
"analyze": [ "analyze": [
"@analyze:composer", "@analyze:composer",
"@analyze:psalm", "@analyze:psalm",
"@analyze:phpcpd", "@analyze:phpcpd",
"@analyze:phpmd" "@analyze:phpmd"
], ],
"analyze:composer": "composer validate --strict --no-check-version --no-check-all", "analyze:composer": "composer validate --strict --no-check-version --no-check-all",
"analyze:phpcpd": "phpcpd --fuzzy --exclude tests --exclude vendor .", "analyze:phpcpd": "phpcpd --fuzzy --exclude tests --exclude vendor .",
"analyze:phpmd": "phpmd . ansi phpmd.xml.dist --exclude 'dependencies/*,tests/*,vendor/*'", "analyze:phpmd": "phpmd . ansi phpmd.xml.dist --exclude 'dependencies/*,tests/*,vendor/*'",
"analyze:psalm": "psalm", "analyze:psalm": "psalm",
"build": "./scripts/build", "build": "./scripts/build",
"ci": [ "ci": [
"@fix", "@fix",
"@analyze", "@analyze",
"@test" "@test"
], ],
"fix": "php-cs-fixer fix", "fix": "php-cs-fixer fix",
"test": "phpunit --stderr --coverage-html=tests/coverage", "test": "phpunit --stderr --coverage-html=tests/coverage",
"zip": "composer archive --format=zip --file=dist" "zip": "composer archive --format=zip --file=dist"
} }
} }

2
kirby/composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "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": "538cd92719a152cbecaaaec5cde21f03", "content-hash": "1d71d8053b544a0d3380bc67234ff81d",
"packages": [ "packages": [
{ {
"name": "claviska/simpleimage", "name": "claviska/simpleimage",

View File

@@ -1,81 +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',
'helpers' => 'Kirby\Cms\Helpers', 'helpers' => 'Kirby\Cms\Helpers',
'html' => 'Kirby\Cms\Html', 'html' => 'Kirby\Cms\Html',
'kirby' => 'Kirby\Cms\App', 'kirby' => 'Kirby\Cms\App',
'page' => 'Kirby\Cms\Page', 'page' => 'Kirby\Cms\Page',
'pages' => 'Kirby\Cms\Pages', 'pages' => 'Kirby\Cms\Pages',
'pagination' => 'Kirby\Cms\Pagination', 'pagination' => 'Kirby\Cms\Pagination',
'r' => 'Kirby\Cms\R', 'r' => 'Kirby\Cms\R',
'response' => 'Kirby\Cms\Response', 'response' => 'Kirby\Cms\Response',
's' => 'Kirby\Cms\S', 's' => 'Kirby\Cms\S',
'sane' => 'Kirby\Sane\Sane', 'sane' => 'Kirby\Sane\Sane',
'site' => 'Kirby\Cms\Site', 'site' => 'Kirby\Cms\Site',
'structure' => 'Kirby\Cms\Structure', 'structure' => 'Kirby\Cms\Structure',
'url' => 'Kirby\Cms\Url', 'url' => 'Kirby\Cms\Url',
'user' => 'Kirby\Cms\User', 'user' => 'Kirby\Cms\User',
'users' => 'Kirby\Cms\Users', 'users' => 'Kirby\Cms\Users',
'visitor' => 'Kirby\Cms\Visitor', '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,109 +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(),
'panelImage' => fn (File $file) => $file->panel()->image(), 'panelImage' => fn (File $file) => $file->panel()->image(),
'panelUrl' => fn (File $file) => $file->panel()->url(true), 'panelUrl' => fn (File $file) => $file->panel()->url(true),
'prev' => fn (File $file) => $file->prev(), 'prev' => fn (File $file) => $file->prev(),
'prevWithTemplate' => function (File $file) { 'prevWithTemplate' => 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);
}, },
'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

@@ -8,116 +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 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.'); Helpers::deprecated('The API field page.next has been deprecated and will be removed in 3.8.0.');
return $page return $page
->nextAll() ->nextAll()
->filter('intendedTemplate', $page->intendedTemplate()) ->filter('intendedTemplate', $page->intendedTemplate())
->filter('status', $page->status()) ->filter('status', $page->status())
->filter('isReadable', true) ->filter('isReadable', true)
->first(); ->first();
}, },
'num' => fn (Page $page) => $page->num(), 'num' => fn (Page $page) => $page->num(),
'options' => fn (Page $page) => $page->panel()->options(['preview']), 'options' => fn (Page $page) => $page->panel()->options(['preview']),
'panelImage' => fn (Page $page) => $page->panel()->image(), 'panelImage' => fn (Page $page) => $page->panel()->image(),
'parent' => fn (Page $page) => $page->parent(), 'parent' => fn (Page $page) => $page->parent(),
'parents' => fn (Page $page) => $page->parents()->flip(), 'parents' => fn (Page $page) => $page->parents()->flip(),
/** /**
* @deprecated 3.6.0 * @deprecated 3.6.0
* @todo Remove in 3.8.0 * @todo Remove in 3.8.0
* @codeCoverageIgnore * @codeCoverageIgnore
*/ */
'prev' => function (Page $page) { 'prev' => function (Page $page) {
Helpers::deprecated('The API field page.prev has been deprecated and will be removed in 3.8.0.'); Helpers::deprecated('The API field page.prev has been deprecated and will be removed in 3.8.0.');
return $page return $page
->prevAll() ->prevAll()
->filter('intendedTemplate', $page->intendedTemplate()) ->filter('intendedTemplate', $page->intendedTemplate())
->filter('status', $page->status()) ->filter('status', $page->status())
->filter('isReadable', true) ->filter('isReadable', true)
->last(); ->last();
}, },
'previewUrl' => fn (Page $page) => $page->previewUrl(), 'previewUrl' => fn (Page $page) => $page->previewUrl(),
'siblings' => function (Page $page) { 'siblings' => function (Page $page) {
if ($page->isDraft() === true) { if ($page->isDraft() === true) {
return $page->parentModel()->children()->not($page); return $page->parentModel()->children()->not($page);
} else { } else {
return $page->siblings(); return $page->siblings();
} }
}, },
'slug' => fn (Page $page) => $page->slug(), 'slug' => fn (Page $page) => $page->slug(),
'status' => fn (Page $page) => $page->status(), 'status' => fn (Page $page) => $page->status(),
'template' => fn (Page $page) => $page->intendedTemplate()->name(), 'template' => fn (Page $page) => $page->intendedTemplate()->name(),
'title' => fn (Page $page) => $page->title()->value(), 'title' => fn (Page $page) => $page->title()->value(),
'url' => fn (Page $page) => $page->url(), 'url' => fn (Page $page) => $page->url(),
], ],
'type' => 'Kirby\Cms\Page', 'type' => 'Kirby\Cms\Page',
'views' => [ 'views' => [
'compact' => [ 'compact' => [
'id', 'id',
'title', 'title',
'url', 'url',
'num' 'num'
], ],
'default' => [ 'default' => [
'content', 'content',
'id', 'id',
'status', 'status',
'num', 'num',
'options', 'options',
'parent' => 'compact', 'parent' => 'compact',
'slug', 'slug',
'template', 'template',
'title', 'title',
'url' 'url'
], ],
'panel' => [ 'panel' => [
'id', 'id',
'blueprint', 'blueprint',
'content', 'content',
'status', 'status',
'options', 'options',
'next' => ['id', 'slug', 'title'], 'next' => ['id', 'slug', 'title'],
'parents' => ['id', 'slug', 'title'], 'parents' => ['id', 'slug', 'title'],
'prev' => ['id', 'slug', 'title'], 'prev' => ['id', 'slug', 'title'],
'previewUrl', 'previewUrl',
'slug', 'slug',
'title', 'title',
'url' 'url'
], ],
'selector' => [ 'selector' => [
'id', 'id',
'title', 'title',
'parent' => [ 'parent' => [
'id', 'id',
'title' 'title'
], ],
'children' => [ 'children' => [
'hasChildren', 'hasChildren',
'id', 'id',
'panelIcon', 'panelIcon',
'panelImage', 'panelImage',
'title', '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,40 +5,40 @@
* Content Lock Routes * Content Lock Routes
*/ */
return [ return [
[ [
'pattern' => '(:all)/lock', 'pattern' => '(:all)/lock',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $path) { 'action' => function (string $path) {
if ($lock = $this->parent($path)->lock()) { if ($lock = $this->parent($path)->lock()) {
return $lock->create(); return $lock->create();
} }
} }
], ],
[ [
'pattern' => '(:all)/lock', 'pattern' => '(:all)/lock',
'method' => 'DELETE', 'method' => 'DELETE',
'action' => function (string $path) { 'action' => function (string $path) {
if ($lock = $this->parent($path)->lock()) { if ($lock = $this->parent($path)->lock()) {
return $lock->remove(); return $lock->remove();
} }
} }
], ],
[ [
'pattern' => '(:all)/unlock', 'pattern' => '(:all)/unlock',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $path) { 'action' => function (string $path) {
if ($lock = $this->parent($path)->lock()) { if ($lock = $this->parent($path)->lock()) {
return $lock->unlock(); return $lock->unlock();
} }
} }
], ],
[ [
'pattern' => '(:all)/unlock', 'pattern' => '(:all)/unlock',
'method' => 'DELETE', 'method' => 'DELETE',
'action' => function (string $path) { 'action' => function (string $path) {
if ($lock = $this->parent($path)->lock()) { if ($lock = $this->parent($path)->lock()) {
return $lock->resolve(); return $lock->resolve();
} }
} }
], ],
]; ];

View File

@@ -6,116 +6,116 @@
*/ */
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' => 'pages/(:any)/blueprints', 'pattern' => 'pages/(:any)/blueprints',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id)->blueprints($this->requestQuery('section')); return $this->page($id)->blueprints($this->requestQuery('section'));
} }
], ],
[ [
'pattern' => 'pages/(:any)/children', 'pattern' => 'pages/(:any)/children',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->pages($id, $this->requestQuery('status')); return $this->pages($id, $this->requestQuery('status'));
} }
], ],
[ [
'pattern' => 'pages/(:any)/children', 'pattern' => 'pages/(:any)/children',
'method' => 'POST', 'method' => 'POST',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id)->createChild($this->requestBody()); return $this->page($id)->createChild($this->requestBody());
} }
], ],
[ [
'pattern' => 'pages/(:any)/children/search', 'pattern' => 'pages/(:any)/children/search',
'method' => 'GET|POST', 'method' => 'GET|POST',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->searchPages($id); return $this->searchPages($id);
} }
], ],
[ [
'pattern' => 'pages/(:any)/duplicate', 'pattern' => 'pages/(:any)/duplicate',
'method' => 'POST', 'method' => 'POST',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id)->duplicate($this->requestBody('slug'), [ return $this->page($id)->duplicate($this->requestBody('slug'), [
'children' => $this->requestBody('children'), 'children' => $this->requestBody('children'),
'files' => $this->requestBody('files'), 'files' => $this->requestBody('files'),
]); ]);
} }
], ],
[ [
'pattern' => 'pages/(:any)/slug', 'pattern' => 'pages/(:any)/slug',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id)->changeSlug($this->requestBody('slug')); return $this->page($id)->changeSlug($this->requestBody('slug'));
} }
], ],
[ [
'pattern' => 'pages/(:any)/status', 'pattern' => 'pages/(:any)/status',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id)->changeStatus($this->requestBody('status'), $this->requestBody('position')); return $this->page($id)->changeStatus($this->requestBody('status'), $this->requestBody('position'));
} }
], ],
[ [
'pattern' => 'pages/(:any)/template', 'pattern' => 'pages/(:any)/template',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id)->changeTemplate($this->requestBody('template')); return $this->page($id)->changeTemplate($this->requestBody('template'));
} }
], ],
[ [
'pattern' => 'pages/(:any)/title', 'pattern' => 'pages/(:any)/title',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id)->changeTitle($this->requestBody('title')); return $this->page($id)->changeTitle($this->requestBody('title'));
} }
], ],
[ [
'pattern' => 'pages/(:any)/sections/(:any)', 'pattern' => 'pages/(:any)/sections/(:any)',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id, string $sectionName) { 'action' => function (string $id, string $sectionName) {
if ($section = $this->page($id)->blueprint()->section($sectionName)) { if ($section = $this->page($id)->blueprint()->section($sectionName)) {
return $section->toResponse(); return $section->toResponse();
} }
} }
], ],
[ [
'pattern' => 'pages/(:any)/fields/(:any)/(:all?)', 'pattern' => 'pages/(: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) {
if ($page = $this->page($id)) { if ($page = $this->page($id)) {
return $this->fieldApi($page, $fieldName, $path); return $this->fieldApi($page, $fieldName, $path);
} }
} }
], ],
]; ];

View File

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

View File

@@ -6,99 +6,99 @@
*/ */
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' => 'site/blueprints', 'pattern' => 'site/blueprints',
'method' => 'GET', 'method' => 'GET',
'action' => function () { 'action' => function () {
return $this->site()->blueprints($this->requestQuery('section')); return $this->site()->blueprints($this->requestQuery('section'));
} }
], ],
[ [
'pattern' => 'site/find', 'pattern' => 'site/find',
'method' => 'POST', 'method' => 'POST',
'action' => function () { 'action' => function () {
return $this->site()->find(false, ...$this->requestBody()); return $this->site()->find(false, ...$this->requestBody());
} }
], ],
[ [
'pattern' => 'site/title', 'pattern' => 'site/title',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function () { 'action' => function () {
return $this->site()->changeTitle($this->requestBody('title')); return $this->site()->changeTitle($this->requestBody('title'));
} }
], ],
[ [
'pattern' => 'site/search', 'pattern' => 'site/search',
'method' => 'GET|POST', 'method' => 'GET|POST',
'action' => function () { 'action' => function () {
$pages = $this $pages = $this
->site() ->site()
->index(true) ->index(true)
->filter('isReadable', 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

@@ -3,12 +3,12 @@
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return function () { return function () {
return [ return [
'icon' => 'account', 'icon' => 'account',
'label' => I18n::translate('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

@@ -5,31 +5,31 @@ use Kirby\Cms\Find;
use Kirby\Panel\Panel; 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' => App::instance()->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.logout' => [
'pattern' => 'logout', 'pattern' => 'logout',
'auth' => false, 'auth' => false,
'action' => function () { 'action' => function () {
if ($user = App::instance()->user()) { if ($user = App::instance()->user()) {
$user->logout(); $user->logout();
} }
Panel::go('login'); Panel::go('login');
}, },
], ],
'account.password' => [ 'account.password' => [
'pattern' => 'reset-password', 'pattern' => 'reset-password',
'action' => fn () => ['component' => 'k-reset-password-view'] 'action' => fn () => ['component' => 'k-reset-password-view']
] ]
]; ];

View File

@@ -14,119 +14,119 @@ use Kirby\Toolkit\I18n;
* 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' => I18n::translate('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' => I18n::translate('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($file->kirby()->request()->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' => I18n::translate('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)($file->kirby()->request()->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' => I18n::template('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

@@ -4,37 +4,37 @@ use Kirby\Panel\Panel;
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'settings', 'icon' => 'settings',
'label' => I18n::translate('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

@@ -3,11 +3,11 @@
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'globe', 'icon' => 'globe',
'label' => I18n::translate('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

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

View File

@@ -4,22 +4,22 @@ 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 = App::instance(); $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

@@ -4,41 +4,41 @@ use Kirby\Panel\Panel;
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'user', 'icon' => 'user',
'label' => I18n::translate('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

@@ -3,16 +3,16 @@
use Kirby\Toolkit\I18n; 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(I18n::translate('view.site'))->toString(); return $kirby->site()->title()->or(I18n::translate('view.site'))->toString();
}, },
'icon' => 'home', 'icon' => 'home',
'label' => $kirby->site()->blueprint()->title() ?? I18n::translate('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

@@ -5,53 +5,53 @@ use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'pages' => [ 'pages' => [
'label' => I18n::translate('pages'), 'label' => I18n::translate('pages'),
'icon' => 'page', 'icon' => 'page',
'query' => function (string $query = null) { 'query' => function (string $query = null) {
$pages = App::instance()->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' => I18n::translate('files'), 'label' => I18n::translate('files'),
'icon' => 'image', 'icon' => 'image',
'query' => function (string $query = null) { 'query' => function (string $query = null) {
$files = App::instance()->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

@@ -4,24 +4,24 @@ 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 () => App::instance()->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

@@ -3,11 +3,11 @@
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'settings', 'icon' => 'settings',
'label' => I18n::translate('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

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

View File

@@ -3,53 +3,53 @@
use Kirby\Cms\App; use Kirby\Cms\App;
return [ return [
'system' => [ 'system' => [
'pattern' => 'system', 'pattern' => 'system',
'action' => function () { 'action' => function () {
$kirby = App::instance(); $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(),
'name' => [ 'name' => [
'text' => $plugin->name(), 'text' => $plugin->name(),
'href' => $plugin->link(), 'href' => $plugin->link(),
], ],
'version' => $plugin->version(), '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' => $kirby->environment()->https(), 'https' => $kirby->environment()->https(),
'version' => $kirby->version(), 'version' => $kirby->version(),
'urls' => [ 'urls' => [
'content' => $system->exposedFileUrl('content'), 'content' => $system->exposedFileUrl('content'),
'git' => $system->exposedFileUrl('git'), 'git' => $system->exposedFileUrl('git'),
'kirby' => $system->exposedFileUrl('kirby'), 'kirby' => $system->exposedFileUrl('kirby'),
'site' => $system->exposedFileUrl('site') 'site' => $system->exposedFileUrl('site')
] ]
] ]
]; ];
} }
], ],
]; ];

View File

@@ -3,14 +3,14 @@
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'users', 'icon' => 'users',
'label' => I18n::translate('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

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

@@ -5,23 +5,23 @@ use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'users' => [ 'users' => [
'label' => I18n::translate('users'), 'label' => I18n::translate('users'),
'icon' => 'users', 'icon' => 'users',
'query' => function (string $query = null) { 'query' => function (string $query = null) {
$users = App::instance()->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

@@ -5,62 +5,62 @@ 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 = App::instance(); $kirby = App::instance();
$role = $kirby->request()->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' => $kirby->request()->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

@@ -9,10 +9,10 @@ $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();
} }
?> ?>

View File

@@ -21,395 +21,395 @@ 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
* *
* @deprecated 3.7.0 Disable `dump()` via `KIRBY_HELPER_DUMP` instead and create your own function * @deprecated 3.7.0 Disable `dump()` via `KIRBY_HELPER_DUMP` instead and create your own function
* @todo move to `Helpers::dump()`, remove component in 3.8.0 * @todo move to `Helpers::dump()`, remove component in 3.8.0
*/ */
'dump' => function (App $kirby, $variable, bool $echo = true) { 'dump' => function (App $kirby, $variable, bool $echo = true) {
if ($kirby->environment()->cli() === true) { if ($kirby->environment()->cli() === true) {
$output = print_r($variable, true) . PHP_EOL; $output = print_r($variable, true) . PHP_EOL;
} else { } else {
$output = '<pre>' . print_r($variable, true) . '</pre>'; $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 // if not, create job file
$job = $mediaRoot . '/.jobs/' . $thumbName . '.json'; $job = $mediaRoot . '/.jobs/' . $thumbName . '.json';
try { try {
Data::write($job, array_merge($options, [ Data::write($job, array_merge($options, [
'filename' => $file->filename() 'filename' => $file->filename()
])); ]));
} catch (Throwable $e) { } catch (Throwable $e) {
// if thumb doesn't exist yet and job file cannot // if thumb doesn't exist yet and job file cannot
// be created, return // be created, return
return $file; return $file;
} }
} }
return new FileVersion([ return new FileVersion([
'modifications' => $options, 'modifications' => $options,
'original' => $file, 'original' => $file,
'root' => $thumbRoot, 'root' => $thumbRoot,
'url' => dirname($file->mediaUrl()) . '/' . $thumbName, 'url' => dirname($file->mediaUrl()) . '/' . $thumbName,
]); ]);
}, },
/** /**
* Used by the `js()` helper * Used by the `js()` 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
*/ */
'js' => fn (App $kirby, string $url, $options = null): string => $url, 'js' => fn (App $kirby, string $url, $options = null): string => $url,
/** /**
* Add your own Markdown parser * Add your own Markdown 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 Markdown options * @param array $options Markdown options
* @param bool $inline Whether to wrap the text in `<p>` tags (deprecated: set via $options['inline'] instead) * @param bool $inline Whether to wrap the text in `<p>` tags (deprecated: set via $options['inline'] instead)
* @return string * @return string
* @todo remove $inline parameter in in 3.8.0 * @todo remove $inline parameter in in 3.8.0
*/ */
'markdown' => function (App $kirby, string $text = null, array $options = [], bool $inline = false): string { 'markdown' => function (App $kirby, string $text = null, array $options = [], bool $inline = false): string {
static $markdown; static $markdown;
static $config; static $config;
// warning for deprecated fourth parameter // warning for deprecated fourth parameter
if (func_num_args() === 4 && isset($options['inline']) === false) { if (func_num_args() === 4 && isset($options['inline']) === false) {
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
Helpers::deprecated('markdown component: the $inline parameter is deprecated and will be removed in Kirby 3.8.0. Use $options[\'inline\'] instead.'); Helpers::deprecated('markdown component: the $inline parameter is deprecated and will be removed in Kirby 3.8.0. Use $options[\'inline\'] instead.');
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} }
// 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'] ?? false); 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(
$kirby->option('thumbs.driver', 'gd'), $kirby->option('thumbs.driver', 'gd'),
$kirby->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 ($parts[0] ?? null) { if ($parts[0] ?? null) {
$page = $kirby->site()->find($parts[0]); $page = $kirby->site()->find($parts[0]);
} else { } else {
$page = $kirby->site()->page(); $page = $kirby->site()->page();
} }
if ($page) { if ($page) {
$path = $page->url($language); $path = $page->url($language);
if (isset($parts[1]) === true) { if (isset($parts[1]) === true) {
$path .= '#' . $parts[1]; $path .= '#' . $parts[1];
} }
} }
} }
// keep relative urls // keep relative urls
if ( if (
$path !== null && $path !== null &&
(substr($path, 0, 2) === './' || substr($path, 0, 3) === '../') (substr($path, 0, 2) === './' || substr($path, 0, 3) === '../')
) { ) {
return $path; return $path;
} }
$url = Url::makeAbsolute($path, $kirby->url()); $url = Url::makeAbsolute($path, $kirby->url());
if ($options === null) { if ($options === null) {
return $url; return $url;
} }
return (new Uri($url, $options))->toString(); 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

@@ -5,107 +5,107 @@ 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 = App::instance(); $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,200 +5,200 @@ 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; $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 // Skip hidden and unsaveable fields
// They should never be included as column // They should never be included as column
if ($field['type'] === 'hidden' || $field['saveable'] === false) { if ($field['type'] === 'hidden' || $field['saveable'] === false) {
continue; continue;
} }
$columns[$field['name']] = [ $columns[$field['name']] = [
'type' => $field['type'], 'type' => $field['type'],
'label' => $field['label'] ?? $field['name'] 'label' => $field['label'] ?? $field['name']
]; ];
} }
} else { } else {
foreach ($this->columns as $columnName => $columnProps) { foreach ($this->columns as $columnName => $columnProps) {
if (is_array($columnProps) === false) { if (is_array($columnProps) === false) {
$columnProps = []; $columnProps = [];
} }
$field = $this->fields[$columnName] ?? null; $field = $this->fields[$columnName] ?? null;
if (empty($field) === true || $field['saveable'] === false) { if (empty($field) === true || $field['saveable'] === false) {
continue; continue;
} }
if (($columnProps['mobile'] ?? false) === true) { if (($columnProps['mobile'] ?? false) === true) {
$mobile++; $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']
]); ]);
} }
} }
// make the first column visible on mobile // make the first column visible on mobile
// if no other mobile columns are defined // if no other mobile columns are defined
if ($mobile === 0) { if ($mobile === 0) {
$columns[array_key_first($columns)]['mobile'] = true; $columns[array_key_first($columns)]['mobile'] = true;
} }
return $columns; return $columns;
} }
], ],
'methods' => [ 'methods' => [
'rows' => function ($value) { 'rows' => function ($value) {
$rows = Data::decode($value, 'yaml'); $rows = Data::decode($value, 'yaml');
$value = []; $value = [];
foreach ($rows as $index => $row) { foreach ($rows as $index => $row) {
if (is_array($row) === false) { if (is_array($row) === false) {
continue; continue;
} }
$value[] = $this->form($row)->values(); $value[] = $this->form($row)->values();
} }
return $value; return $value;
}, },
'form' => function (array $values = []) { 'form' => function (array $values = []) {
return new Form([ return new Form([
'fields' => $this->attrs['fields'], 'fields' => $this->attrs['fields'],
'values' => $values, 'values' => $values,
'model' => $this->model 'model' => $this->model
]); ]);
}, },
], ],
'api' => function () { 'api' => function () {
return [ return [
[ [
'pattern' => 'validate', 'pattern' => 'validate',
'method' => 'ALL', 'method' => 'ALL',
'action' => function () { 'action' => function () {
return array_values($this->field()->form($this->requestBody())->errors()); return array_values($this->field()->form($this->requestBody())->errors());
} }
] ]
]; ];
}, },
'save' => function ($value) { 'save' => function ($value) {
$data = []; $data = [];
foreach ($value as $row) { foreach ($value as $row) {
$data[] = $this->form($row)->content(); $data[] = $this->form($row)->content();
} }
return $data; return $data;
}, },
'validations' => [ 'validations' => [
'min', 'min',
'max' 'max'
] ]
]; ];

View File

@@ -5,99 +5,99 @@ 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 // already a valid object
if (is_array($option) === true && isset($option['value'], $option['text']) === true) { if (is_array($option) === true && isset($option['value'], $option['text']) === true) {
return $option; return $option;
} }
$index = array_search($option, array_column($options, 'value')); $index = array_search($option, array_column($options, 'value'));
if ($index !== false) { if ($index !== false) {
return $options[$index]; return $options[$index];
} }
return [ return [
'value' => $option, 'value' => $option,
'text' => $option, 'text' => $option,
]; ];
}, Str::split($value, $this->separator())); }, Str::split($value, $this->separator()));
} }
], ],
'save' => function (array $value = null): string { 'save' => function (array $value = null): string {
return A::join( return A::join(
A::pluck($value, 'value'), A::pluck($value, 'value'),
$this->separator() . ' ' $this->separator() . ' '
); );
}, },
'validations' => [ 'validations' => [
'min', 'min',
'max' '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,99 +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;
} }
$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, trim($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

@@ -1,41 +1,41 @@
<?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,
/** /**
* Toggles will automatically span the full width of the field. With the grow option, you can disable this behaviour for a more compact layout. * 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) { 'grow' => function (bool $grow = true) {
return $grow; return $grow;
}, },
/** /**
* If `false` all labels will be hidden for icon-only toggles. * If `false` all labels will be hidden for icon-only toggles.
*/ */
'labels' => function (bool $labels = true) { 'labels' => function (bool $labels = true) {
return $labels; return $labels;
}, },
/** /**
* A toggle can be deactivated on click. If reset is `false` deactivating a toggle is no longer possible. * A toggle can be deactivated on click. If reset is `false` deactivating a toggle is no longer possible.
*/ */
'reset' => function (bool $reset = true) { 'reset' => function (bool $reset = true) {
return $reset; return $reset;
} }
], ],
'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

@@ -3,39 +3,39 @@
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,
'spellcheck' => null, 'spellcheck' => null,
/** /**
* Sets the HTML5 autocomplete attribute * Sets the HTML5 autocomplete attribute
*/ */
'autocomplete' => function (string $autocomplete = 'url') { 'autocomplete' => function (string $autocomplete = 'url') {
return $autocomplete; return $autocomplete;
}, },
/** /**
* Changes the link icon * Changes the link icon
*/ */
'icon' => function (string $icon = 'url') { 'icon' => function (string $icon = 'url') {
return $icon; return $icon;
}, },
/** /**
* Sets custom placeholder text, when the field is empty * Sets custom placeholder text, when the field is empty
*/ */
'placeholder' => function ($value = null) { 'placeholder' => function ($value = null) {
return I18n::translate($value, $value) ?? 'https://example.com'; return I18n::translate($value, $value) ?? 'https://example.com';
} }
], ],
'validations' => [ 'validations' => [
'minlength', 'minlength',
'maxlength', 'maxlength',
'url' 'url'
], ],
]; ];

View File

@@ -5,101 +5,101 @@ use Kirby\Data\Data;
use Kirby\Toolkit\A; use Kirby\Toolkit\A;
return [ return [
'mixins' => [ 'mixins' => [
'layout', 'layout',
'min', 'min',
'picker', 'picker',
'userpicker' 'userpicker'
], ],
'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 user(s) when a new page/file/user is created * Default selected user(s) when a new page/file/user is created
*/ */
'default' => function ($default = null) { 'default' => function ($default = null) {
if ($default === false) { if ($default === false) {
return []; return [];
} }
if ($default === null && $user = $this->kirby()->user()) { if ($default === null && $user = $this->kirby()->user()) {
return [ return [
$this->userResponse($user) $this->userResponse($user)
]; ];
} }
return $this->toUsers($default); return $this->toUsers($default);
}, },
'value' => function ($value = null) { 'value' => function ($value = null) {
return $this->toUsers($value); return $this->toUsers($value);
}, },
], ],
'computed' => [ 'computed' => [
/** /**
* Unset inherited computed * Unset inherited computed
*/ */
'default' => null 'default' => null
], ],
'methods' => [ 'methods' => [
'userResponse' => function ($user) { 'userResponse' => function ($user) {
return $user->panel()->pickerData([ return $user->panel()->pickerData([
'info' => $this->info, 'info' => $this->info,
'image' => $this->image, 'image' => $this->image,
'layout' => $this->layout, 'layout' => $this->layout,
'text' => $this->text, 'text' => $this->text,
]); ]);
}, },
'toUsers' => function ($value = null) { 'toUsers' => function ($value = null) {
$users = []; $users = [];
$kirby = App::instance(); $kirby = App::instance();
foreach (Data::decode($value, 'yaml') as $email) { foreach (Data::decode($value, 'yaml') as $email) {
if (is_array($email) === true) { if (is_array($email) === true) {
$email = $email['email'] ?? null; $email = $email['email'] ?? null;
} }
if ($email !== null && ($user = $kirby->user($email))) { if ($email !== null && ($user = $kirby->user($email))) {
$users[] = $this->userResponse($user); $users[] = $this->userResponse($user);
} }
} }
return $users; return $users;
} }
], ],
'api' => function () { 'api' => function () {
return [ return [
[ [
'pattern' => '/', 'pattern' => '/',
'action' => function () { 'action' => function () {
$field = $this->field(); $field = $this->field();
return $field->userpicker([ return $field->userpicker([
'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()
]); ]);
} }
] ]
]; ];
}, },
'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

@@ -3,34 +3,34 @@
use Kirby\Sane\Sane; use Kirby\Sane\Sane;
return [ return [
'props' => [ 'props' => [
/** /**
* Enables inline mode, which will not wrap new lines in paragraphs and creates hard breaks instead. * Enables inline mode, which will not wrap new lines in paragraphs and creates hard breaks instead.
* *
* @param bool $inline * @param bool $inline
*/ */
'inline' => function (bool $inline = false) { 'inline' => function (bool $inline = false) {
return $inline; return $inline;
}, },
/** /**
* Sets the allowed HTML formats. Available formats: `bold`, `italic`, `underline`, `strike`, `code`, `link`, `email`. 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`, `email`. Activate them all by passing `true`. Deactivate them all by passing `false`
* @param array|bool $marks * @param array|bool $marks
*/ */
'marks' => function ($marks = true) { 'marks' => function ($marks = true) {
return $marks; return $marks;
}, },
/** /**
* Sets the allowed nodes. Available nodes: `paragraph`, `heading`, `bulletList`, `orderedList`. Activate/deactivate them all by passing `true`/`false`. Default nodes are `paragraph`, `heading`, `bulletList`, `orderedList`. * Sets the allowed nodes. Available nodes: `paragraph`, `heading`, `bulletList`, `orderedList`. Activate/deactivate them all by passing `true`/`false`. Default nodes are `paragraph`, `heading`, `bulletList`, `orderedList`.
* @param array|bool|null $nodes * @param array|bool|null $nodes
*/ */
'nodes' => function ($nodes = null) { 'nodes' => function ($nodes = null) {
return $nodes; return $nodes;
} }
], ],
'computed' => [ 'computed' => [
'value' => function () { 'value' => function () {
$value = trim($this->value ?? ''); $value = trim($this->value ?? '');
return Sane::sanitize($value, 'html'); return Sane::sanitize($value, 'html');
} }
], ],
]; ];

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,24 +3,24 @@
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return function (array $props) { return function (array $props) {
$props['sections'] = [ $props['sections'] = [
'files' => [ 'files' => [
'headline' => $props['headline'] ?? I18n::translate('files'), 'headline' => $props['headline'] ?? I18n::translate('files'),
'type' => 'files', 'type' => 'files',
'layout' => $props['layout'] ?? 'cards', 'layout' => $props['layout'] ?? 'cards',
'template' => $props['template'] ?? null, 'template' => $props['template'] ?? null,
'image' => $props['image'] ?? null, 'image' => $props['image'] ?? null,
'info' => '{{ file.dimensions }}' 'info' => '{{ file.dimensions }}'
] ]
]; ];
// remove global options // remove global options
unset( unset(
$props['headline'], $props['headline'],
$props['layout'], $props['layout'],
$props['template'], $props['template'],
$props['image'] $props['image']
); );
return $props; return $props;
}; };

View File

@@ -3,72 +3,72 @@
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return function ($props) { return function ($props) {
$section = function ($defaults, $props) { $section = function ($defaults, $props) {
if ($props === true) { if ($props === true) {
$props = []; $props = [];
} }
if (is_string($props) === true) { if (is_string($props) === true) {
$props = [ $props = [
'headline' => $props 'headline' => $props
]; ];
} }
return array_replace_recursive($defaults, $props); return array_replace_recursive($defaults, $props);
}; };
if (empty($props['sidebar']) === false) { if (empty($props['sidebar']) === false) {
$sidebar = $props['sidebar']; $sidebar = $props['sidebar'];
} else { } else {
$sidebar = []; $sidebar = [];
$pages = $props['pages'] ?? []; $pages = $props['pages'] ?? [];
$files = $props['files'] ?? []; $files = $props['files'] ?? [];
if ($pages !== false) { if ($pages !== false) {
$sidebar['pages'] = $section([ $sidebar['pages'] = $section([
'headline' => I18n::translate('pages'), 'headline' => I18n::translate('pages'),
'type' => 'pages', 'type' => 'pages',
'status' => 'all', 'status' => 'all',
'layout' => 'list', 'layout' => 'list',
], $pages); ], $pages);
} }
if ($files !== false) { if ($files !== false) {
$sidebar['files'] = $section([ $sidebar['files'] = $section([
'headline' => I18n::translate('files'), 'headline' => I18n::translate('files'),
'type' => 'files', 'type' => 'files',
'layout' => 'list' 'layout' => 'list'
], $files); ], $files);
} }
} }
if (empty($sidebar) === true) { if (empty($sidebar) === true) {
$props['fields'] = $props['fields'] ?? []; $props['fields'] = $props['fields'] ?? [];
unset( unset(
$props['files'], $props['files'],
$props['pages'] $props['pages']
); );
} else { } else {
$props['columns'] = [ $props['columns'] = [
[ [
'width' => '2/3', 'width' => '2/3',
'fields' => $props['fields'] ?? [] 'fields' => $props['fields'] ?? []
], ],
[ [
'width' => '1/3', 'width' => '1/3',
'sections' => $sidebar 'sections' => $sidebar
], ],
]; ];
unset( unset(
$props['fields'], $props['fields'],
$props['files'], $props['files'],
$props['pages'], $props['pages'],
$props['sidebar'] $props['sidebar']
); );
} }
return $props; return $props;
}; };

View File

@@ -4,56 +4,56 @@ use Kirby\Toolkit\I18n;
return function (array $props) { return function (array $props) {
// load the general templates setting for all sections // load the general templates setting for all sections
$templates = $props['templates'] ?? null; $templates = $props['templates'] ?? null;
$section = function ($headline, $status, $props) use ($templates) { $section = function ($headline, $status, $props) use ($templates) {
$defaults = [ $defaults = [
'headline' => $headline, 'headline' => $headline,
'type' => 'pages', 'type' => 'pages',
'layout' => 'list', 'layout' => 'list',
'status' => $status 'status' => $status
]; ];
if ($props === true) { if ($props === true) {
$props = []; $props = [];
} }
if (is_string($props) === true) { if (is_string($props) === true) {
$props = [ $props = [
'headline' => $props 'headline' => $props
]; ];
} }
// inject the global templates definition // inject the global templates definition
if (empty($templates) === false) { if (empty($templates) === false) {
$props['templates'] = $props['templates'] ?? $templates; $props['templates'] = $props['templates'] ?? $templates;
} }
return array_replace_recursive($defaults, $props); return array_replace_recursive($defaults, $props);
}; };
$sections = []; $sections = [];
$drafts = $props['drafts'] ?? []; $drafts = $props['drafts'] ?? [];
$unlisted = $props['unlisted'] ?? false; $unlisted = $props['unlisted'] ?? false;
$listed = $props['listed'] ?? []; $listed = $props['listed'] ?? [];
if ($drafts !== false) { if ($drafts !== false) {
$sections['drafts'] = $section(I18n::translate('pages.status.draft'), 'drafts', $drafts); $sections['drafts'] = $section(I18n::translate('pages.status.draft'), 'drafts', $drafts);
} }
if ($unlisted !== false) { if ($unlisted !== false) {
$sections['unlisted'] = $section(I18n::translate('pages.status.unlisted'), 'unlisted', $unlisted); $sections['unlisted'] = $section(I18n::translate('pages.status.unlisted'), 'unlisted', $unlisted);
} }
if ($listed !== false) { if ($listed !== false) {
$sections['listed'] = $section(I18n::translate('pages.status.listed'), 'listed', $listed); $sections['listed'] = $section(I18n::translate('pages.status.listed'), 'listed', $listed);
} }
// cleaning up // cleaning up
unset($props['drafts'], $props['unlisted'], $props['listed'], $props['templates']); unset($props['drafts'], $props['unlisted'], $props['listed'], $props['templates']);
return array_merge($props, ['sections' => $sections]); return array_merge($props, ['sections' => $sections]);
}; };

View File

@@ -8,141 +8,141 @@ use Kirby\Panel\Plugins;
use Kirby\Toolkit\Str; use Kirby\Toolkit\Str;
return function ($kirby) { return function ($kirby) {
$api = $kirby->option('api.slug', 'api'); $api = $kirby->option('api.slug', 'api');
$panel = $kirby->option('panel.slug', 'panel'); $panel = $kirby->option('panel.slug', 'panel');
$index = $kirby->url('index'); $index = $kirby->url('index');
$media = $kirby->url('media'); $media = $kirby->url('media');
if (Str::startsWith($media, $index) === true) { if (Str::startsWith($media, $index) === true) {
$media = Str::after($media, $index); $media = Str::after($media, $index);
} else { } else {
// media URL is outside of the site, we can't make routing work; // media URL is outside of the site, we can't make routing work;
// fall back to the standard media route // fall back to the standard media route
$media = 'media'; $media = 'media';
} }
/** /**
* Before routes are running before the * Before routes are running before the
* plugin routes and cannot be overwritten by * plugin routes and cannot be overwritten by
* plugins. * plugins.
*/ */
$before = [ $before = [
[ [
'pattern' => $api . '/(:all)', 'pattern' => $api . '/(:all)',
'method' => 'ALL', 'method' => 'ALL',
'env' => 'api', 'env' => 'api',
'action' => function ($path = null) use ($kirby) { 'action' => function ($path = null) use ($kirby) {
if ($kirby->option('api') === false) { if ($kirby->option('api') === false) {
return null; return null;
} }
$request = $kirby->request(); $request = $kirby->request();
return $kirby->api()->render($path, $this->method(), [ return $kirby->api()->render($path, $this->method(), [
'body' => $request->body()->toArray(), 'body' => $request->body()->toArray(),
'files' => $request->files()->toArray(), 'files' => $request->files()->toArray(),
'headers' => $request->headers(), 'headers' => $request->headers(),
'query' => $request->query()->toArray(), 'query' => $request->query()->toArray(),
]); ]);
} }
], ],
[ [
'pattern' => $media . '/plugins/index.(css|js)', 'pattern' => $media . '/plugins/index.(css|js)',
'env' => 'media', 'env' => 'media',
'action' => function (string $type) use ($kirby) { 'action' => function (string $type) use ($kirby) {
$plugins = new Plugins(); $plugins = new Plugins();
return $kirby return $kirby
->response() ->response()
->type($type) ->type($type)
->body($plugins->read($type)); ->body($plugins->read($type));
} }
], ],
[ [
'pattern' => $media . '/plugins/(:any)/(:any)/(:all).(css|map|gif|js|mjs|jpg|png|svg|webp|avif|woff2|woff|json)', 'pattern' => $media . '/plugins/(:any)/(:any)/(:all).(css|map|gif|js|mjs|jpg|png|svg|webp|avif|woff2|woff|json)',
'env' => 'media', 'env' => 'media',
'action' => function (string $provider, string $pluginName, string $filename, string $extension) { 'action' => function (string $provider, string $pluginName, string $filename, string $extension) {
return PluginAssets::resolve($provider . '/' . $pluginName, $filename . '.' . $extension); return PluginAssets::resolve($provider . '/' . $pluginName, $filename . '.' . $extension);
} }
], ],
[ [
'pattern' => $media . '/pages/(:all)/(:any)/(:any)', 'pattern' => $media . '/pages/(:all)/(:any)/(:any)',
'env' => 'media', 'env' => 'media',
'action' => function ($path, $hash, $filename) use ($kirby) { 'action' => function ($path, $hash, $filename) use ($kirby) {
return Media::link($kirby->page($path), $hash, $filename); return Media::link($kirby->page($path), $hash, $filename);
} }
], ],
[ [
'pattern' => $media . '/site/(:any)/(:any)', 'pattern' => $media . '/site/(:any)/(:any)',
'env' => 'media', 'env' => 'media',
'action' => function ($hash, $filename) use ($kirby) { 'action' => function ($hash, $filename) use ($kirby) {
return Media::link($kirby->site(), $hash, $filename); return Media::link($kirby->site(), $hash, $filename);
} }
], ],
[ [
'pattern' => $media . '/users/(:any)/(:any)/(:any)', 'pattern' => $media . '/users/(:any)/(:any)/(:any)',
'env' => 'media', 'env' => 'media',
'action' => function ($id, $hash, $filename) use ($kirby) { 'action' => function ($id, $hash, $filename) use ($kirby) {
return Media::link($kirby->user($id), $hash, $filename); return Media::link($kirby->user($id), $hash, $filename);
} }
], ],
[ [
'pattern' => $media . '/assets/(:all)/(:any)/(:any)', 'pattern' => $media . '/assets/(:all)/(:any)/(:any)',
'env' => 'media', 'env' => 'media',
'action' => function ($path, $hash, $filename) { 'action' => function ($path, $hash, $filename) {
return Media::thumb($path, $hash, $filename); return Media::thumb($path, $hash, $filename);
} }
], ],
[ [
'pattern' => $panel . '/(:all?)', 'pattern' => $panel . '/(:all?)',
'method' => 'ALL', 'method' => 'ALL',
'env' => 'panel', 'env' => 'panel',
'action' => function ($path = null) { 'action' => function ($path = null) {
return Panel::router($path); return Panel::router($path);
} }
], ],
]; ];
// Multi-language setup // Multi-language setup
if ($kirby->multilang() === true) { if ($kirby->multilang() === true) {
$after = LanguageRoutes::create($kirby); $after = LanguageRoutes::create($kirby);
} else { } else {
// Single-language home // Single-language home
$after[] = [ $after[] = [
'pattern' => '', 'pattern' => '',
'method' => 'ALL', 'method' => 'ALL',
'env' => 'site', 'env' => 'site',
'action' => function () use ($kirby) { 'action' => function () use ($kirby) {
return $kirby->resolve(); return $kirby->resolve();
} }
]; ];
// redirect the home page folder to the real homepage // redirect the home page folder to the real homepage
$after[] = [ $after[] = [
'pattern' => $kirby->option('home', 'home'), 'pattern' => $kirby->option('home', 'home'),
'method' => 'ALL', 'method' => 'ALL',
'env' => 'site', 'env' => 'site',
'action' => function () use ($kirby) { 'action' => function () use ($kirby) {
return $kirby return $kirby
->response() ->response()
->redirect($kirby->site()->url()); ->redirect($kirby->site()->url());
} }
]; ];
// Single-language subpages // Single-language subpages
$after[] = [ $after[] = [
'pattern' => '(:all)', 'pattern' => '(:all)',
'method' => 'ALL', 'method' => 'ALL',
'env' => 'site', 'env' => 'site',
'action' => function (string $path) use ($kirby) { 'action' => function (string $path) use ($kirby) {
return $kirby->resolve($path); return $kirby->resolve($path);
} }
]; ];
} }
return [ return [
'before' => $before, 'before' => $before,
'after' => $after 'after' => $after
]; ];
}; };

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