Update Kirby and dependencies

This commit is contained in:
Paul Nicoué 2022-08-31 15:02:43 +02:00
parent 503b339974
commit 399fa20902
439 changed files with 66915 additions and 64442 deletions

View file

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

View file

@ -3,25 +3,25 @@
use Kirby\Exception\PermissionException;
return function () {
$auth = $this->kirby()->auth();
$allowImpersonation = $this->kirby()->option('api.allowImpersonation') ?? false;
$auth = $this->kirby()->auth();
$allowImpersonation = $this->kirby()->option('api.allowImpersonation') ?? false;
// csrf token check
if (
$auth->type($allowImpersonation) === 'session' &&
$auth->csrf() === false
) {
throw new PermissionException('Unauthenticated');
}
// csrf token check
if (
$auth->type($allowImpersonation) === 'session' &&
$auth->csrf() === false
) {
throw new PermissionException('Unauthenticated');
}
// get user from session or basic auth
if ($user = $auth->user(null, $allowImpersonation)) {
if ($user->role()->permissions()->for('access', 'panel') === false) {
throw new PermissionException(['key' => 'access.panel']);
}
// get user from session or basic auth
if ($user = $auth->user(null, $allowImpersonation)) {
if ($user->role()->permissions()->for('access', 'panel') === false) {
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 [
/**
* Children
*/
'children' => [
'model' => 'page',
'type' => 'Kirby\Cms\Pages',
'view' => 'compact'
],
/**
* Children
*/
'children' => [
'model' => 'page',
'type' => 'Kirby\Cms\Pages',
'view' => 'compact'
],
/**
* Files
*/
'files' => [
'model' => 'file',
'type' => 'Kirby\Cms\Files'
],
/**
* Files
*/
'files' => [
'model' => 'file',
'type' => 'Kirby\Cms\Files'
],
/**
* Languages
*/
'languages' => [
'model' => 'language',
'type' => 'Kirby\Cms\Languages'
],
/**
* Languages
*/
'languages' => [
'model' => 'language',
'type' => 'Kirby\Cms\Languages'
],
/**
* Pages
*/
'pages' => [
'model' => 'page',
'type' => 'Kirby\Cms\Pages',
'view' => 'compact'
],
/**
* Pages
*/
'pages' => [
'model' => 'page',
'type' => 'Kirby\Cms\Pages',
'view' => 'compact'
],
/**
* Roles
*/
'roles' => [
'model' => 'role',
'type' => 'Kirby\Cms\Roles',
'view' => 'compact'
],
/**
* Roles
*/
'roles' => [
'model' => 'role',
'type' => 'Kirby\Cms\Roles',
'view' => 'compact'
],
/**
* Translations
*/
'translations' => [
'model' => 'translation',
'type' => 'Kirby\Cms\Translations',
'view' => 'compact'
],
/**
* Translations
*/
'translations' => [
'model' => 'translation',
'type' => 'Kirby\Cms\Translations',
'view' => 'compact'
],
/**
* Users
*/
'users' => [
'default' => fn () => $this->users(),
'model' => 'user',
'type' => 'Kirby\Cms\Users',
'view' => 'compact'
]
/**
* Users
*/
'users' => [
'default' => fn () => $this->users(),
'model' => 'user',
'type' => 'Kirby\Cms\Users',
'view' => 'compact'
]
];

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,17 +1,18 @@
<?php
use Kirby\Toolkit\I18n;
return function ($kirby) {
return [
'breadcrumbLabel' => function () use ($kirby) {
return $kirby->site()->title()->or(t('view.site'))->toString();
},
'icon' => 'home',
'label' => $kirby->site()->blueprint()->title() ?? t('view.site'),
'menu' => true,
'dialogs' => require __DIR__ . '/site/dialogs.php',
'dropdowns' => require __DIR__ . '/site/dropdowns.php',
'searches' => require __DIR__ . '/site/searches.php',
'views' => require __DIR__ . '/site/views.php',
];
return [
'breadcrumbLabel' => function () use ($kirby) {
return $kirby->site()->title()->or(I18n::translate('view.site'))->toString();
},
'icon' => 'home',
'label' => $kirby->site()->blueprint()->title() ?? I18n::translate('view.site'),
'menu' => true,
'dialogs' => require __DIR__ . '/site/dialogs.php',
'dropdowns' => require __DIR__ . '/site/dropdowns.php',
'searches' => require __DIR__ . '/site/searches.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';
return [
'changes' => [
'pattern' => 'changes',
'options' => fn () => Dropdown::changes()
],
'page' => [
'pattern' => 'pages/(:any)',
'options' => function (string $path) {
return Find::page($path)->panel()->dropdown();
}
],
'page.file' => [
'pattern' => '(pages/.*?)/files/(:any)',
'options' => $files['file']
],
'site.file' => [
'pattern' => '(site)/files/(:any)',
'options' => $files['file']
]
'changes' => [
'pattern' => 'changes',
'options' => fn () => Dropdown::changes()
],
'page' => [
'pattern' => 'pages/(:any)',
'options' => function (string $path) {
return Find::page($path)->panel()->dropdown();
}
],
'page.file' => [
'pattern' => '(pages/.*?)/files/(:any)',
'options' => $files['file']
],
'site.file' => [
'pattern' => '(site)/files/(:any)',
'options' => $files['file']
]
];

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,17 +1,17 @@
<?php
return [
'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`
*/
'marks' => function ($marks = true) {
return $marks;
}
],
'computed' => [
'value' => function () {
return trim($this->value ?? '');
}
]
'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`
*/
'marks' => function ($marks = true) {
return $marks;
}
],
'computed' => [
'value' => function () {
return trim($this->value ?? '');
}
]
];

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,69 +5,69 @@ use Kirby\Toolkit\A;
use Kirby\Toolkit\I18n;
return [
'props' => [
/**
* Unset inherited props
*/
'placeholder' => null,
'props' => [
/**
* Unset inherited props
*/
'placeholder' => null,
/**
* Default value which will be saved when a new page/user/file is created
*/
'default' => function ($default = null) {
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.
*/
'text' => function ($value = null) {
$model = $this->model();
/**
* Default value which will be saved when a new page/user/file is created
*/
'default' => function ($default = null) {
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.
*/
'text' => function ($value = null) {
$model = $this->model();
if (is_array($value) === true) {
if (A::isAssociative($value) === true) {
return $model->toSafeString(I18n::translate($value, $value));
}
if (is_array($value) === true) {
if (A::isAssociative($value) === true) {
return $model->toSafeString(I18n::translate($value, $value));
}
foreach ($value as $key => $val) {
$value[$key] = $model->toSafeString(I18n::translate($val, $val));
}
foreach ($value as $key => $val) {
$value[$key] = $model->toSafeString(I18n::translate($val, $val));
}
return $value;
}
return $value;
}
if (empty($value) === false) {
return $model->toSafeString(I18n::translate($value, $value));
}
if (empty($value) === false) {
return $model->toSafeString(I18n::translate($value, $value));
}
return $value;
},
],
'computed' => [
'default' => function () {
return $this->toBool($this->default);
},
'value' => function () {
if ($this->props['value'] === null) {
return $this->default();
} else {
return $this->toBool($this->props['value']);
}
}
],
'methods' => [
'toBool' => function ($value) {
return in_array($value, [true, 'true', 1, '1', 'on'], true) === true;
}
],
'save' => function (): string {
return $this->value() === true ? 'true' : 'false';
},
'validations' => [
'boolean',
'required' => function ($value) {
if ($this->isRequired() && ($value === false || $this->isEmpty($value))) {
throw new InvalidArgumentException(I18n::translate('field.required'));
}
},
]
return $value;
},
],
'computed' => [
'default' => function () {
return $this->toBool($this->default);
},
'value' => function () {
if ($this->props['value'] === null) {
return $this->default();
} else {
return $this->toBool($this->props['value']);
}
}
],
'methods' => [
'toBool' => function ($value) {
return in_array($value, [true, 'true', 1, '1', 'on'], true) === true;
}
],
'save' => function (): string {
return $this->value() === true ? 'true' : 'false';
},
'validations' => [
'boolean',
'required' => function ($value) {
if ($this->isRequired() && ($value === false || $this->isEmpty($value))) {
throw new InvalidArgumentException(I18n::translate('field.required'));
}
},
]
];

View file

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

View file

@ -3,39 +3,40 @@
use Kirby\Toolkit\I18n;
return [
'extends' => 'text',
'props' => [
/**
* Unset inherited props
*/
'converter' => null,
'counter' => null,
'spellcheck' => null,
'extends' => 'text',
'props' => [
/**
* Unset inherited props
*/
'converter' => null,
'counter' => null,
'pattern' => null,
'spellcheck' => null,
/**
* Sets the HTML5 autocomplete attribute
*/
'autocomplete' => function (string $autocomplete = 'url') {
return $autocomplete;
},
/**
* Sets the HTML5 autocomplete attribute
*/
'autocomplete' => function (string $autocomplete = 'url') {
return $autocomplete;
},
/**
* Changes the link icon
*/
'icon' => function (string $icon = 'url') {
return $icon;
},
/**
* Changes the link icon
*/
'icon' => function (string $icon = 'url') {
return $icon;
},
/**
* Sets custom placeholder text, when the field is empty
*/
'placeholder' => function ($value = null) {
return I18n::translate($value, $value) ?? 'https://example.com';
}
],
'validations' => [
'minlength',
'maxlength',
'url'
],
/**
* Sets custom placeholder text, when the field is empty
*/
'placeholder' => function ($value = null) {
return I18n::translate($value, $value) ?? 'https://example.com';
}
],
'validations' => [
'minlength',
'maxlength',
'url'
],
];

View file

@ -1,104 +1,105 @@
<?php
use Kirby\Cms\App;
use Kirby\Data\Data;
use Kirby\Toolkit\A;
return [
'mixins' => [
'layout',
'min',
'picker',
'userpicker'
],
'props' => [
/**
* Unset inherited props
*/
'after' => null,
'autofocus' => null,
'before' => null,
'icon' => null,
'placeholder' => null,
'mixins' => [
'layout',
'min',
'picker',
'userpicker'
],
'props' => [
/**
* Unset inherited props
*/
'after' => null,
'autofocus' => null,
'before' => null,
'icon' => null,
'placeholder' => null,
/**
* Default selected user(s) when a new page/file/user is created
*/
'default' => function ($default = null) {
if ($default === false) {
return [];
}
/**
* Default selected user(s) when a new page/file/user is created
*/
'default' => function ($default = null) {
if ($default === false) {
return [];
}
if ($default === null && $user = $this->kirby()->user()) {
return [
$this->userResponse($user)
];
}
if ($default === null && $user = $this->kirby()->user()) {
return [
$this->userResponse($user)
];
}
return $this->toUsers($default);
},
return $this->toUsers($default);
},
'value' => function ($value = null) {
return $this->toUsers($value);
},
],
'computed' => [
/**
* Unset inherited computed
*/
'default' => null
],
'methods' => [
'userResponse' => function ($user) {
return $user->panel()->pickerData([
'info' => $this->info,
'image' => $this->image,
'layout' => $this->layout,
'text' => $this->text,
]);
},
'toUsers' => function ($value = null) {
$users = [];
$kirby = kirby();
'value' => function ($value = null) {
return $this->toUsers($value);
},
],
'computed' => [
/**
* Unset inherited computed
*/
'default' => null
],
'methods' => [
'userResponse' => function ($user) {
return $user->panel()->pickerData([
'info' => $this->info,
'image' => $this->image,
'layout' => $this->layout,
'text' => $this->text,
]);
},
'toUsers' => function ($value = null) {
$users = [];
$kirby = App::instance();
foreach (Data::decode($value, 'yaml') as $email) {
if (is_array($email) === true) {
$email = $email['email'] ?? null;
}
foreach (Data::decode($value, 'yaml') as $email) {
if (is_array($email) === true) {
$email = $email['email'] ?? null;
}
if ($email !== null && ($user = $kirby->user($email))) {
$users[] = $this->userResponse($user);
}
}
if ($email !== null && ($user = $kirby->user($email))) {
$users[] = $this->userResponse($user);
}
}
return $users;
}
],
'api' => function () {
return [
[
'pattern' => '/',
'action' => function () {
$field = $this->field();
return $users;
}
],
'api' => function () {
return [
[
'pattern' => '/',
'action' => function () {
$field = $this->field();
return $field->userpicker([
'image' => $field->image(),
'info' => $field->info(),
'layout' => $field->layout(),
'limit' => $field->limit(),
'page' => $this->requestQuery('page'),
'query' => $field->query(),
'search' => $this->requestQuery('search'),
'text' => $field->text()
]);
}
]
];
},
'save' => function ($value = null) {
return A::pluck($value, 'id');
},
'validations' => [
'max',
'min'
]
return $field->userpicker([
'image' => $field->image(),
'info' => $field->info(),
'layout' => $field->layout(),
'limit' => $field->limit(),
'page' => $this->requestQuery('page'),
'query' => $field->query(),
'search' => $this->requestQuery('search'),
'text' => $field->text()
]);
}
]
];
},
'save' => function ($value = null) {
return A::pluck($value, 'id');
},
'validations' => [
'max',
'min'
]
];

View file

@ -3,34 +3,34 @@
use Kirby\Sane\Sane;
return [
'props' => [
/**
* Enables inline mode, which will not wrap new lines in paragraphs and creates hard breaks instead.
*
* @param bool $inline
*/
'inline' => function (bool $inline = false) {
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`
* @param array|bool $marks
*/
'marks' => function ($marks = true) {
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`.
* @param array|bool|null $nodes
*/
'nodes' => function ($nodes = null) {
return $nodes;
}
],
'computed' => [
'value' => function () {
$value = trim($this->value ?? '');
return Sane::sanitize($value, 'html');
}
],
'props' => [
/**
* Enables inline mode, which will not wrap new lines in paragraphs and creates hard breaks instead.
*
* @param bool $inline
*/
'inline' => function (bool $inline = false) {
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`
* @param array|bool $marks
*/
'marks' => function ($marks = true) {
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`.
* @param array|bool|null $nodes
*/
'nodes' => function ($nodes = null) {
return $nodes;
}
],
'computed' => [
'value' => function () {
$value = trim($this->value ?? '');
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

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

View file

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

View file

@ -1,57 +1,58 @@
<?php
use Kirby\Toolkit\I18n;
return function (array $props) {
// load the general templates setting for all sections
$templates = $props['templates'] ?? null;
// load the general templates setting for all sections
$templates = $props['templates'] ?? null;
$section = function ($headline, $status, $props) use ($templates) {
$defaults = [
'headline' => $headline,
'type' => 'pages',
'layout' => 'list',
'status' => $status
];
$section = function ($headline, $status, $props) use ($templates) {
$defaults = [
'headline' => $headline,
'type' => 'pages',
'layout' => 'list',
'status' => $status
];
if ($props === true) {
$props = [];
}
if ($props === true) {
$props = [];
}
if (is_string($props) === true) {
$props = [
'headline' => $props
];
}
if (is_string($props) === true) {
$props = [
'headline' => $props
];
}
// inject the global templates definition
if (empty($templates) === false) {
$props['templates'] = $props['templates'] ?? $templates;
}
// inject the global templates definition
if (empty($templates) === false) {
$props['templates'] = $props['templates'] ?? $templates;
}
return array_replace_recursive($defaults, $props);
};
return array_replace_recursive($defaults, $props);
};
$sections = [];
$sections = [];
$drafts = $props['drafts'] ?? [];
$unlisted = $props['unlisted'] ?? false;
$listed = $props['listed'] ?? [];
$drafts = $props['drafts'] ?? [];
$unlisted = $props['unlisted'] ?? false;
$listed = $props['listed'] ?? [];
if ($drafts !== false) {
$sections['drafts'] = $section(t('pages.status.draft'), 'drafts', $drafts);
}
if ($drafts !== false) {
$sections['drafts'] = $section(I18n::translate('pages.status.draft'), 'drafts', $drafts);
}
if ($unlisted !== false) {
$sections['unlisted'] = $section(t('pages.status.unlisted'), 'unlisted', $unlisted);
}
if ($unlisted !== false) {
$sections['unlisted'] = $section(I18n::translate('pages.status.unlisted'), 'unlisted', $unlisted);
}
if ($listed !== false) {
$sections['listed'] = $section(t('pages.status.listed'), 'listed', $listed);
}
if ($listed !== false) {
$sections['listed'] = $section(I18n::translate('pages.status.listed'), 'listed', $listed);
}
// cleaning up
unset($props['drafts'], $props['unlisted'], $props['listed'], $props['templates']);
// cleaning up
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,140 @@ use Kirby\Panel\Plugins;
use Kirby\Toolkit\Str;
return function ($kirby) {
$api = $kirby->option('api.slug', 'api');
$panel = $kirby->option('panel.slug', 'panel');
$index = $kirby->url('index');
$media = $kirby->url('media');
$api = $kirby->option('api.slug', 'api');
$panel = $kirby->option('panel.slug', 'panel');
$index = $kirby->url('index');
$media = $kirby->url('media');
if (Str::startsWith($media, $index) === true) {
$media = Str::after($media, $index);
} else {
// media URL is outside of the site, we can't make routing work;
// fall back to the standard media route
$media = 'media';
}
if (Str::startsWith($media, $index) === true) {
$media = Str::after($media, $index);
} else {
// media URL is outside of the site, we can't make routing work;
// fall back to the standard media route
$media = 'media';
}
/**
* Before routes are running before the
* plugin routes and cannot be overwritten by
* plugins.
*/
$before = [
[
'pattern' => $api . '/(:all)',
'method' => 'ALL',
'env' => 'api',
'action' => function ($path = null) use ($kirby) {
if ($kirby->option('api') === false) {
return null;
}
/**
* Before routes are running before the
* plugin routes and cannot be overwritten by
* plugins.
*/
$before = [
[
'pattern' => $api . '/(:all)',
'method' => 'ALL',
'env' => 'api',
'action' => function ($path = null) use ($kirby) {
if ($kirby->option('api') === false) {
return null;
}
$request = $kirby->request();
$request = $kirby->request();
return $kirby->api()->render($path, $this->method(), [
'body' => $request->body()->toArray(),
'files' => $request->files()->toArray(),
'headers' => $request->headers(),
'query' => $request->query()->toArray(),
]);
}
],
[
'pattern' => $media . '/plugins/index.(css|js)',
'env' => 'media',
'action' => function (string $type) use ($kirby) {
$plugins = new Plugins();
return $kirby->api()->render($path, $this->method(), [
'body' => $request->body()->toArray(),
'files' => $request->files()->toArray(),
'headers' => $request->headers(),
'query' => $request->query()->toArray(),
]);
}
],
[
'pattern' => $media . '/plugins/index.(css|js)',
'env' => 'media',
'action' => function (string $type) use ($kirby) {
$plugins = new Plugins();
return $kirby
->response()
->type($type)
->body($plugins->read($type));
}
],
[
'pattern' => $media . '/plugins/(:any)/(:any)/(:all).(css|map|gif|js|mjs|jpg|png|svg|webp|avif|woff2|woff|json)',
'env' => 'media',
'action' => function (string $provider, string $pluginName, string $filename, string $extension) {
return PluginAssets::resolve($provider . '/' . $pluginName, $filename . '.' . $extension);
}
],
[
'pattern' => $media . '/pages/(:all)/(:any)/(:any)',
'env' => 'media',
'action' => function ($path, $hash, $filename) use ($kirby) {
return Media::link($kirby->page($path), $hash, $filename);
}
],
[
'pattern' => $media . '/site/(:any)/(:any)',
'env' => 'media',
'action' => function ($hash, $filename) use ($kirby) {
return Media::link($kirby->site(), $hash, $filename);
}
],
[
'pattern' => $media . '/users/(:any)/(:any)/(:any)',
'env' => 'media',
'action' => function ($id, $hash, $filename) use ($kirby) {
return Media::link($kirby->user($id), $hash, $filename);
}
],
[
'pattern' => $media . '/assets/(:all)/(:any)/(:any)',
'env' => 'media',
'action' => function ($path, $hash, $filename) {
return Media::thumb($path, $hash, $filename);
}
],
[
'pattern' => $panel . '/(:all?)',
'method' => 'ALL',
'env' => 'panel',
'action' => function ($path = null) {
return Panel::router($path);
}
],
];
return $kirby
->response()
->type($type)
->body($plugins->read($type));
}
],
[
'pattern' => $media . '/plugins/(:any)/(:any)/(:all).(css|map|gif|js|mjs|jpg|png|svg|webp|avif|woff2|woff|json)',
'env' => 'media',
'action' => function (string $provider, string $pluginName, string $filename, string $extension) {
return PluginAssets::resolve($provider . '/' . $pluginName, $filename . '.' . $extension);
}
],
[
'pattern' => $media . '/pages/(:all)/(:any)/(:any)',
'env' => 'media',
'action' => function ($path, $hash, $filename) use ($kirby) {
return Media::link($kirby->page($path), $hash, $filename);
}
],
[
'pattern' => $media . '/site/(:any)/(:any)',
'env' => 'media',
'action' => function ($hash, $filename) use ($kirby) {
return Media::link($kirby->site(), $hash, $filename);
}
],
[
'pattern' => $media . '/users/(:any)/(:any)/(:any)',
'env' => 'media',
'action' => function ($id, $hash, $filename) use ($kirby) {
return Media::link($kirby->user($id), $hash, $filename);
}
],
[
'pattern' => $media . '/assets/(:all)/(:any)/(:any)',
'env' => 'media',
'action' => function ($path, $hash, $filename) {
return Media::thumb($path, $hash, $filename);
}
],
[
'pattern' => $panel . '/(:all?)',
'method' => 'ALL',
'env' => 'panel',
'action' => function ($path = null) {
return Panel::router($path);
}
],
];
// Multi-language setup
if ($kirby->multilang() === true) {
$after = LanguageRoutes::create($kirby);
} else {
// Multi-language setup
if ($kirby->multilang() === true) {
$after = LanguageRoutes::create($kirby);
} else {
// Single-language home
$after[] = [
'pattern' => '',
'method' => 'ALL',
'env' => 'site',
'action' => function () use ($kirby) {
return $kirby->resolve();
}
];
// Single-language home
$after[] = [
'pattern' => '',
'method' => 'ALL',
'env' => 'site',
'action' => function () use ($kirby) {
return $kirby->resolve();
}
];
// redirect the home page folder to the real homepage
$after[] = [
'pattern' => $kirby->option('home', 'home'),
'method' => 'ALL',
'env' => 'site',
'action' => function () use ($kirby) {
return $kirby
->response()
->redirect($kirby->site()->url());
}
];
// redirect the home page folder to the real homepage
$after[] = [
'pattern' => $kirby->option('home', 'home'),
'method' => 'ALL',
'env' => 'site',
'action' => function () use ($kirby) {
return $kirby
->response()
->redirect($kirby->site()->url());
}
];
// Single-language subpages
$after[] = [
'pattern' => '(:all)',
'method' => 'ALL',
'env' => 'site',
'action' => function (string $path) use ($kirby) {
return $kirby->resolve($path);
}
];
}
// Single-language subpages
$after[] = [
'pattern' => '(:all)',
'method' => 'ALL',
'env' => 'site',
'action' => function (string $path) use ($kirby) {
return $kirby->resolve($path);
}
];
}
return [
'before' => $before,
'after' => $after
];
return [
'before' => $before,
'after' => $after
];
};

View file

@ -3,55 +3,55 @@
use Kirby\Form\Form;
return [
'props' => [
'fields' => function (array $fields = []) {
return $fields;
}
],
'computed' => [
'form' => function () {
$fields = $this->fields;
$disabled = $this->model->permissions()->update() === false;
$lang = $this->model->kirby()->languageCode();
$content = $this->model->content($lang)->toArray();
'props' => [
'fields' => function (array $fields = []) {
return $fields;
}
],
'computed' => [
'form' => function () {
$fields = $this->fields;
$disabled = $this->model->permissions()->update() === false;
$lang = $this->model->kirby()->languageCode();
$content = $this->model->content($lang)->toArray();
if ($disabled === true) {
foreach ($fields as $key => $props) {
$fields[$key]['disabled'] = true;
}
}
if ($disabled === true) {
foreach ($fields as $key => $props) {
$fields[$key]['disabled'] = true;
}
}
return new Form([
'fields' => $fields,
'values' => $content,
'model' => $this->model,
'strict' => true
]);
},
'fields' => function () {
$fields = $this->form->fields()->toArray();
return new Form([
'fields' => $fields,
'values' => $content,
'model' => $this->model,
'strict' => true
]);
},
'fields' => function () {
$fields = $this->form->fields()->toArray();
if (is_a($this->model, 'Kirby\Cms\Page') === true || is_a($this->model, 'Kirby\Cms\Site') === true) {
// the title should never be updated directly via
// fields section to avoid conflicts with the rename dialog
unset($fields['title']);
}
if (is_a($this->model, 'Kirby\Cms\Page') === true || is_a($this->model, 'Kirby\Cms\Site') === true) {
// the title should never be updated directly via
// fields section to avoid conflicts with the rename dialog
unset($fields['title']);
}
foreach ($fields as $index => $props) {
unset($fields[$index]['value']);
}
foreach ($fields as $index => $props) {
unset($fields[$index]['value']);
}
return $fields;
}
],
'methods' => [
'errors' => function () {
return $this->form->errors();
}
],
'toArray' => function () {
return [
'fields' => $this->fields,
];
}
return $fields;
}
],
'methods' => [
'errors' => function () {
return $this->form->errors();
}
],
'toArray' => function () {
return [
'fields' => $this->fields,
];
}
];

View file

@ -4,242 +4,203 @@ use Kirby\Cms\File;
use Kirby\Toolkit\I18n;
return [
'mixins' => [
'empty',
'headline',
'help',
'layout',
'min',
'max',
'pagination',
'parent',
],
'props' => [
/**
* Enables/disables reverse sorting
*/
'flip' => function (bool $flip = false) {
return $flip;
},
/**
* Image options to control the source and look of file previews
*/
'image' => function ($image = null) {
return $image ?? [];
},
/**
* Optional info text setup. Info text is shown on the right (lists, cardlets) or below (cards) the filename.
*/
'info' => function ($info = null) {
return I18n::translate($info, $info);
},
/**
* The size option controls the size of cards. By default cards are auto-sized and the cards grid will always fill the full width. With a size you can disable auto-sizing. Available sizes: `tiny`, `small`, `medium`, `large`, `huge`
*/
'size' => function (string $size = 'auto') {
return $size;
},
/**
* Enables/disables manual sorting
*/
'sortable' => function (bool $sortable = true) {
return $sortable;
},
/**
* Overwrites manual sorting and sorts by the given field and sorting direction (i.e. `filename desc`)
*/
'sortBy' => function (string $sortBy = null) {
return $sortBy;
},
/**
* Filters all files by template and also sets the template, which will be used for all uploads
*/
'template' => function (string $template = null) {
return $template;
},
/**
* Setup for the main text in the list or cards. By default this will display the filename.
*/
'text' => function ($text = '{{ file.filename }}') {
return I18n::translate($text, $text);
}
],
'computed' => [
'accept' => function () {
if ($this->template) {
$file = new File([
'filename' => 'tmp',
'parent' => $this->model(),
'template' => $this->template
]);
'mixins' => [
'details',
'empty',
'headline',
'help',
'layout',
'min',
'max',
'pagination',
'parent',
'search',
'sort'
],
'props' => [
/**
* Filters all files by template and also sets the template, which will be used for all uploads
*/
'template' => function (string $template = null) {
return $template;
},
/**
* Setup for the main text in the list or cards. By default this will display the filename.
*/
'text' => function ($text = '{{ file.filename }}') {
return I18n::translate($text, $text);
}
],
'computed' => [
'accept' => function () {
if ($this->template) {
$file = new File([
'filename' => 'tmp',
'parent' => $this->model(),
'template' => $this->template
]);
return $file->blueprint()->acceptMime();
}
return $file->blueprint()->acceptMime();
}
return null;
},
'parent' => function () {
return $this->parentModel();
},
'files' => function () {
$files = $this->parent->files()->template($this->template);
return null;
},
'parent' => function () {
return $this->parentModel();
},
'files' => function () {
$files = $this->parent->files()->template($this->template);
// filter out all protected files
$files = $files->filter('isReadable', true);
// filter out all protected files
$files = $files->filter('isReadable', true);
if ($this->sortBy) {
$files = $files->sort(...$files::sortArgs($this->sortBy));
} else {
$files = $files->sorted();
}
// search
if ($this->search === true && empty($this->searchterm()) === false) {
$files = $files->search($this->searchterm());
}
// flip
if ($this->flip === true) {
$files = $files->flip();
}
// sort
if ($this->sortBy) {
$files = $files->sort(...$files::sortArgs($this->sortBy));
} else {
$files = $files->sorted();
}
// apply the default pagination
$files = $files->paginate([
'page' => $this->page,
'limit' => $this->limit,
'method' => 'none' // the page is manually provided
]);
// flip
if ($this->flip === true) {
$files = $files->flip();
}
return $files;
},
'data' => function () {
$data = [];
// apply the default pagination
$files = $files->paginate([
'page' => $this->page,
'limit' => $this->limit,
'method' => 'none' // the page is manually provided
]);
// the drag text needs to be absolute when the files come from
// a different parent model
$dragTextAbsolute = $this->model->is($this->parent) === false;
return $files;
},
'data' => function () {
$data = [];
foreach ($this->files as $file) {
$panel = $file->panel();
// the drag text needs to be absolute when the files come from
// a different parent model
$dragTextAbsolute = $this->model->is($this->parent) === false;
$data[] = [
'dragText' => $panel->dragText('auto', $dragTextAbsolute),
'extension' => $file->extension(),
'filename' => $file->filename(),
'id' => $file->id(),
'image' => $panel->image($this->image, $this->layout),
'info' => $file->toSafeString($this->info ?? false),
'link' => $panel->url(true),
'mime' => $file->mime(),
'parent' => $file->parent()->panel()->path(),
'template' => $file->template(),
'text' => $file->toSafeString($this->text),
'url' => $file->url(),
];
}
foreach ($this->files as $file) {
$panel = $file->panel();
return $data;
},
'total' => function () {
return $this->files->pagination()->total();
},
'errors' => function () {
$errors = [];
$item = [
'dragText' => $panel->dragText('auto', $dragTextAbsolute),
'extension' => $file->extension(),
'filename' => $file->filename(),
'id' => $file->id(),
'image' => $panel->image(
$this->image,
$this->layout === 'table' ? 'list' : $this->layout
),
'info' => $file->toSafeString($this->info ?? false),
'link' => $panel->url(true),
'mime' => $file->mime(),
'parent' => $file->parent()->panel()->path(),
'template' => $file->template(),
'text' => $file->toSafeString($this->text),
'url' => $file->url(),
];
if ($this->validateMax() === false) {
$errors['max'] = I18n::template('error.section.files.max.' . I18n::form($this->max), [
'max' => $this->max,
'section' => $this->headline
]);
}
if ($this->layout === 'table') {
$item = $this->columnsValues($item, $file);
}
if ($this->validateMin() === false) {
$errors['min'] = I18n::template('error.section.files.min.' . I18n::form($this->min), [
'min' => $this->min,
'section' => $this->headline
]);
}
$data[] = $item;
}
if (empty($errors) === true) {
return [];
}
return $data;
},
'total' => function () {
return $this->files->pagination()->total();
},
'errors' => function () {
$errors = [];
return [
$this->name => [
'label' => $this->headline,
'message' => $errors,
]
];
},
'link' => function () {
$modelLink = $this->model->panel()->url(true);
$parentLink = $this->parent->panel()->url(true);
if ($this->validateMax() === false) {
$errors['max'] = I18n::template('error.section.files.max.' . I18n::form($this->max), [
'max' => $this->max,
'section' => $this->headline
]);
}
if ($modelLink !== $parentLink) {
return $parentLink;
}
},
'pagination' => function () {
return $this->pagination();
},
'sortable' => function () {
if ($this->sortable === false) {
return false;
}
if ($this->validateMin() === false) {
$errors['min'] = I18n::template('error.section.files.min.' . I18n::form($this->min), [
'min' => $this->min,
'section' => $this->headline
]);
}
if ($this->sortBy !== null) {
return false;
}
if (empty($errors) === true) {
return [];
}
if ($this->flip === true) {
return false;
}
return [
$this->name => [
'label' => $this->headline,
'message' => $errors,
]
];
},
'pagination' => function () {
return $this->pagination();
},
'upload' => function () {
if ($this->isFull() === true) {
return false;
}
return true;
},
'upload' => function () {
if ($this->isFull() === true) {
return false;
}
// count all uploaded files
$total = count($this->data);
$max = $this->max ? $this->max - $total : null;
// count all uploaded files
$total = count($this->data);
$max = $this->max ? $this->max - $total : null;
if ($this->max && $total === $this->max - 1) {
$multiple = false;
} else {
$multiple = true;
}
if ($this->max && $total === $this->max - 1) {
$multiple = false;
} else {
$multiple = true;
}
$template = $this->template === 'default' ? null : $this->template;
$template = $this->template === 'default' ? null : $this->template;
return [
'accept' => $this->accept,
'multiple' => $multiple,
'max' => $max,
'api' => $this->parent->apiUrl(true) . '/files',
'attributes' => array_filter([
'sort' => $this->sortable === true ? $total + 1 : null,
'template' => $template
])
];
}
],
'toArray' => function () {
return [
'data' => $this->data,
'errors' => $this->errors,
'options' => [
'accept' => $this->accept,
'apiUrl' => $this->parent->apiUrl(true),
'empty' => $this->empty,
'headline' => $this->headline,
'help' => $this->help,
'layout' => $this->layout,
'link' => $this->link,
'max' => $this->max,
'min' => $this->min,
'size' => $this->size,
'sortable' => $this->sortable,
'upload' => $this->upload
],
'pagination' => $this->pagination
];
}
return [
'accept' => $this->accept,
'multiple' => $multiple,
'max' => $max,
'api' => $this->parent->apiUrl(true) . '/files',
'attributes' => array_filter([
'sort' => $this->sortable === true ? $total + 1 : null,
'template' => $template
])
];
}
],
'toArray' => function () {
return [
'data' => $this->data,
'errors' => $this->errors,
'options' => [
'accept' => $this->accept,
'apiUrl' => $this->parent->apiUrl(true),
'columns' => $this->columns,
'empty' => $this->empty,
'headline' => $this->headline,
'help' => $this->help,
'layout' => $this->layout,
'link' => $this->link(),
'max' => $this->max,
'min' => $this->min,
'search' => $this->search,
'size' => $this->size,
'sortable' => $this->sortable,
'upload' => $this->upload
],
'pagination' => $this->pagination
];
}
];

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