1
0

downgrade to kirby v3

This commit is contained in:
Philip Wagner
2024-09-01 10:47:15 +02:00
parent a4b2aece7b
commit af86acb7a1
1085 changed files with 54743 additions and 65042 deletions

View File

@@ -3,6 +3,7 @@
return [
// cms classes
'collection' => 'Kirby\Cms\Collection',
'field' => 'Kirby\Cms\Field',
'file' => 'Kirby\Cms\File',
'files' => 'Kirby\Cms\Files',
'find' => 'Kirby\Cms\Find',
@@ -23,9 +24,6 @@ return [
'users' => 'Kirby\Cms\Users',
'visitor' => 'Kirby\Cms\Visitor',
// content classes
'field' => 'Kirby\Content\Field',
// data handler
'data' => 'Kirby\Data\Data',
'json' => 'Kirby\Data\Json',
@@ -48,6 +46,7 @@ return [
'cookie' => 'Kirby\Http\Cookie',
'header' => 'Kirby\Http\Header',
'remote' => 'Kirby\Http\Remote',
'server' => 'Kirby\Http\Server',
// image classes
'dimensions' => 'Kirby\Image\Dimensions',
@@ -55,10 +54,6 @@ return [
// panel classes
'panel' => 'Kirby\Panel\Panel',
// template classes
'snippet' => 'Kirby\Template\Snippet',
'slot' => 'Kirby\Template\Slot',
// toolkit classes
'a' => 'Kirby\Toolkit\A',
'c' => 'Kirby\Toolkit\Config',
@@ -71,25 +66,16 @@ return [
'v' => 'Kirby\Toolkit\V',
'xml' => 'Kirby\Toolkit\Xml',
// Deprecated aliases:
// Any of these might be removed at any point in the future
'kirby\cms\asset' => 'Kirby\Filesystem\Asset',
'kirby\cms\content' => 'Kirby\Content\Content',
'kirby\cms\contenttranslation' => 'Kirby\Content\ContentTranslation',
'kirby\cms\dir' => 'Kirby\Filesystem\Dir',
'kirby\cms\filename' => 'Kirby\Filesystem\Filename',
'kirby\cms\filefoundation' => 'Kirby\Filesystem\IsFile',
'kirby\cms\field' => 'Kirby\Content\Field',
'kirby\cms\form' => 'Kirby\Form\Form',
'kirby\cms\kirbytag' => 'Kirby\Text\KirbyTag',
'kirby\cms\kirbytags' => 'Kirby\Text\KirbyTags',
'kirby\cms\template' => 'Kirby\Template\Template',
'kirby\form\options' => 'Kirby\Option\Options',
'kirby\form\optionsapi' => 'Kirby\Option\OptionsApi',
'kirby\form\optionsquery' => 'Kirby\Option\OptionsQuery',
'kirby\toolkit\dir' => 'Kirby\Filesystem\Dir',
'kirby\toolkit\f' => 'Kirby\Filesystem\F',
'kirby\toolkit\file' => 'Kirby\Filesystem\File',
'kirby\toolkit\mime' => 'Kirby\Filesystem\Mime',
'kirby\toolkit\query' => 'Kirby\Query\Query',
// 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

@@ -1,6 +1,6 @@
<?php
use Kirby\Exception\AuthException;
use Kirby\Exception\PermissionException;
return function () {
$auth = $this->kirby()->auth();
@@ -11,17 +11,17 @@ return function () {
$auth->type($allowImpersonation) === 'session' &&
$auth->csrf() === false
) {
throw new AuthException('Unauthenticated');
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 AuthException(['key' => 'access.panel']);
throw new PermissionException(['key' => 'access.panel']);
}
return $user;
}
throw new AuthException('Unauthenticated');
throw new PermissionException('Unauthenticated');
};

View File

@@ -3,14 +3,6 @@
/**
* Api Collection Definitions
*/
use Kirby\Cms\Files;
use Kirby\Cms\Languages;
use Kirby\Cms\Pages;
use Kirby\Cms\Roles;
use Kirby\Cms\Translations;
use Kirby\Cms\Users;
return [
/**
@@ -18,7 +10,7 @@ return [
*/
'children' => [
'model' => 'page',
'type' => Pages::class,
'type' => 'Kirby\Cms\Pages',
'view' => 'compact'
],
@@ -27,7 +19,7 @@ return [
*/
'files' => [
'model' => 'file',
'type' => Files::class,
'type' => 'Kirby\Cms\Files'
],
/**
@@ -35,7 +27,7 @@ return [
*/
'languages' => [
'model' => 'language',
'type' => Languages::class,
'type' => 'Kirby\Cms\Languages'
],
/**
@@ -43,7 +35,7 @@ return [
*/
'pages' => [
'model' => 'page',
'type' => Pages::class,
'type' => 'Kirby\Cms\Pages',
'view' => 'compact'
],
@@ -52,7 +44,7 @@ return [
*/
'roles' => [
'model' => 'role',
'type' => Roles::class,
'type' => 'Kirby\Cms\Roles',
'view' => 'compact'
],
@@ -61,7 +53,7 @@ return [
*/
'translations' => [
'model' => 'translation',
'type' => Translations::class,
'type' => 'Kirby\Cms\Translations',
'view' => 'compact'
],
@@ -71,7 +63,7 @@ return [
'users' => [
'default' => fn () => $this->users(),
'model' => 'user',
'type' => Users::class,
'type' => 'Kirby\Cms\Users',
'view' => 'compact'
]

View File

@@ -8,7 +8,6 @@ return [
'FileBlueprint' => include __DIR__ . '/models/FileBlueprint.php',
'FileVersion' => include __DIR__ . '/models/FileVersion.php',
'Language' => include __DIR__ . '/models/Language.php',
'License' => include __DIR__ . '/models/License.php',
'Page' => include __DIR__ . '/models/Page.php',
'PageBlueprint' => include __DIR__ . '/models/PageBlueprint.php',
'Role' => include __DIR__ . '/models/Role.php',

View File

@@ -57,9 +57,8 @@ return [
},
'type' => fn (File $file) => $file->type(),
'url' => fn (File $file) => $file->url(),
'uuid' => fn (File $file) => $file->uuid()?->toString()
],
'type' => File::class,
'type' => 'Kirby\Cms\File',
'views' => [
'default' => [
'content',
@@ -80,8 +79,7 @@ return [
'size',
'template',
'type',
'url',
'uuid'
'url'
],
'compact' => [
'filename',
@@ -89,7 +87,6 @@ return [
'link',
'type',
'url',
'uuid'
],
'panel' => [
'blueprint',
@@ -112,8 +109,7 @@ return [
'prevWithTemplate' => 'compact',
'template',
'type',
'url',
'uuid'
'url'
]
],
];

View File

@@ -12,6 +12,7 @@ return [
'tabs' => fn (FileBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (FileBlueprint $blueprint) => $blueprint->title(),
],
'type' => FileBlueprint::class,
'views' => [],
'type' => 'Kirby\Cms\FileBlueprint',
'views' => [
],
];

View File

@@ -20,7 +20,7 @@ return [
'type' => fn (FileVersion $file) => $file->type(),
'url' => fn (FileVersion $file) => $file->url(),
],
'type' => FileVersion::class,
'type' => 'Kirby\Cms\FileVersion',
'views' => [
'default' => [
'dimensions',

View File

@@ -15,7 +15,7 @@ return [
'rules' => fn (Language $language) => $language->rules(),
'url' => fn (Language $language) => $language->url(),
],
'type' => Language::class,
'type' => 'Kirby\Cms\Language',
'views' => [
'default' => [
'code',

View File

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

View File

@@ -1,5 +1,6 @@
<?php
use Kirby\Cms\Helpers;
use Kirby\Cms\Page;
use Kirby\Form\Form;
@@ -20,34 +21,62 @@ return [
'hasFiles' => fn (Page $page) => $page->hasFiles(),
'id' => fn (Page $page) => $page->id(),
'isSortable' => fn (Page $page) => $page->isSortable(),
'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(),
'previewUrl' => fn (Page $page) => $page->previewUrl(),
'siblings' => function (Page $page) {
/**
* @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();
}
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(),
'uuid' => fn (Page $page) => $page->uuid()?->toString()
],
'type' => Page::class,
'type' => 'Kirby\Cms\Page',
'views' => [
'compact' => [
'id',
'title',
'url',
'num',
'uuid'
'num'
],
'default' => [
'content',
@@ -59,8 +88,7 @@ return [
'slug',
'template',
'title',
'url',
'uuid'
'url'
],
'panel' => [
'id',
@@ -74,8 +102,7 @@ return [
'previewUrl',
'slug',
'title',
'url',
'uuid'
'url'
],
'selector' => [
'id',

View File

@@ -15,6 +15,7 @@ return [
'tabs' => fn (PageBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (PageBlueprint $blueprint) => $blueprint->title(),
],
'type' => PageBlueprint::class,
'views' => [],
'type' => 'Kirby\Cms\PageBlueprint',
'views' => [
],
];

View File

@@ -12,7 +12,7 @@ return [
'permissions' => fn (Role $role) => $role->permissions()->toArray(),
'title' => fn (Role $role) => $role->title(),
],
'type' => Role::class,
'type' => 'Kirby\Cms\Role',
'views' => [
'compact' => [
'description',

View File

@@ -19,7 +19,7 @@ return [
'title' => fn (Site $site) => $site->title()->value(),
'url' => fn (Site $site) => $site->url(),
],
'type' => Site::class,
'type' => 'Kirby\Cms\Site',
'views' => [
'compact' => [
'title',

View File

@@ -12,6 +12,6 @@ return [
'tabs' => fn (SiteBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (SiteBlueprint $blueprint) => $blueprint->title(),
],
'type' => SiteBlueprint::class,
'type' => 'Kirby\Cms\SiteBlueprint',
'views' => [],
];

View File

@@ -32,24 +32,31 @@ return [
'slugs' => fn () => Str::$language,
'title' => fn () => $this->site()->title()->value(),
'translation' => function () {
$code = $this->user()?->language() ??
$this->kirby()->panelLanguage();
if ($user = $this->user()) {
$translationCode = $user->language();
} else {
$translationCode = $this->kirby()->panelLanguage();
}
return
$this->kirby()->translation($code) ??
$this->kirby()->translation('en');
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 () {
if ($this->user()?->role()->permissions()->for('access', 'system') === true) {
return $this->kirby()->version();
}
$user = $this->user();
return null;
if ($user && $user->role()->permissions()->for('access', 'system') === true) {
return $this->kirby()->version();
} else {
return null;
}
}
],
'type' => System::class,
'type' => 'Kirby\Cms\System',
'views' => [
'login' => [
'authStatus',

View File

@@ -13,7 +13,7 @@ return [
'id' => fn (Translation $translation) => $translation->id(),
'name' => fn (Translation $translation) => $translation->name(),
],
'type' => Translation::class,
'type' => 'Kirby\Cms\Translation',
'views' => [
'compact' => [
'direction',

View File

@@ -9,7 +9,7 @@ use Kirby\Form\Form;
return [
'default' => fn () => $this->user(),
'fields' => [
'avatar' => fn (User $user) => $user->avatar()?->crop(512),
'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(),
@@ -24,10 +24,9 @@ return [
'prev' => fn (User $user) => $user->prev(),
'role' => fn (User $user) => $user->role(),
'roles' => fn (User $user) => $user->roles(),
'username' => fn (User $user) => $user->username(),
'uuid' => fn (User $user) => $user->uuid()?->toString()
'username' => fn (User $user) => $user->username()
],
'type' => User::class,
'type' => 'Kirby\Cms\User',
'views' => [
'default' => [
'avatar',
@@ -40,8 +39,7 @@ return [
'options',
'prev' => 'compact',
'role',
'username',
'uuid'
'username'
],
'compact' => [
'avatar' => 'compact',
@@ -50,8 +48,7 @@ return [
'language',
'name',
'role' => 'compact',
'username',
'uuid'
'username'
],
'auth' => [
'avatar' => 'compact',
@@ -75,7 +72,6 @@ return [
'prev' => ['id', 'name'],
'role',
'username',
'uuid'
],
]
];

View File

@@ -12,6 +12,7 @@ return [
'tabs' => fn (UserBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (UserBlueprint $blueprint) => $blueprint->title(),
],
'type' => UserBlueprint::class,
'views' => [],
'type' => 'Kirby\Cms\UserBlueprint',
'views' => [
],
];

View File

@@ -19,10 +19,7 @@ return function ($kirby) {
// 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'
);
$routes = array_merge($routes, include __DIR__ . '/routes/languages.php');
}
return $routes;

View File

@@ -70,11 +70,13 @@ return [
$user = $auth->login($email, $password, $long);
}
} else {
$mode = match (true) {
isset($methods['code']) => 'login',
isset($methods['password-reset']) => 'password-reset',
default => throw new InvalidArgumentException('Login without password is not enabled')
};
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);
}
@@ -85,13 +87,13 @@ return [
'status' => 'ok',
'user' => $this->resolve($user)->view('auth')->toArray()
];
} else {
return [
'code' => 200,
'status' => 'ok',
'challenge' => $status->challenge()
];
}
return [
'code' => 200,
'status' => 'ok',
'challenge' => $status->challenge()
];
}
],
[
@@ -103,14 +105,4 @@ return [
return true;
}
],
[
'pattern' => 'auth/ping',
'method' => 'POST',
'auth' => false,
'action' => function () {
// refresh the session timeout
$this->kirby()->session();
return true;
}
],
];

View File

@@ -1,148 +1,132 @@
<?php
// routing pattern to match all models with files
$filePattern = '(account/|pages/[^/]+/|site/|users/[^/]+/|)files/(:any)';
$parentPattern = '(account|pages/[^/]+|site|users/[^/]+)/files';
$pattern = '(account|pages/[^/]+|site|users/[^/]+)';
/**
* Files Routes
*/
return [
[
'pattern' => $filePattern . '/fields/(:any)/(:all?)',
'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|null $path = null) {
'action' => function (string $parent, string $filename, string $fieldName, string $path = null) {
if ($file = $this->file($parent, $filename)) {
return $this->fieldApi($file, $fieldName, $path);
}
}
],
[
'pattern' => $filePattern . '/sections/(:any)',
'method' => 'GET',
'action' => function (string $path, string $filename, string $sectionName) {
return $this->file($path, $filename)->blueprint()->section($sectionName)?->toResponse();
}
],
[
'pattern' => $filePattern . '/sections/(:any)/(:all?)',
'method' => 'ALL',
'action' => function (string $parent, string $filename, string $sectionName, string|null $path = null) {
if ($file = $this->file($parent, $filename)) {
return $this->sectionApi($file, $sectionName, $path);
}
}
],
[
'pattern' => $parentPattern,
'pattern' => $pattern . '/files',
'method' => 'GET',
'action' => function (string $path) {
return $this->files($path)->sorted();
return $this->parent($path)->files()->sorted();
}
],
[
'pattern' => $parentPattern,
'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) {
$props = [
return $this->parent($path)->createFile([
'content' => [
'sort' => $this->requestBody('sort')
],
'source' => $source,
'template' => $this->requestBody('template'),
'filename' => $filename
];
// move the source file from the temp dir
return $this->parent($path)->createFile($props, true);
]);
});
// @codeCoverageIgnoreEnd
}
],
[
'pattern' => $parentPattern . '/search',
'pattern' => $pattern . '/files/search',
'method' => 'GET|POST',
'action' => function (string $path) {
$files = $this->files($path);
$files = $this->parent($path)->files();
if ($this->requestMethod() === 'GET') {
return $files->search($this->requestQuery('q'));
} else {
return $files->query($this->requestBody());
}
return $files->query($this->requestBody());
}
],
[
'pattern' => $parentPattern . '/sort',
'pattern' => $pattern . '/files/sort',
'method' => 'PATCH',
'action' => function (string $path) {
return $this->files($path)->changeSort(
return $this->parent($path)->files()->changeSort(
$this->requestBody('files'),
$this->requestBody('index')
);
}
],
[
'pattern' => $filePattern,
'pattern' => $pattern . '/files/(:any)',
'method' => 'GET',
'action' => function (string $path, string $filename) {
return $this->file($path, $filename);
}
],
[
'pattern' => $filePattern,
'pattern' => $pattern . '/files/(:any)',
'method' => 'PATCH',
'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->update(
$this->requestBody(),
$this->language(),
true
);
return $this->file($path, $filename)->update($this->requestBody(), $this->language(), true);
}
],
[
'pattern' => $filePattern,
'pattern' => $pattern . '/files/(:any)',
'method' => 'POST',
'action' => function (string $path, string $filename) {
// move the source file from the temp dir
return $this->upload(
fn ($source) => $this->file($path, $filename)->replace($source, true)
);
return $this->upload(function ($source) use ($path, $filename) {
return $this->file($path, $filename)->replace($source);
});
}
],
[
'pattern' => $filePattern,
'pattern' => $pattern . '/files/(:any)',
'method' => 'DELETE',
'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->delete();
}
],
[
'pattern' => $filePattern . '/name',
'pattern' => $pattern . '/files/(:any)/name',
'method' => 'PATCH',
'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->changeName($this->requestBody('name'));
}
],
[
'pattern' => $parentPattern . '/search',
'pattern' => 'files/search',
'method' => 'GET|POST',
'action' => function () {
$files = $this
->site()
->index(true)
->filter('isListable', true)
->files()
->filter('isListable', true);
->filter('isReadable', true)
->files();
if ($this->requestMethod() === 'GET') {
return $files->search($this->requestQuery('q'));
} else {
return $files->query($this->requestBody());
}
return $files->query($this->requestBody());
}
],
];

View File

@@ -1,35 +0,0 @@
<?php
// @codeCoverageIgnoreStart
return [
'routes' => function ($kirby) {
return [
[
'pattern' => 'query',
'method' => 'POST|GET',
'auth' => $kirby->option('kql.auth') !== false,
'action' => function () use ($kirby) {
$kql = '\Kirby\Kql\Kql';
if (class_exists($kql) === false) {
return [
'code' => 500,
'status' => 'error',
'message' => 'KQL plugin is not installed',
];
}
$input = $kirby->request()->get();
$result = $kql::run($input);
return [
'code' => 200,
'result' => $result,
'status' => 'ok',
];
}
]
];
}
];
// @codeCoverageIgnoreEnd

View File

@@ -29,14 +29,18 @@ return [
'pattern' => 'languages/(:any)',
'method' => 'PATCH',
'action' => function (string $code) {
return $this->kirby()->languages()->find($code)?->update($this->requestBody());
if ($language = $this->kirby()->languages()->find($code)) {
return $language->update($this->requestBody());
}
}
],
[
'pattern' => 'languages/(:any)',
'method' => 'DELETE',
'action' => function (string $code) {
return $this->kirby()->languages()->find($code)?->delete();
if ($language = $this->kirby()->languages()->find($code)) {
return $language->delete();
}
}
]
];

View File

@@ -4,34 +4,22 @@
/**
* Content Lock Routes
*/
use Kirby\Exception\NotFoundException;
return [
[
'pattern' => '(:all)/lock',
'method' => 'GET',
'action' => function (string $path) {
return [
'lock' => $this->parent($path)->lock()?->toArray() ?? false
];
}
],
[
'pattern' => '(:all)/lock',
'method' => 'PATCH',
'action' => function (string $path) {
return $this->parent($path)->lock()?->create();
if ($lock = $this->parent($path)->lock()) {
return $lock->create();
}
}
],
[
'pattern' => '(:all)/lock',
'method' => 'DELETE',
'action' => function (string $path) {
try {
return $this->parent($path)->lock()?->remove();
} catch (NotFoundException) {
return true;
if ($lock = $this->parent($path)->lock()) {
return $lock->remove();
}
}
],
@@ -39,17 +27,17 @@ return [
'pattern' => '(:all)/unlock',
'method' => 'PATCH',
'action' => function (string $path) {
return $this->parent($path)->lock()?->unlock();
if ($lock = $this->parent($path)->lock()) {
return $lock->unlock();
}
}
],
[
'pattern' => '(:all)/unlock',
'method' => 'DELETE',
'action' => function (string $path) {
try {
return $this->parent($path)->lock()?->resolve();
} catch (NotFoundException) {
return true;
if ($lock = $this->parent($path)->lock()) {
return $lock->resolve();
}
}
],

View File

@@ -5,7 +5,7 @@
* Page Routes
*/
return [
// @codeCoverageIgnoreStart
[
'pattern' => 'pages/(:any)',
'method' => 'GET',
@@ -100,30 +100,22 @@ return [
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|null $path = null) {
'action' => function (string $id, string $fieldName, string $path = null) {
if ($page = $this->page($id)) {
return $this->fieldApi($page, $fieldName, $path);
}
}
],
[
'pattern' => 'pages/(:any)/sections/(:any)',
'method' => 'GET',
'action' => function (string $id, string $sectionName) {
return $this->page($id)->blueprint()->section($sectionName)?->toResponse();
}
],
[
'pattern' => 'pages/(:any)/sections/(:any)/(:all?)',
'method' => 'ALL',
'action' => function (string $id, string $sectionName, string|null $path = null) {
if ($page = $this->page($id)) {
return $this->sectionApi($page, $sectionName, $path);
}
}
],
// @codeCoverageIgnoreEnd
];

View File

@@ -10,11 +10,14 @@ return [
'action' => function () {
$kirby = $this->kirby();
return match ($kirby->request()->get('canBe')) {
'changed' => $kirby->roles()->canBeChanged(),
'created' => $kirby->roles()->canBeCreated(),
default => $kirby->roles()
};
switch ($kirby->request()->get('canBe')) {
case 'changed':
return $kirby->roles()->canBeChanged();
case 'created':
return $kirby->roles()->canBeCreated();
default:
return $kirby->roles();
}
}
],
[

View File

@@ -5,7 +5,7 @@
* Site Routes
*/
return [
// @codeCoverageIgnoreStart
[
'pattern' => 'site',
'action' => function () {
@@ -75,35 +75,30 @@ return [
$pages = $this
->site()
->index(true)
->filter('isListable', true);
->filter('isReadable', true);
if ($this->requestMethod() === 'GET') {
return $pages->search($this->requestQuery('q'));
} else {
return $pages->query($this->requestBody());
}
return $pages->query($this->requestBody());
}
],
[
'pattern' => 'site/fields/(:any)/(:all?)',
'method' => 'ALL',
'action' => function (string $fieldName, string|null $path = null) {
return $this->fieldApi($this->site(), $fieldName, $path);
}
],
[
'pattern' => 'site/sections/(:any)',
'method' => 'GET',
'action' => function (string $sectionName) {
return $this->site()->blueprint()->section($sectionName)?->toResponse();
if ($section = $this->site()->blueprint()->section($sectionName)) {
return $section->toResponse();
}
}
],
[
'pattern' => 'site/sections/(:any)/(:all?)',
'pattern' => 'site/fields/(:any)/(:all?)',
'method' => 'ALL',
'action' => function (string $sectionName, string|null $path = null) {
return $this->sectionApi($this->site(), $sectionName, $path);
'action' => function (string $fieldName, string $path = null) {
return $this->fieldApi($this->site(), $fieldName, $path);
}
],
// @codeCoverageIgnoreEnd
]
];

View File

@@ -17,18 +17,19 @@ return [
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'
];
}
$info = match ($system->isOk()) {
true => $this->resolve($system)->view('login')->toArray(),
false => $this->resolve($system)->view('troubleshooting')->toArray()
};
return [
'status' => 'ok',
'data' => $info,
'type' => 'model'
];
}
],
[

View File

@@ -8,7 +8,6 @@ use Kirby\Toolkit\Str;
* User Routes
*/
return [
// @codeCoverageIgnoreStart
[
'pattern' => 'users',
'method' => 'GET',
@@ -29,9 +28,9 @@ return [
'action' => function () {
if ($this->requestMethod() === 'GET') {
return $this->users()->search($this->requestQuery('q'));
} else {
return $this->users()->query($this->requestBody());
}
return $this->users()->query($this->requestBody());
}
],
[
@@ -101,18 +100,17 @@ return [
}
// delete the old avatar
$this->user($id)->avatar()?->delete();
if ($avatar = $this->user($id)->avatar()) {
$avatar->delete();
}
$props = [
return $this->user($id)->createFile([
'filename' => 'profile.' . F::extension($filename),
'template' => 'avatar',
'source' => $source
];
// move the source file from the temp dir
return $this->user($id)->createFile($props, true);
]);
},
single: true
$single = true
);
}
],
@@ -206,16 +204,6 @@ return [
return $this->user($id)->roles();
}
],
[
'pattern' => [
'(account)/fields/(:any)/(:all?)',
'users/(:any)/fields/(:any)/(:all?)',
],
'method' => 'ALL',
'action' => function (string $id, string $fieldName, string|null $path = null) {
return $this->fieldApi($this->user($id), $fieldName, $path);
}
],
[
'pattern' => [
'(account)/sections/(:any)',
@@ -230,13 +218,12 @@ return [
],
[
'pattern' => [
'(account)/sections/(:any)/(:all?)',
'users/(:any)/sections/(:any)/(:all?)',
'(account)/fields/(:any)/(:all?)',
'users/(:any)/fields/(:any)/(:all?)',
],
'method' => 'ALL',
'action' => function (string $id, string $sectionName, string|null $path = null) {
return $this->sectionApi($this->user($id), $sectionName, $path);
'action' => function (string $id, string $fieldName, string $path = null) {
return $this->fieldApi($this->user($id), $fieldName, $path);
}
],
// @codeCoverageIgnoreEnd
];

View File

@@ -8,7 +8,6 @@ return function () {
'label' => I18n::translate('view.account'),
'search' => 'users',
'dialogs' => require __DIR__ . '/account/dialogs.php',
'drawers' => require __DIR__ . '/account/drawers.php',
'dropdowns' => require __DIR__ . '/account/dropdowns.php',
'views' => require __DIR__ . '/account/views.php'
];

View File

@@ -1,7 +1,5 @@
<?php
use Kirby\Panel\UserTotpEnableDialog;
$dialogs = require __DIR__ . '/../users/dialogs.php';
return [
@@ -48,13 +46,6 @@ return [
'submit' => $dialogs['user.delete']['submit'],
],
// account fields dialogs
'account.fields' => [
'pattern' => '(account)/files/(:any)/fields/(:any)/(:all?)',
'load' => $dialogs['user.fields']['load'],
'submit' => $dialogs['user.fields']['submit']
],
// change file name
'account.file.changeName' => [
'pattern' => '(account)/files/(:any)/changeName',
@@ -69,13 +60,6 @@ return [
'submit' => $dialogs['user.file.changeSort']['submit'],
],
// change file template
'account.file.changeTemplate' => [
'pattern' => '(account)/files/(:any)/changeTemplate',
'load' => $dialogs['user.file.changeTemplate']['load'],
'submit' => $dialogs['user.file.changeTemplate']['submit'],
],
// delete
'account.file.delete' => [
'pattern' => '(account)/files/(:any)/delete',
@@ -83,24 +67,4 @@ return [
'submit' => $dialogs['user.file.delete']['submit'],
],
// account file fields dialogs
'account.file.fields' => [
'pattern' => '(account)/files/(:any)/fields/(:any)/(:all?)',
'load' => $dialogs['user.file.fields']['load'],
'submit' => $dialogs['user.file.fields']['submit']
],
// account enable TOTP
'account.totp.enable' => [
'pattern' => '(account)/totp/enable',
'load' => fn () => (new UserTotpEnableDialog())->load(),
'submit' => fn () => (new UserTotpEnableDialog())->submit()
],
// account disable TOTP
'account.totp.disable' => [
'pattern' => '(account)/totp/disable',
'load' => $dialogs['user.totp.disable']['load'],
'submit' => $dialogs['user.totp.disable']['submit']
],
];

View File

@@ -1,19 +0,0 @@
<?php
$drawers = require __DIR__ . '/../users/drawers.php';
return [
// account fields drawers
'account.fields' => [
'pattern' => '(account)/files/(:any)/fields/(:any)/(:all?)',
'load' => $drawers['user.fields']['load'],
'submit' => $drawers['user.fields']['submit']
],
// account file fields drawers
'account.file.fields' => [
'pattern' => '(account)/files/(:any)/fields/(:any)/(:all?)',
'load' => $drawers['user.file.fields']['load'],
'submit' => $drawers['user.file.fields']['submit']
],
];

View File

@@ -2,7 +2,6 @@
use Kirby\Cms\App;
use Kirby\Cms\Find;
use Kirby\Toolkit\I18n;
return [
'account' => [
@@ -20,13 +19,6 @@ return [
],
'account.password' => [
'pattern' => 'reset-password',
'action' => fn () => [
'component' => 'k-reset-password-view',
'breadcrumb' => [
[
'label' => I18n::translate('view.resetPassword')
]
]
]
'action' => fn () => ['component' => 'k-reset-password-view']
]
];

View File

@@ -1,61 +0,0 @@
<?php
use Kirby\Cms\Find;
use Kirby\Panel\Field;
return [
'model' => [
'load' => function (
string $modelPath,
string $fieldName,
string|null $path = null
) {
return Field::dialog(
model: Find::parent($modelPath),
fieldName: $fieldName,
path: $path,
method: 'GET'
);
},
'submit' => function (
string $modelPath,
string $fieldName,
string|null $path = null
) {
return Field::dialog(
model: Find::parent($modelPath),
fieldName: $fieldName,
path: $path,
method: 'POST'
);
}
],
'file' => [
'load' => function (
string $modelPath,
string $filename,
string $fieldName,
string|null $path = null
) {
return Field::dialog(
model: Find::file($modelPath, $filename),
fieldName: $fieldName,
path: $path,
method: 'GET'
);
},
'submit' => function (
string $modelPath,
string $filename,
string $fieldName,
string|null $path = null
) {
return Field::dialog(
model: Find::file($modelPath, $filename),
fieldName: $fieldName,
path: $path,
method: 'POST'
);
}
],
];

View File

@@ -1,61 +0,0 @@
<?php
use Kirby\Cms\Find;
use Kirby\Panel\Field;
return [
'model' => [
'load' => function (
string $modelPath,
string $fieldName,
string|null $path = null
) {
return Field::drawer(
model: Find::parent($modelPath),
fieldName: $fieldName,
path: $path,
method: 'GET'
);
},
'submit' => function (
string $modelPath,
string $fieldName,
string|null $path = null
) {
return Field::drawer(
model: Find::parent($modelPath),
fieldName: $fieldName,
path: $path,
method: 'POST'
);
}
],
'file' => [
'load' => function (
string $modelPath,
string $filename,
string $fieldName,
string|null $path = null
) {
return Field::drawer(
model: Find::file($modelPath, $filename),
fieldName: $fieldName,
path: $path,
method: 'GET'
);
},
'submit' => function (
string $modelPath,
string $filename,
string $fieldName,
string|null $path = null
) {
return Field::drawer(
model: Find::file($modelPath, $filename),
fieldName: $fieldName,
path: $path,
method: 'POST'
);
}
],
];

View File

@@ -26,7 +26,7 @@ return [
'type' => 'slug',
'required' => true,
'icon' => 'title',
'allow' => 'a-z0-9@._-',
'allow' => '@._-',
'after' => '.' . $file->extension(),
'preselect' => true
]
@@ -40,8 +40,7 @@ return [
},
'submit' => function (string $path, string $filename) {
$file = Find::file($path, $filename);
$name = $file->kirby()->request()->get('name');
$renamed = $file->changeName($name);
$renamed = $file->changeName($file->kirby()->request()->get('name'));
$oldUrl = $file->panel()->url(true);
$newUrl = $renamed->panel()->url(true);
$response = [
@@ -97,44 +96,6 @@ return [
}
],
'changeTemplate' => [
'load' => function (string $path, string $filename) {
$file = Find::file($path, $filename);
$blueprints = $file->blueprints();
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'warning' => [
'type' => 'info',
'theme' => 'notice',
'text' => I18n::translate('file.changeTemplate.notice')
],
'template' => Field::template($blueprints, [
'required' => true
])
],
'theme' => 'notice',
'submitButton' => I18n::translate('change'),
'value' => [
'template' => $file->template()
]
]
];
},
'submit' => function (string $path, string $filename) {
$file = Find::file($path, $filename);
$template = $file->kirby()->request()->get('template');
$file->changeTemplate($template);
return [
'event' => 'file.changeTemplate',
];
}
],
'delete' => [
'load' => function (string $path, string $filename) {
$file = Find::file($path, $filename);
@@ -168,5 +129,4 @@ return [
];
}
],
];

View File

@@ -1,11 +0,0 @@
<?php
return function () {
return [
'icon' => 'lab',
'label' => 'Lab',
'menu' => false,
'drawers' => require __DIR__ . '/lab/drawers.php',
'views' => require __DIR__ . '/lab/views.php'
];
};

View File

@@ -1,30 +0,0 @@
<?php
use Kirby\Panel\Lab\Docs;
return [
'lab.docs' => [
'pattern' => 'lab/docs/(:any)',
'load' => function (string $component) {
if (Docs::installed() === false) {
return [
'component' => 'k-text-drawer',
'props' => [
'text' => 'The UI docs are not installed.'
]
];
}
$docs = new Docs($component);
return [
'component' => 'k-lab-docs-drawer',
'props' => [
'icon' => 'book',
'title' => $component,
'docs' => $docs->toArray()
]
];
},
],
];

View File

@@ -1,148 +0,0 @@
<?php
use Kirby\Panel\Lab\Category;
use Kirby\Panel\Lab\Docs;
return [
'lab' => [
'pattern' => 'lab',
'action' => function () {
return [
'component' => 'k-lab-index-view',
'props' => [
'categories' => Category::all(),
'info' => Category::installed() ? null : 'The default Lab examples are not installed.',
'tab' => 'examples',
],
];
}
],
'lab.docs' => [
'pattern' => 'lab/docs',
'action' => function () {
$props = match (Docs::installed()) {
true => [
'categories' => [['examples' => Docs::all()]],
'tab' => 'docs',
],
false => [
'info' => 'The UI docs are not installed.',
'tab' => 'docs',
]
};
return [
'component' => 'k-lab-index-view',
'title' => 'Docs',
'breadcrumb' => [
[
'label' => 'Docs',
'link' => 'lab/docs'
]
],
'props' => $props,
];
}
],
'lab.doc' => [
'pattern' => 'lab/docs/(:any)',
'action' => function (string $component) {
$crumbs = [
[
'label' => 'Docs',
'link' => 'lab/docs'
],
[
'label' => $component,
'link' => 'lab/docs/' . $component
]
];
if (Docs::installed() === false) {
return [
'component' => 'k-lab-index-view',
'title' => $component,
'breadcrumb' => $crumbs,
'props' => [
'info' => 'The UI docs are not installed.',
'tab' => 'docs',
],
];
}
$docs = new Docs($component);
return [
'component' => 'k-lab-docs-view',
'title' => $component,
'breadcrumb' => $crumbs,
'props' => [
'component' => $component,
'docs' => $docs->toArray(),
'lab' => $docs->lab()
]
];
}
],
'lab.vue' => [
'pattern' => [
'lab/(:any)/(:any)/index.vue',
'lab/(:any)/(:any)/(:any)/index.vue'
],
'action' => function (
string $category,
string $id,
string|null $tab = null
) {
return Category::factory($category)->example($id, $tab)->serve();
}
],
'lab.example' => [
'pattern' => 'lab/(:any)/(:any)/(:any?)',
'action' => function (
string $category,
string $id,
string|null $tab = null
) {
$category = Category::factory($category);
$example = $category->example($id, $tab);
$props = $example->props();
$vue = $example->vue();
if (Docs::installed() === true && $docs = $props['docs'] ?? null) {
$docs = new Docs($docs);
}
$github = $docs?->github();
if ($source = $props['source'] ?? null) {
$github ??= 'https://github.com/getkirby/kirby/tree/main/' . $source;
}
return [
'component' => 'k-lab-playground-view',
'breadcrumb' => [
[
'label' => $category->name(),
],
[
'label' => $example->title(),
'link' => $example->url()
]
],
'props' => [
'docs' => $docs?->name(),
'examples' => $vue['examples'],
'file' => $example->module(),
'github' => $github,
'props' => $props,
'styles' => $vue['style'],
'tab' => $example->tab(),
'tabs' => array_values($example->tabs()),
'template' => $vue['template'],
'title' => $example->title(),
],
];
}
]
];

View File

@@ -4,7 +4,7 @@ use Kirby\Toolkit\I18n;
return function ($kirby) {
return [
'icon' => 'translate',
'icon' => 'globe',
'label' => I18n::translate('view.languages'),
'menu' => true,
'dialogs' => require __DIR__ . '/languages/dialogs.php',

View File

@@ -2,15 +2,13 @@
use Kirby\Cms\App;
use Kirby\Cms\Find;
use Kirby\Cms\LanguageVariable;
use Kirby\Exception\NotFoundException;
use Kirby\Panel\Field;
use Kirby\Toolkit\A;
use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
$languageDialogFields = [
'name' => [
'counter' => false,
'label' => I18n::translate('language.name'),
'type' => 'text',
'required' => true,
@@ -21,7 +19,7 @@ $languageDialogFields = [
'type' => 'text',
'required' => true,
'counter' => false,
'icon' => 'translate',
'icon' => 'globe',
'width' => '1/2'
],
'direction' => [
@@ -36,27 +34,11 @@ $languageDialogFields = [
'width' => '1/2'
],
'locale' => [
'counter' => false,
'label' => I18n::translate('language.locale'),
'type' => 'text',
'label' => I18n::translate('language.locale'),
'type' => 'text',
],
];
$translationDialogFields = [
'key' => [
'counter' => false,
'icon' => null,
'label' => I18n::translate('language.variable.key'),
'type' => 'text'
],
'value' => [
'buttons' => false,
'counter' => false,
'label' => I18n::translate('language.variable.value'),
'type' => 'textarea'
]
];
return [
// create language
@@ -110,10 +92,8 @@ return [
},
'submit' => function (string $id) {
Find::language($id)->delete();
return [
'event' => 'language.delete',
'redirect' => 'languages'
'event' => 'language.delete',
];
}
],
@@ -172,95 +152,4 @@ return [
];
}
],
'language.translation.create' => [
'pattern' => 'languages/(:any)/translations/create',
'load' => function (string $languageCode) use ($translationDialogFields) {
// find the language to make sure it exists
Find::language($languageCode);
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => $translationDialogFields,
'size' => 'large',
],
];
},
'submit' => function (string $languageCode) {
$request = App::instance()->request();
$language = Find::language($languageCode);
$key = $request->get('key', '');
$value = $request->get('value', '');
LanguageVariable::create($key, $value);
if ($language->isDefault() === false) {
$language->variable($key)->update($value);
}
return true;
}
],
'language.translation.delete' => [
'pattern' => 'languages/(:any)/translations/(:any)/delete',
'load' => function (string $languageCode, string $translationKey) {
$variable = Find::language($languageCode)->variable($translationKey, true);
if ($variable->exists() === false) {
throw new NotFoundException([
'key' => 'language.variable.notFound'
]);
}
return [
'component' => 'k-remove-dialog',
'props' => [
'text' => I18n::template('language.variable.delete.confirm', [
'key' => Escape::html($variable->key())
])
],
];
},
'submit' => function (string $languageCode, string $translationKey) {
return Find::language($languageCode)->variable($translationKey, true)->delete();
}
],
'language.translation.update' => [
'pattern' => 'languages/(:any)/translations/(:any)/update',
'load' => function (string $languageCode, string $translationKey) use ($translationDialogFields) {
$variable = Find::language($languageCode)->variable($translationKey, true);
if ($variable->exists() === false) {
throw new NotFoundException([
'key' => 'language.variable.notFound'
]);
}
$fields = $translationDialogFields;
$fields['key']['disabled'] = true;
$fields['value']['autofocus'] = true;
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => $fields,
'size' => 'large',
'value' => [
'key' => $variable->key(),
'value' => $variable->value()
]
],
];
},
'submit' => function (string $languageCode, string $translationKey) {
Find::language($languageCode)->variable($translationKey, true)->update(
App::instance()->request()->get('value')
);
return true;
}
]
];

View File

@@ -1,110 +1,9 @@
<?php
use Kirby\Cms\App;
use Kirby\Cms\Find;
use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
return [
'language' => [
'pattern' => 'languages/(:any)',
'when' => function (): bool {
return App::instance()->option('languages.variables', true) !== false;
},
'action' => function (string $code) {
$kirby = App::instance();
$language = Find::language($code);
$link = '/languages/' . $language->code();
$strings = [];
$foundation = $kirby->defaultLanguage()->translations();
$translations = $language->translations();
// TODO: update following line and adapt for update and delete options
// when new `languageVariables.*` permissions available
$canUpdate = $kirby->user()?->role()->permissions()->for('languages', 'update') === true;
ksort($foundation);
foreach ($foundation as $key => $value) {
$strings[] = [
'key' => $key,
'value' => $translations[$key] ?? null,
'options' => [
[
'click' => 'update',
'disabled' => $canUpdate === false,
'icon' => 'edit',
'text' => I18n::translate('edit'),
],
[
'click' => 'delete',
'disabled' => $canUpdate === false || $language->isDefault() === false,
'icon' => 'trash',
'text' => I18n::translate('delete'),
]
]
];
}
$next = function () use ($language) {
if ($next = $language->next()) {
return [
'link' => '/languages/' . $next->code(),
'title' => $next->name(),
];
}
};
$prev = function () use ($language) {
if ($prev = $language->prev()) {
return [
'link' => '/languages/' . $prev->code(),
'title' => $prev->name(),
];
}
};
return [
'component' => 'k-language-view',
'breadcrumb' => [
[
'label' => $name = $language->name(),
'link' => $link,
]
],
'props' => [
'deletable' => $language->isDeletable(),
'code' => Escape::html($language->code()),
'default' => $language->isDefault(),
'direction' => $language->direction(),
'id' => $language->code(),
'info' => [
[
'label' => 'Status',
'value' => I18n::translate('language.' . ($language->isDefault() ? 'default' : 'secondary')),
],
[
'label' => I18n::translate('language.code'),
'value' => $language->code(),
],
[
'label' => I18n::translate('language.locale'),
'value' => $language->locale(LC_ALL)
],
[
'label' => I18n::translate('language.direction'),
'value' => I18n::translate('language.direction.' . $language->direction()),
],
],
'name' => $name,
'next' => $next,
'prev' => $prev,
'translations' => $strings,
'url' => $language->url(),
]
];
}
],
'languages' => [
'pattern' => 'languages',
'action' => function () {
@@ -114,15 +13,13 @@ return [
'component' => 'k-languages-view',
'props' => [
'languages' => $kirby->languages()->values(fn ($language) => [
'deletable' => $language->isDeletable(),
'default' => $language->isDefault(),
'id' => $language->code(),
'info' => Escape::html($language->code()),
'text' => Escape::html($language->name()),
]),
'variables' => $kirby->option('languages.variables', true)
'default' => $language->isDefault(),
'id' => $language->code(),
'info' => Escape::html($language->code()),
'text' => Escape::html($language->name()),
])
]
];
}
]
],
];

View File

@@ -1,11 +0,0 @@
<?php
use Kirby\Toolkit\I18n;
return function () {
return [
'icon' => 'search',
'label' => I18n::translate('search'),
'views' => require __DIR__ . '/search/views.php'
];
};

View File

@@ -1,17 +0,0 @@
<?php
use Kirby\Cms\App;
return [
'search' => [
'pattern' => 'search',
'action' => function () {
return [
'component' => 'k-search-view',
'props' => [
'type' => App::instance()->request()->get('type'),
]
];
}
],
];

View File

@@ -11,9 +11,7 @@ return function ($kirby) {
'label' => $kirby->site()->blueprint()->title() ?? I18n::translate('view.site'),
'menu' => true,
'dialogs' => require __DIR__ . '/site/dialogs.php',
'drawers' => require __DIR__ . '/site/drawers.php',
'dropdowns' => require __DIR__ . '/site/dropdowns.php',
'requests' => require __DIR__ . '/site/requests.php',
'searches' => require __DIR__ . '/site/searches.php',
'views' => require __DIR__ . '/site/views.php',
];

View File

@@ -2,20 +2,14 @@
use Kirby\Cms\App;
use Kirby\Cms\Find;
use Kirby\Cms\PageRules;
use Kirby\Exception\Exception;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\PermissionException;
use Kirby\Panel\ChangesDialog;
use Kirby\Panel\Field;
use Kirby\Panel\PageCreateDialog;
use Kirby\Panel\Panel;
use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
use Kirby\Toolkit\Str;
use Kirby\Uuid\Uuids;
$fields = require __DIR__ . '/../fields/dialogs.php';
$files = require __DIR__ . '/../files/dialogs.php';
return [
@@ -161,16 +155,10 @@ return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'notice' => [
'type' => 'info',
'theme' => 'notice',
'text' => I18n::translate('page.changeTemplate.notice')
],
'template' => Field::template($blueprints, [
'required' => true
])
],
'theme' => 'notice',
'submitButton' => I18n::translate('change'),
'value' => [
'template' => $page->intendedTemplate()->name()
@@ -179,10 +167,9 @@ return [
];
},
'submit' => function (string $id) {
$page = Find::page($id);
$template = App::instance()->request()->get('template');
$request = App::instance()->request();
$page->changeTemplate($template);
Find::page($id)->changeTemplate($request->get('template'));
return [
'event' => 'page.changeTemplate',
@@ -212,7 +199,7 @@ return [
'slug' => Field::slug([
'required' => true,
'preselect' => $select === 'slug',
'path' => $page->parent() ? '/' . $page->parent()->uri() . '/' : '/',
'path' => $page->parent() ? '/' . $page->parent()->id() . '/' : '/',
'disabled' => $permissions->can('changeSlug') === false,
'wizard' => [
'text' => I18n::translate('page.changeSlug.fromTitle'),
@@ -237,8 +224,17 @@ return [
$slug = trim($request->get('slug', ''));
// basic input validation before we move on
PageRules::validateTitleLength($title);
PageRules::validateSlugLength($slug);
if (Str::length($title) === 0) {
throw new InvalidArgumentException([
'key' => 'page.changeTitle.empty'
]);
}
if (Str::length($slug) === 0) {
throw new InvalidArgumentException([
'key' => 'page.slug.invalid'
]);
}
// nothing changed
if ($page->title()->value() === $title && $page->slug() === $slug) {
@@ -281,30 +277,93 @@ return [
'page.create' => [
'pattern' => 'pages/create',
'load' => function () {
$request = App::instance()->request();
$dialog = new PageCreateDialog(
parentId: $request->get('parent'),
sectionId: $request->get('section'),
slug: $request->get('slug'),
template: $request->get('template'),
title: $request->get('title'),
viewId: $request->get('view'),
);
$kirby = App::instance();
$request = $kirby->request();
return $dialog->load();
// the parent model for the new page
$parent = $request->get('parent', 'site');
// the view on which the add button is located
// this is important to find the right section
// and provide the correct templates for the new page
$view = $request->get('view', $parent);
// templates will be fetched depending on the
// section settings in the blueprint
$section = $request->get('section');
// this is the parent model
$model = Find::parent($parent);
// this is the view model
// i.e. site if the add button is on
// the dashboard
$view = Find::parent($view);
// available blueprints/templates for the new page
// are always loaded depending on the matching section
// in the view model blueprint
$blueprints = $view->blueprints($section);
// the pre-selected template
$template = $blueprints[0]['name'] ?? $blueprints[0]['value'] ?? null;
$fields = [
'parent' => Field::hidden(),
'title' => Field::title([
'required' => true,
'preselect' => true
]),
'slug' => Field::slug([
'required' => true,
'sync' => 'title',
'path' => empty($model->id()) === false ? '/' . $model->id() . '/' : '/'
]),
'template' => Field::hidden()
];
// only show template field if > 1 templates available
// or when in debug mode
if (count($blueprints) > 1 || $kirby->option('debug') === true) {
$fields['template'] = Field::template($blueprints, [
'required' => true
]);
}
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => $fields,
'submitButton' => I18n::translate('page.draft.create'),
'value' => [
'parent' => $parent,
'slug' => '',
'template' => $template,
'title' => '',
]
]
];
},
'submit' => function () {
$request = App::instance()->request();
$dialog = new PageCreateDialog(
parentId: $request->get('parent'),
sectionId: $request->get('section'),
slug: $request->get('slug'),
template: $request->get('template'),
title: $request->get('title'),
viewId: $request->get('view'),
);
$title = trim($request->get('title', ''));
return $dialog->submit($request->get());
if (Str::length($title) === 0) {
throw new InvalidArgumentException([
'key' => 'page.changeTitle.empty'
]);
}
$page = Find::parent($request->get('parent', 'site'))->createChild([
'content' => ['title' => $title],
'slug' => $request->get('slug'),
'template' => $request->get('template'),
]);
return [
'event' => 'page.create',
'redirect' => $page->panel()->url(true)
];
}
],
@@ -420,26 +479,6 @@ return [
];
}
$slugAppendix = Str::slug(I18n::translate('page.duplicate.appendix'));
$titleAppendix = I18n::translate('page.duplicate.appendix');
// if the item to be duplicated already exists
// add a suffix at the end of slug and title
$duplicateSlug = $page->slug() . '-' . $slugAppendix;
$siblingKeys = $page->parentModel()->childrenAndDrafts()->pluck('uid');
if (in_array($duplicateSlug, $siblingKeys) === true) {
$suffixCounter = 2;
$newSlug = $duplicateSlug . $suffixCounter;
while (in_array($newSlug, $siblingKeys) === true) {
$newSlug = $duplicateSlug . ++$suffixCounter;
}
$slugAppendix .= $suffixCounter;
$titleAppendix .= ' ' . $suffixCounter;
}
return [
'component' => 'k-form-dialog',
'props' => [
@@ -448,8 +487,8 @@ return [
'value' => [
'children' => false,
'files' => false,
'slug' => $page->slug() . '-' . $slugAppendix,
'title' => $page->title() . ' ' . $titleAppendix
'slug' => $page->slug() . '-' . Str::slug(I18n::translate('page.duplicate.appendix')),
'title' => $page->title() . ' ' . I18n::translate('page.duplicate.appendix')
]
]
];
@@ -470,13 +509,6 @@ return [
}
],
// page field dialogs
'page.fields' => [
'pattern' => '(pages/.*?)/fields/(:any)/(:all?)',
'load' => $fields['model']['load'],
'submit' => $fields['model']['submit']
],
// change filename
'page.file.changeName' => [
'pattern' => '(pages/.*?)/files/(:any)/changeName',
@@ -491,13 +523,6 @@ return [
'submit' => $files['changeSort']['submit'],
],
// change template
'page.file.changeTemplate' => [
'pattern' => '(pages/.*?)/files/(:any)/changeTemplate',
'load' => $files['changeTemplate']['load'],
'submit' => $files['changeTemplate']['submit'],
],
// delete
'page.file.delete' => [
'pattern' => '(pages/.*?)/files/(:any)/delete',
@@ -505,56 +530,6 @@ return [
'submit' => $files['delete']['submit'],
],
// page file field dialogs
'page.file.fields' => [
'pattern' => '(pages/.*?)/files/(:any)/fields/(:any)/(:all?)',
'load' => $fields['file']['load'],
'submit' => $fields['file']['submit'],
],
// move page
'page.move' => [
'pattern' => 'pages/(:any)/move',
'load' => function (string $id) {
$page = Find::page($id);
$parent = $page->parentModel();
if (Uuids::enabled() === false) {
$parentId = $parent?->id() ?? '/';
} else {
$parentId = $parent?->uuid()->toString() ?? 'site://';
}
return [
'component' => 'k-page-move-dialog',
'props' => [
'value' => [
'move' => $page->panel()->url(true),
'parent' => $parentId
]
]
];
},
'submit' => function (string $id) {
$kirby = App::instance();
$parentId = $kirby->request()->get('parent');
$parent = (empty($parentId) === true || $parentId === '/' || $parentId === 'site://') ? $kirby->site() : Find::page($parentId);
$oldPage = Find::page($id);
$newPage = $oldPage->move($parent);
return [
'event' => 'page.move',
'redirect' => $newPage->panel()->url(true),
'dispatch' => [
'content/move' => [
$oldPage->panel()->url(true),
$newPage->panel()->url(true)
]
],
];
}
],
// change site title
'site.changeTitle' => [
'pattern' => 'site/changeTitle',
@@ -577,21 +552,14 @@ return [
},
'submit' => function () {
$kirby = App::instance();
$kirby->site()->changeTitle($kirby->request()->get('title'));
$kirby->site()->changeTitle($kirby->request()->get('title'));
return [
'event' => 'site.changeTitle',
];
}
],
// site field dialogs
'site.fields' => [
'pattern' => '(site)/fields/(:any)/(:all?)',
'load' => $fields['model']['load'],
'submit' => $fields['model']['submit'],
],
// change filename
'site.file.changeName' => [
'pattern' => '(site)/files/(:any)/changeName',
@@ -606,13 +574,6 @@ return [
'submit' => $files['changeSort']['submit'],
],
// change template
'site.file.changeTemplate' => [
'pattern' => '(site)/files/(:any)/changeTemplate',
'load' => $files['changeTemplate']['load'],
'submit' => $files['changeTemplate']['submit'],
],
// delete
'site.file.delete' => [
'pattern' => '(site)/files/(:any)/delete',
@@ -620,24 +581,4 @@ return [
'submit' => $files['delete']['submit'],
],
// site file field dialogs
'site.file.fields' => [
'pattern' => '(site)/files/(:any)/fields/(:any)/(:all?)',
'load' => $fields['file']['load'],
'submit' => $fields['file']['submit'],
],
// content changes
'changes' => [
'pattern' => 'changes',
'load' => function () {
$dialog = new ChangesDialog();
return $dialog->load();
},
'submit' => function () {
$dialog = new ChangesDialog();
$ids = App::instance()->request()->get('ids');
return $dialog->submit($ids);
}
],
];

View File

@@ -1,33 +0,0 @@
<?php
$fields = require __DIR__ . '/../fields/drawers.php';
return [
// page field drawers
'page.fields' => [
'pattern' => '(pages/.*?)/fields/(:any)/(:all?)',
'load' => $fields['model']['load'],
'submit' => $fields['model']['submit']
],
// page file field drawers
'page.file.fields' => [
'pattern' => '(pages/.*?)/files/(:any)/fields/(:any)/(:all?)',
'load' => $fields['file']['load'],
'submit' => $fields['file']['submit'],
],
// site field drawers
'site.fields' => [
'pattern' => '(site)/fields/(:any)/(:all?)',
'load' => $fields['model']['load'],
'submit' => $fields['model']['submit'],
],
// site file field drawers
'site.file.fields' => [
'pattern' => '(site)/files/(:any)/fields/(:any)/(:all?)',
'load' => $fields['file']['load'],
'submit' => $fields['file']['submit'],
],
];

View File

@@ -1,9 +1,14 @@
<?php
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) {

View File

@@ -1,66 +0,0 @@
<?php
use Kirby\Cms\App;
use Kirby\Cms\Find;
use Kirby\Toolkit\I18n;
return [
'tree' => [
'pattern' => 'site/tree',
'action' => function () {
$kirby = App::instance();
$request = $kirby->request();
$move = $request->get('move');
$move = $move ? Find::parent($move) : null;
$parent = $request->get('parent');
if ($parent === null) {
$site = $kirby->site();
$panel = $site->panel();
$uuid = $site->uuid()?->toString();
$url = $site->url();
$value = $uuid ?? '/';
return [
[
'children' => $panel->url(true),
'disabled' => $move?->isMovableTo($site) === false,
'hasChildren' => true,
'icon' => 'home',
'id' => '/',
'label' => I18n::translate('view.site'),
'open' => false,
'url' => $url,
'uuid' => $uuid,
'value' => $value
]
];
}
$parent = Find::parent($parent);
$pages = [];
foreach ($parent->childrenAndDrafts()->filterBy('isListable', true) as $child) {
$panel = $child->panel();
$uuid = $child->uuid()?->toString();
$url = $child->url();
$value = $uuid ?? $child->id();
$pages[] = [
'children' => $panel->url(true),
'disabled' => $move?->isMovableTo($child) === false,
'hasChildren' => $child->hasChildren() === true || $child->hasDrafts() === true,
'icon' => $panel->image()['icon'] ?? null,
'id' => $child->id(),
'open' => false,
'label' => $child->title()->value(),
'url' => $url,
'uuid' => $uuid,
'value' => $value
];
}
return $pages;
}
]
];

View File

@@ -8,49 +8,50 @@ return [
'pages' => [
'label' => I18n::translate('pages'),
'icon' => 'page',
'query' => function (string $query = null, int $limit, int $page) {
$kirby = App::instance();
$pages = $kirby->site()
'query' => function (string $query = null) {
$pages = App::instance()->site()
->index(true)
->search($query)
->filter('isListable', true)
->paginate($limit, $page);
->filter('isReadable', true)
->limit(10);
return [
'results' => $pages->values(fn ($page) => [
$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()),
'uuid' => $page->uuid()?->toString(),
]),
'pagination' => $pages->pagination()->toArray()
];
'info' => Escape::html($page->id())
];
}
return $results;
}
],
'files' => [
'label' => I18n::translate('files'),
'icon' => 'image',
'query' => function (string $query = null, int $limit, int $page) {
$kirby = App::instance();
$files = $kirby->site()
'query' => function (string $query = null) {
$files = App::instance()->site()
->index(true)
->filter('isListable', true)
->filter('isReadable', true)
->files()
->filter('isListable', true)
->search($query)
->paginate($limit, $page);
->limit(10);
return [
'results' => $files->values(fn ($file) => [
$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()),
'uuid' => $file->uuid()->toString(),
]),
'pagination' => $files->pagination()->toArray()
];
'info' => Escape::html($file->id())
];
}
return $results;
}
]
];

View File

@@ -1,7 +1,6 @@
<?php
use Kirby\Cms\App;
use Kirby\Exception\LogicException;
use Kirby\Panel\Field;
use Kirby\Toolkit\I18n;
@@ -9,85 +8,59 @@ return [
// license key
'license' => [
'load' => function () {
$kirby = App::instance();
$license = $kirby->system()->license();
$obfuscated = $kirby->user()->isAdmin() === false;
$status = $license->status();
$renewable = $status->renewable();
$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-license-dialog',
'component' => 'k-form-dialog',
'props' => [
'license' => [
'code' => $license->code($obfuscated),
'icon' => $status->icon(),
'info' => $status->info($license->renewal('Y-m-d', 'date')),
'theme' => $status->theme(),
'type' => $license->label(),
'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>'
]
],
'cancelButton' => $renewable,
'submitButton' => $renewable ? [
'icon' => 'refresh',
'text' => I18n::translate('renew'),
'theme' => 'love',
] : false,
'submitButton' => false,
'cancelButton' => false,
]
];
},
'submit' => function () {
// @codeCoverageIgnoreStart
$response = App::instance()->system()->license()->upgrade();
// the upgrade is still needed
if ($response['status'] === 'upgrade') {
return [
'redirect' => $response['url']
];
}
// the upgrade has already been completed
if ($response['status'] === 'complete') {
return [
'event' => 'system.renew',
'message' => I18n::translate('license.success')
];
}
throw new LogicException('The upgrade failed');
// @codeCoverageIgnoreEnd
}
],
// license registration
'registration' => [
'load' => function () {
$system = App::instance()->system();
$local = $system->isLocal();
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'domain' => [
'label' => I18n::translate('license.activate.label'),
'type' => 'info',
'theme' => $local ? 'warning' : 'info',
'text' => I18n::template('license.activate.' . ($local ? 'local' : 'domain'), ['host' => $system->indexUrl()])
],
'license' => [
'label' => I18n::translate('license.code.label'),
'label' => I18n::translate('license.register.label'),
'type' => 'text',
'required' => true,
'counter' => false,
'placeholder' => 'K-',
'help' => I18n::translate('license.code.help') . ' ' . '<a href="https://getkirby.com/buy" target="_blank">' . I18n::translate('license.buy') . ' &rarr;</a>'
'placeholder' => 'K3-',
'help' => I18n::translate('license.register.help')
],
'email' => Field::email(['required' => true])
],
'submitButton' => [
'icon' => 'key',
'text' => I18n::translate('activate'),
'theme' => 'love',
'email' => Field::email([
'required' => true
])
],
'submitButton' => I18n::translate('license.register'),
'value' => [
'license' => null,
'email' => null
@@ -105,7 +78,7 @@ return [
return [
'event' => 'system.register',
'message' => I18n::translate('license.success')
'message' => I18n::translate('license.register.success')
];
// @codeCoverageIgnoreEnd
}

View File

@@ -1,94 +1,48 @@
<?php
use Kirby\Cms\App;
use Kirby\Toolkit\I18n;
return [
'system' => [
'pattern' => 'system',
'action' => function () {
$kirby = App::instance();
$system = $kirby->system();
$updateStatus = $system->updateStatus();
$license = $system->license();
$kirby = App::instance();
$system = $kirby->system();
$license = $system->license();
$environment = [
[
'label' => $license->status()->label(),
'value' => $license->label(),
'theme' => $license->status()->theme(),
'icon' => $license->status()->icon(),
'dialog' => $license->status()->dialog()
],
[
'label' => $updateStatus?->label() ?? I18n::translate('version'),
'value' => $kirby->version(),
'link' => $updateStatus?->url() ??
'https://github.com/getkirby/kirby/releases/tag/' . $kirby->version(),
'theme' => $updateStatus?->theme(),
'icon' => $updateStatus?->icon() ?? 'info'
],
[
'label' => 'PHP',
'value' => phpversion(),
'icon' => 'code'
],
[
'label' => I18n::translate('server'),
'value' => $system->serverSoftware() ?? '?',
'icon' => 'server'
]
];
$exceptions = $updateStatus?->exceptionMessages() ?? [];
$plugins = $system->plugins()->values(function ($plugin) use (&$exceptions) {
$authors = $plugin->authorsNames();
$updateStatus = $plugin->updateStatus();
$version = $updateStatus?->toArray() ?? $plugin->version() ?? '';
if ($updateStatus !== null) {
$exceptions = array_merge($exceptions, $updateStatus->exceptionMessages());
}
// @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' => empty($authors) ? '' : $authors,
'license' => $plugin->license() ?? '',
'author' => $plugin->authorsNames(),
'license' => $plugin->license(),
'name' => [
'text' => $plugin->name() ?? '',
'text' => $plugin->name(),
'href' => $plugin->link(),
],
'version' => $version,
'version' => $plugin->version(),
];
});
$security = $updateStatus?->messages() ?? [];
if ($kirby->option('debug', false) === true) {
$security[] = [
'id' => 'debug',
'text' => I18n::translate('system.issues.debug'),
'link' => 'https://getkirby.com/security/debug'
];
}
if ($kirby->environment()->https() !== true) {
$security[] = [
'id' => 'https',
'text' => I18n::translate('system.issues.https'),
'link' => 'https://getkirby.com/security/https'
];
}
return [
'component' => 'k-system-view',
'props' => [
'environment' => $environment,
'exceptions' => $kirby->option('debug') === true ? $exceptions : [],
'info' => $system->info(),
'plugins' => $plugins,
'security' => $security,
'urls' => [
'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'),

View File

@@ -9,7 +9,6 @@ return function ($kirby) {
'search' => 'users',
'menu' => true,
'dialogs' => require __DIR__ . '/users/dialogs.php',
'drawers' => require __DIR__ . '/users/drawers.php',
'dropdowns' => require __DIR__ . '/users/dropdowns.php',
'searches' => require __DIR__ . '/users/searches.php',
'views' => require __DIR__ . '/users/views.php'

View File

@@ -6,11 +6,9 @@ use Kirby\Cms\UserRules;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Panel\Field;
use Kirby\Panel\Panel;
use Kirby\Panel\UserTotpDisableDialog;
use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
$fields = require __DIR__ . '/../fields/dialogs.php';
$files = require __DIR__ . '/../files/dialogs.php';
return [
@@ -20,12 +18,6 @@ return [
'pattern' => 'users/create',
'load' => function () {
$kirby = App::instance();
// get default value for role
if ($role = $kirby->request()->get('role')) {
$role = $kirby->roles()->find($role)?->id();
}
return [
'component' => 'k-form-dialog',
'props' => [
@@ -49,7 +41,7 @@ return [
'email' => '',
'password' => '',
'translation' => $kirby->panelLanguage(),
'role' => $role ?? $kirby->user()->role()->name()
'role' => $kirby->user()->role()->name()
]
]
];
@@ -295,13 +287,6 @@ return [
}
],
// user field dialogs
'user.fields' => [
'pattern' => '(users/.*?)/fields/(:any)/(:all?)',
'load' => $fields['model']['load'],
'submit' => $fields['model']['submit']
],
// change file name
'user.file.changeName' => [
'pattern' => '(users/.*?)/files/(:any)/changeName',
@@ -316,31 +301,11 @@ return [
'submit' => $files['changeSort']['submit'],
],
// change file template
'user.file.changeTemplate' => [
'pattern' => '(users/.*?)/files/(:any)/changeTemplate',
'load' => $files['changeTemplate']['load'],
'submit' => $files['changeTemplate']['submit'],
],
// delete file
'user.file.delete' => [
'pattern' => '(users/.*?)/files/(:any)/delete',
'load' => $files['delete']['load'],
'submit' => $files['delete']['submit'],
],
]
// user file fields dialogs
'user.file.fields' => [
'pattern' => '(users/.*?)/files/(:any)/fields/(:any)/(:all?)',
'load' => $fields['file']['load'],
'submit' => $fields['file']['submit']
],
// user disable TOTP
'user.totp.disable' => [
'pattern' => 'users/(:any)/totp/disable',
'load' => fn (string $id) => (new UserTotpDisableDialog($id))->load(),
'submit' => fn (string $id) => (new UserTotpDisableDialog($id))->submit()
],
];

View File

@@ -1,18 +0,0 @@
<?php
$fields = require __DIR__ . '/../fields/drawers.php';
return [
// user field drawers
'user.fields' => [
'pattern' => '(users/.*?)/fields/(:any)/(:all?)',
'load' => $fields['model']['load'],
'submit' => $fields['model']['submit']
],
// user file fields drawers
'user.file.fields' => [
'pattern' => '(users/.*?)/files/(:any)/fields/(:any)/(:all?)',
'load' => $fields['file']['load'],
'submit' => $fields['file']['submit']
],
];

View File

@@ -8,22 +8,20 @@ return [
'users' => [
'label' => I18n::translate('users'),
'icon' => 'users',
'query' => function (string $query = null, int $limit, int $page) {
$kirby = App::instance();
$users = $kirby->users()
->search($query)
->paginate($limit, $page);
'query' => function (string $query = null) {
$users = App::instance()->users()->search($query)->limit(10);
$results = [];
return [
'results' => $users->values(fn ($user) => [
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()),
'uuid' => $user->uuid()->toString(),
]),
'pagination' => $users->pagination()->toArray()
];
'info' => Escape::html($user->role()->title())
];
}
return $results;
}
]
];

View File

@@ -31,10 +31,6 @@ return [
$users = $users->role($role);
}
// sort users alphabetically
$users = $users->sortBy('username', 'asc');
// paginate
$users = $users->paginate([
'limit' => 20,
'page' => $kirby->request()->get('page')

View File

@@ -1,10 +1,5 @@
<?php
/** @var \Kirby\Cms\Block $block */
$caption = $block->caption();
$crop = $block->crop()->isTrue();
$ratio = $block->ratio()->or('auto');
?>
<figure<?= Html::attr(['data-ratio' => $ratio, 'data-crop' => $crop], null, ' ') ?>>
<?php /** @var \Kirby\Cms\Block $block */ ?>
<figure>
<ul>
<?php foreach ($block->images()->toFiles() as $image): ?>
<li>
@@ -12,9 +7,4 @@ $ratio = $block->ratio()->or('auto');
</li>
<?php endforeach ?>
</ul>
<?php if ($caption->isNotEmpty()): ?>
<figcaption>
<?= $caption ?>
</figcaption>
<?php endif ?>
</figure>

View File

@@ -8,33 +8,9 @@ fields:
query: model.images
multiple: true
layout: cards
size: small
size: tiny
empty: field.blocks.gallery.images.empty
uploads:
template: blocks/image
image:
ratio: 1/1
caption:
label: field.blocks.image.caption
type: writer
icon: text
inline: true
ratio:
label: field.blocks.image.ratio
type: select
placeholder: Auto
width: 1/2
options:
1/1: "1:1"
16/9: "16:9"
10/8: "10:8"
21/9: "21:9"
7/5: "7:5"
4/3: "4:3"
5/3: "5:3"
3/2: "3:2"
3/1: "3:1"
crop:
label: field.blocks.image.crop
type: toggle
width: 1/2

View File

@@ -5,31 +5,20 @@ preview: heading
fields:
level:
label: field.blocks.heading.level
type: toggles
type: select
empty: false
default: "h2"
labels: false
width: 1/6
options:
- value: h1
icon: h1
text: H1
- value: h2
icon: h2
text: H2
- value: h3
icon: h3
text: H3
- value: h4
icon: h4
text: H4
- value: h5
icon: h5
text: H5
- value: h6
icon: h6
text: H6
- h1
- h2
- h3
- h4
- h5
- h6
text:
label: field.blocks.heading.text
type: writer
inline: true
width: 5/6
placeholder: field.blocks.heading.placeholder

View File

@@ -11,7 +11,7 @@ $src = null;
if ($block->location() == 'web') {
$src = $block->src()->esc();
} elseif ($image = $block->image()->toFile()) {
$alt = $alt->or($image->alt());
$alt = $alt ?? $image->alt();
$src = $image->url();
}

View File

@@ -8,8 +8,8 @@ fields:
columns: 2
default: "kirby"
options:
kirby: "{{ t('field.blocks.image.location.internal') }}"
web: "{{ t('field.blocks.image.location.external') }}"
kirby: Kirby
web: Web
image:
label: field.blocks.image.name
type: files

View File

@@ -2,30 +2,12 @@
use Kirby\Cms\Html;
/** @var \Kirby\Cms\Block $block */
$caption = $block->caption();
if (
$block->location() == 'kirby' &&
$video = $block->video()->toFile()
) {
$url = $video->url();
$attrs = array_filter([
'autoplay' => $block->autoplay()->toBool(),
'controls' => $block->controls()->toBool(),
'loop' => $block->loop()->toBool(),
'muted' => $block->muted()->toBool(),
'poster' => $block->poster()->toFile()?->url(),
'preload' => $block->preload()->value(),
]);
} else {
$url = $block->url();
}
?>
<?php if ($video = Html::video($url, [], $attrs ?? [])): ?>
<?php if ($video = Html::video($block->url())): ?>
<figure>
<?= $video ?>
<?php if ($caption->isNotEmpty()): ?>
<figcaption><?= $caption ?></figcaption>
<?php if ($block->caption()->isNotEmpty()): ?>
<figcaption><?= $block->caption() ?></figcaption>
<?php endif ?>
</figure>
<?php endif ?>

View File

@@ -2,77 +2,11 @@ name: field.blocks.video.name
icon: video
preview: video
fields:
location:
label: field.blocks.video.location
type: radio
columns: 2
default: "web"
options:
kirby: "{{ t('field.blocks.image.location.internal') }}"
web: "{{ t('field.blocks.image.location.external') }}"
url:
label: field.blocks.video.url.label
type: url
placeholder: field.blocks.video.url.placeholder
when:
location: web
video:
label: field.blocks.video.name
type: files
query: model.videos
multiple: false
# you might want to add a template for videos here
when:
location: kirby
poster:
label: field.blocks.video.poster
type: files
query: model.images
multiple: false
image:
back: black
uploads:
template: blocks/image
when:
location: kirby
caption:
label: field.blocks.video.caption
type: writer
inline: true
autoplay:
label: field.blocks.video.autoplay
type: toggle
width: 1/3
when:
location: kirby
muted:
label: field.blocks.video.muted
type: toggle
width: 1/3
default: true
when:
location: kirby
loop:
label: field.blocks.video.loop
type: toggle
width: 1/3
when:
location: kirby
controls:
label: field.blocks.video.controls
type: toggle
width: 1/3
default: true
when:
location: kirby
preload:
label: field.blocks.video.preload
type: select
width: 2/3
default: auto
options:
- auto
- metadata
- none
when:
location: kirby

View File

@@ -0,0 +1,56 @@
name: Code
icon: code
fields:
code:
label: Code
type: textarea
buttons: false
font: monospace
language:
label: Language
type: select
default: text
options:
bash: Bash
basic: BASIC
c: C
clojure: Clojure
cpp: C++
csharp: C#
css: CSS
diff: Diff
elixir: Elixir
elm: Elm
erlang: Erlang
go: Go
graphql: GraphQL
haskell: Haskell
html: HTML
java: Java
js: JavaScript
json: JSON
latext: LaTeX
less: Less
lisp: Lisp
lua: Lua
makefile: Makefile
markdown: Markdown
markup: Markup
objectivec: Objective-C
pascal: Pascal
perl: Perl
php: PHP
text: Plain Text
python: Python
r: R
ruby: Ruby
rust: Rust
sass: Sass
scss: SCSS
shell: Shell
sql: SQL
swift: Swift
typescript: TypeScript
vbnet: VB.net
xml: XML
yaml: YAML

View File

@@ -0,0 +1,20 @@
icon: title
fields:
text:
type: text
level:
type: select
width: 1/2
empty: false
default: "2"
options:
- value: "1"
text: Heading 1
- value: "2"
text: Heading 2
- value: "3"
text: Heading 3
id:
type: text
label: ID
width: 1/2

View File

@@ -0,0 +1,16 @@
name: Image
icon: image
fields:
image:
type: files
multiple: false
alt:
type: text
icon: title
caption:
type: writer
inline: true
icon: text
link:
type: text
icon: url

View File

@@ -0,0 +1,12 @@
name: Quote
icon: quote
fields:
text:
label: Quote Text
type: writer
inline: true
citation:
label: Citation
type: writer
inline: true
placeholder: by …

View File

@@ -0,0 +1,25 @@
name: Table
icon: menu
fields:
rows:
label: Menu
type: structure
columns:
dish: true
description: true
price:
before:
width: 1/4
align: right
fields:
dish:
label: Dish
type: text
description:
label: Description
type: text
price:
label: Price
type: number
before:
step: 0.01

View File

@@ -0,0 +1,5 @@
name: Text
icon: text
fields:
text:
type: writer

View File

@@ -0,0 +1,8 @@
name: Video
icon: video
label: "{{ url }}"
fields:
url:
type: url
caption:
type: writer

View File

@@ -0,0 +1,2 @@
name: File
title: file

View File

@@ -0,0 +1,3 @@
name: Page
title: Page

View File

@@ -0,0 +1,7 @@
name: Site
title: Site
sections:
pages:
headline: Pages
type: pages

View File

@@ -4,69 +4,90 @@ use Kirby\Cms\App;
use Kirby\Cms\Collection;
use Kirby\Cms\File;
use Kirby\Cms\FileVersion;
use Kirby\Cms\Page;
use Kirby\Cms\User;
use Kirby\Cms\Helpers;
use Kirby\Cms\Template;
use Kirby\Data\Data;
use Kirby\Email\PHPMailer as Emailer;
use Kirby\Exception\NotFoundException;
use Kirby\Filesystem\F;
use Kirby\Filesystem\Filename;
use Kirby\Http\Uri;
use Kirby\Http\Url;
use Kirby\Image\Darkroom;
use Kirby\Template\Snippet;
use Kirby\Template\Template;
use Kirby\Text\Markdown;
use Kirby\Text\SmartyPants;
use Kirby\Toolkit\A;
use Kirby\Toolkit\Str;
use Kirby\Uuid\Uuid;
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,
/**
* 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;
}
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
) {
'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 {
'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 = []
) {
'file::version' => function (App $kirby, $file, array $options = []) {
// if file is not resizable, return
if ($file->isResizable() === false) {
return $file;
@@ -87,7 +108,7 @@ return [
Data::write($job, array_merge($options, [
'filename' => $file->filename()
]));
} catch (Throwable) {
} catch (Throwable $e) {
// if thumb doesn't exist yet and job file cannot
// be created, return
return $file;
@@ -105,6 +126,7 @@ return [
/**
* 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
*/
@@ -113,17 +135,27 @@ return [
/**
* 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 = []
): string {
'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;
// if the config options have changed or the component is called for the first time,
// (re-)initialize the parser object
if ($config !== $options) {
@@ -137,14 +169,17 @@ return [
/**
* 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|null $query = null,
string|array $params = []
): Collection {
'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, '|')];
}
@@ -156,57 +191,37 @@ return [
'words' => false,
];
$collection = clone $collection;
$options = array_merge($defaults, $params);
$query = trim($query ?? '');
// empty or too short search query
if (Str::length($query) < $options['minlength']) {
return $collection->limit(0);
}
$words = preg_replace('/(\s)/u', ',', $query);
$words = Str::split($words, ',', $options['minlength']);
$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) {
$words = array_diff($words, $options['stopwords']);
$searchWords = array_diff($searchWords, $options['stopwords']);
}
// returns an empty collection if there is no search word
if (empty($words) === true) {
return $collection->limit(0);
}
$searchWords = array_map(function ($value) use ($options) {
return $options['words'] ? '\b' . preg_quote($value) . '\b' : preg_quote($value);
}, $searchWords);
$words = A::map(
$words,
fn ($value) => Str::wrap(preg_quote($value), $options['words'] ? '\b' : '')
);
$exact = preg_quote($query);
if ($options['words']) {
$exact = '(\b' . $exact . '\b)';
}
$query = Str::lower($query);
$preg = '!(' . implode('|', $words) . ')!iu';
$scores = [];
$results = $collection->filter(function ($item) use ($query, $exact, $preg, $options, &$scores) {
$data = $item->content()->toArray();
$keys = array_keys($data);
$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 ($item instanceof User) {
if (is_a($item, 'Kirby\Cms\User') === true) {
$keys[] = 'name';
$keys[] = 'email';
$keys[] = 'role';
} elseif ($item instanceof Page) {
} elseif (is_a($item, 'Kirby\Cms\Page') === true) {
// apply the default score for pages
$options['score'] = array_merge(
['id' => 64, 'title' => 64],
$options['score']
);
$options['score'] = array_merge([
'id' => 64,
'title' => 64,
], $options['score']);
}
if (empty($options['fields']) === false) {
@@ -214,10 +229,8 @@ return [
$keys = array_intersect($keys, $fields);
}
$scoring = [
'hits' => 0,
'score' => 0
];
$item->searchHits = 0;
$item->searchScore = 0;
foreach ($keys as $key) {
$score = $options['score'][$key] ?? 1;
@@ -226,53 +239,43 @@ return [
$lowerValue = Str::lower($value);
// check for exact matches
if ($query == $lowerValue) {
$scoring['score'] += 16 * $score;
$scoring['hits'] += 1;
if ($lowerQuery == $lowerValue) {
$item->searchScore += 16 * $score;
$item->searchHits += 1;
// check for exact beginning matches
} elseif (
$options['words'] === false &&
Str::startsWith($lowerValue, $query) === true
) {
$scoring['score'] += 8 * $score;
$scoring['hits'] += 1;
} 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('!' . $exact . '!ui', $value, $r)) {
$scoring['score'] += 2 * $score;
$scoring['hits'] += $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)) {
$scoring['score'] += $matches * $score;
$scoring['hits'] += $matches;
$item->searchHits += $matches;
$item->searchScore += $matches * $score;
}
}
$scores[$item->id()] = $scoring;
return $scoring['hits'] > 0;
return $item->searchHits > 0;
});
return $results->sort(
fn ($item) => $scores[$item->id()]['score'],
'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 {
'smartypants' => function (App $kirby, string $text = null, array $options = []): string {
static $smartypants;
static $config;
@@ -289,55 +292,59 @@ return [
/**
* 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,
string|array|null $name,
array $data = [],
bool $slots = false
): Snippet|string {
return Snippet::factory($name, $data, $slots);
'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';
if (file_exists($file) === false) {
$file = $kirby->extensions('snippets')[$name] ?? null;
}
if ($file) {
break;
}
}
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\Template\Template
* @return \Kirby\Cms\Template
*/
'template' => function (
App $kirby,
string $name,
string $type = 'html',
string $defaultType = 'html'
) {
'template' => function (App $kirby, string $name, string $type = 'html', string $defaultType = 'html') {
return new Template($name, $type, $defaultType);
},
/**
* 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 {
'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();
$options = $darkroom->preprocess($src, $options);
$root = (new Filename($src, $dst, $options))->toString();
F::copy($src, $root, true);
$darkroom->process($root, $options);
@@ -348,15 +355,12 @@ return [
/**
* 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
* @throws \Kirby\Exception\NotFoundException If an invalid UUID was passed
* @return string
*/
'url' => function (
App $kirby,
string $path = null,
$options = null
): string {
'url' => function (App $kirby, string $path = null, $options = null): string {
$language = null;
// get language from simple string option
@@ -398,23 +402,6 @@ return [
return $path;
}
// support UUIDs
if (
$path !== null &&
(
Uuid::is($path, 'page') === true ||
Uuid::is($path, 'file') === true
)
) {
$model = Uuid::for($path)->model();
if ($model === null) {
throw new NotFoundException('The model could not be found for "' . $path . '" uuid');
}
$path = $model->url();
}
$url = Url::makeAbsolute($path, $kirby->url());
if ($options === null) {

View File

@@ -1,145 +0,0 @@
<?php
use Kirby\Exception\InvalidArgumentException;
use Kirby\Field\FieldOptions;
use Kirby\Toolkit\A;
use Kirby\Toolkit\Escape;
use Kirby\Toolkit\Str;
return [
'props' => [
/**
* Unset inherited props
*/
'after' => null,
'before' => null,
/**
* Whether to allow alpha transparency in the color
*/
'alpha' => function (bool $alpha = false) {
return $alpha;
},
/**
* The CSS format (hex, rgb, hsl) to display and store the value
*/
'format' => function (string $format = 'hex'): string {
if (in_array($format, ['hex', 'hsl', 'rgb']) === false) {
throw new InvalidArgumentException('Unsupported format for color field (supported: hex, rgb, hsl)');
}
return $format;
},
/**
* Change mode to disable the color picker (`input`) or to only
* show the `options` as toggles
*/
'mode' => function (string $mode = 'picker'): string {
if (in_array($mode, ['picker', 'input', 'options']) === false) {
throw new InvalidArgumentException('Unsupported mode for color field (supported: picker, input, options)');
}
return $mode;
},
/**
* List of colors that will be shown as buttons
* to directly select them
*/
'options' => function (array $options = []): array {
return $options;
}
],
'computed' => [
'default' => function (): string {
return Str::lower($this->default);
},
'options' => function (): array {
// resolve options to support manual arrays
// alongside api and query options
$props = FieldOptions::polyfill($this->props);
$options = FieldOptions::factory([
'text' => '{{ item.value }}',
'value' => '{{ item.key }}',
...$props['options']
]);
$options = $options->render($this->model());
if (empty($options) === true) {
return [];
}
$options = match (true) {
// simple array of values
// or value=text (from Options class)
is_numeric($options[0]['value']) ||
$options[0]['value'] === $options[0]['text']
=> A::map($options, fn ($option) => [
'value' => $option['text']
]),
// deprecated: name => value, flipping
// TODO: start throwing in warning in v5
$this->isColor($options[0]['text'])
=> A::map($options, fn ($option) => [
'value' => $option['text'],
// ensure that any HTML in the new text is escaped
'text' => Escape::html($option['value'])
]),
default
=> A::map($options, fn ($option) => [
'value' => $option['value'],
'text' => $option['text']
]),
};
return $options;
}
],
'methods' => [
'isColor' => function (string $value): bool {
return
$this->isHex($value) ||
$this->isRgb($value) ||
$this->isHsl($value);
},
'isHex' => function (string $value): bool {
return preg_match('/^#([\da-f]{3,4}){1,2}$/i', $value) === 1;
},
'isHsl' => function (string $value): bool {
return preg_match('/^hsla?\(\s*(\d{1,3}\.?\d*)(deg|rad|grad|turn)?(?:,|\s)+(\d{1,3})%(?:,|\s)+(\d{1,3})%(?:,|\s|\/)*(\d*(?:\.\d+)?)(%?)\s*\)?$/i', $value) === 1;
},
'isRgb' => function (string $value): bool {
return preg_match('/^rgba?\(\s*(\d{1,3})(%?)(?:,|\s)+(\d{1,3})(%?)(?:,|\s)+(\d{1,3})(%?)(?:,|\s|\/)*(\d*(?:\.\d+)?)(%?)\s*\)?$/i', $value) === 1;
},
],
'validations' => [
'color' => function ($value) {
if (empty($value) === true) {
return true;
}
if ($this->format === 'hex' && $this->isHex($value) === false) {
throw new InvalidArgumentException([
'key' => 'validation.color',
'data' => ['format' => 'hex']
]);
}
if ($this->format === 'rgb' && $this->isRgb($value) === false) {
throw new InvalidArgumentException([
'key' => 'validation.color',
'data' => ['format' => 'rgb']
]);
}
if ($this->format === 'hsl' && $this->isHsl($value) === false) {
throw new InvalidArgumentException([
'key' => 'validation.color',
'data' => ['format' => 'hsl']
]);
}
}
]
];

View File

@@ -46,13 +46,13 @@ return [
/**
* Latest date, which can be selected/saved (Y-m-d)
*/
'max' => function (string $max = null): string|null {
'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|null {
'min' => function (string $min = null): ?string {
return Date::optional($min);
},
@@ -129,7 +129,7 @@ return [
'key' => 'validation.date.between',
'data' => [
'min' => $min->format($format),
'max' => $max->format($format)
'max' => $min->format($format)
]
]);
} elseif ($min && $value->isMin($min) === false) {

View File

@@ -1,6 +1,5 @@
<?php
use Kirby\Cms\ModelWithContent;
use Kirby\Data\Data;
use Kirby\Toolkit\A;
@@ -35,13 +34,7 @@ return [
],
'computed' => [
'parentModel' => function () {
if (
is_string($this->parent) === true &&
$model = $this->model()->query(
$this->parent,
ModelWithContent::class
)
) {
if (is_string($this->parent) === true && $model = $this->model()->query($this->parent, 'Kirby\Cms\Model')) {
return $model;
}
@@ -75,13 +68,10 @@ return [
foreach (Data::decode($value, 'yaml') as $id) {
if (is_array($id) === true) {
$id = $id['uuid'] ?? $id['id'] ?? null;
$id = $id['id'] ?? null;
}
if (
$id !== null &&
($file = $this->kirby()->file($id, $this->model()))
) {
if ($id !== null && ($file = $this->kirby()->file($id, $this->model()))) {
$files[] = $this->fileResponse($file);
}
}
@@ -132,7 +122,7 @@ return [
];
},
'save' => function ($value = null) {
return A::pluck($value, $this->store);
return A::pluck($value, 'uuid');
},
'validations' => [
'max',

View File

@@ -14,6 +14,13 @@ return [
'icon' => null,
'placeholder' => null,
'required' => null,
'translate' => null
'translate' => null,
/**
* If `false`, the prepended number will be hidden
*/
'numbered' => function (bool $numbered = true) {
return $numbered;
}
]
];

View File

@@ -1,5 +1,3 @@
<?php
return [
'hidden' => true
];
return [];

View File

@@ -12,6 +12,7 @@ return [
'before' => null,
'default' => null,
'disabled' => null,
'icon' => null,
'placeholder' => null,
'required' => null,
'translate' => null,

View File

@@ -1,155 +0,0 @@
<?php
use Kirby\Exception\InvalidArgumentException;
use Kirby\Http\Url;
use Kirby\Toolkit\Str;
use Kirby\Toolkit\V;
return [
'props' => [
'after' => null,
'before' => null,
'icon' => null,
'placeholder' => null,
/**
* @values 'anchor', 'url, 'page, 'file', 'email', 'tel', 'custom'
*/
'options' => function (array|null $options = null): array {
return $options ?? [
'url',
'page',
'file',
'email',
'tel',
'anchor'
];
},
'value' => function (string|null $value = null) {
return $value ?? '';
}
],
'methods' => [
'activeTypes' => function () {
return array_filter($this->availableTypes(), function (string $type) {
return in_array($type, $this->props['options']) === true;
}, ARRAY_FILTER_USE_KEY);
},
'availableTypes' => function () {
return [
'anchor' => [
'detect' => function (string $value): bool {
return Str::startsWith($value, '#') === true;
},
'link' => function (string $value): string {
return $value;
},
'validate' => function (string $value): bool {
return Str::startsWith($value, '#') === true;
},
],
'email' => [
'detect' => function (string $value): bool {
return Str::startsWith($value, 'mailto:') === true;
},
'link' => function (string $value): string {
return str_replace('mailto:', '', $value);
},
'validate' => function (string $value): bool {
return V::email($value);
},
],
'file' => [
'detect' => function (string $value): bool {
return Str::startsWith($value, 'file://') === true;
},
'link' => function (string $value): string {
return $value;
},
'validate' => function (string $value): bool {
return V::uuid($value, 'file');
},
],
'page' => [
'detect' => function (string $value): bool {
return Str::startsWith($value, 'page://') === true;
},
'link' => function (string $value): string {
return $value;
},
'validate' => function (string $value): bool {
return V::uuid($value, 'page');
},
],
'tel' => [
'detect' => function (string $value): bool {
return Str::startsWith($value, 'tel:') === true;
},
'link' => function (string $value): string {
return str_replace('tel:', '', $value);
},
'validate' => function (string $value): bool {
return V::tel($value);
},
],
'url' => [
'detect' => function (string $value): bool {
return Str::startsWith($value, 'http://') === true || Str::startsWith($value, 'https://') === true;
},
'link' => function (string $value): string {
return $value;
},
'validate' => function (string $value): bool {
return V::url($value);
},
],
// needs to come last
'custom' => [
'detect' => function (string $value): bool {
return true;
},
'link' => function (string $value): string {
return $value;
},
'validate' => function (): bool {
return true;
},
]
];
},
],
'validations' => [
'value' => function (string|null $value) {
if (empty($value) === true) {
return true;
}
$detected = false;
foreach ($this->activeTypes() as $type => $options) {
if ($options['detect']($value) !== true) {
continue;
}
$link = $options['link']($value);
$detected = true;
if ($options['validate']($link) === false) {
throw new InvalidArgumentException([
'key' => 'validation.' . $type
]);
}
}
// none of the configured types has been detected
if ($detected === false) {
throw new InvalidArgumentException([
'key' => 'validation.linkType'
]);
}
return true;
},
]
];

View File

@@ -7,12 +7,6 @@ return [
*/
'marks' => function ($marks = true) {
return $marks;
},
/**
* Sets the allowed nodes. Available nodes: `bulletList`, `orderedList`
*/
'nodes' => function ($nodes = null) {
return $nodes;
}
],
'computed' => [

View File

@@ -12,7 +12,7 @@ return [
},
/**
* Layout size for cards: `tiny`, `small`, `medium`, `large`, `huge`, `full`
* Layout size for cards: `tiny`, `small`, `medium`, `large` or `huge`
*/
'size' => function (string $size = 'auto') {
return $size;

View File

@@ -1,6 +1,6 @@
<?php
use Kirby\Field\FieldOptions;
use Kirby\Form\Options;
return [
'props' => [
@@ -30,18 +30,19 @@ return [
],
'methods' => [
'getOptions' => function () {
$props = FieldOptions::polyfill($this->props);
$options = FieldOptions::factory($props['options']);
return $options->render($this->model());
return Options::factory(
$this->options(),
$this->props,
$this->model()
);
},
'sanitizeOption' => function ($value) {
$options = array_column($this->options(), 'value');
return in_array($value, $options) === true ? $value : null;
'sanitizeOption' => function ($option) {
$allowed = array_column($this->options(), 'value');
return in_array($option, $allowed, true) === true ? $option : null;
},
'sanitizeOptions' => function ($values) {
$options = array_column($this->options(), 'value');
$options = array_intersect($values, $options);
return array_values($options);
'sanitizeOptions' => function ($options) {
$allowed = array_column($this->options(), 'value');
return array_intersect($options, $allowed);
},
]
];

View File

@@ -1,7 +1,6 @@
<?php
use Kirby\Toolkit\I18n;
use Kirby\Toolkit\Str;
return [
'props' => [
@@ -68,21 +67,12 @@ return [
return $search;
},
/**
* Whether to store UUID or ID in the
* content file of the model
*
* @param string $store 'uuid'|'id'
*/
'store' => function (string $store = 'uuid') {
return Str::lower($store);
},
/**
* Main text for each item
*/
'text' => function (string $text = null) {
return $text;
},
],
];

View File

@@ -3,7 +3,6 @@
use Kirby\Cms\Api;
use Kirby\Cms\File;
use Kirby\Exception\Exception;
use Kirby\Exception\InvalidArgumentException;
return [
'props' => [
@@ -23,23 +22,18 @@ return [
$uploads = [];
}
$uploads['accept'] = '*';
if ($template = $uploads['template'] ?? null) {
// get parent object for upload target
$parent = $this->uploadParent($uploads['parent'] ?? null);
if ($parent === null) {
throw new InvalidArgumentException('"' . $uploads['parent'] . '" could not be resolved as a valid parent for the upload');
}
$template = $uploads['template'] ?? null;
if ($template) {
$file = new File([
'filename' => 'tmp',
'parent' => $parent,
'parent' => $this->model(),
'template' => $template
]);
$uploads['accept'] = $file->blueprint()->acceptAttribute();
$uploads['accept'] = $file->blueprint()->acceptMime();
} else {
$uploads['accept'] = '*';
}
return $uploads;
@@ -51,37 +45,29 @@ return [
throw new Exception('Uploads are disabled for this field');
}
$parent = $this->uploadParent($params['parent'] ?? null);
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();
}
return $api->upload(function ($source, $filename) use ($parent, $params, $map) {
$props = [
$file = $parent->createFile([
'source' => $source,
'template' => $params['template'] ?? null,
'filename' => $filename,
];
]);
// move the source file from the temp dir
$file = $parent->createFile($props, true);
if ($file instanceof File === false) {
if (is_a($file, 'Kirby\Cms\File') === false) {
throw new Exception('The file could not be uploaded');
}
return $map($file, $parent);
});
},
'uploadParent' => function (string $parentQuery = null) {
$parent = $this->model();
if ($parentQuery) {
$parent = $parent->query($parentQuery);
}
if ($parent instanceof File) {
$parent = $parent->parent();
}
return $parent;
}
]
];

View File

@@ -1,35 +1,32 @@
<?php
use Kirby\Toolkit\Str;
use Kirby\Toolkit\V;
return [
'extends' => 'tags',
'props' => [
/**
* If set to `all`, any type of input is accepted. If set to `options` only the predefined options are accepted as input.
* Unset inherited props
*/
'accept' => function ($value = 'options') {
return V::in($value, ['all', 'options']) ? $value : 'all';
},
'accept' => null,
/**
* Custom icon to replace the arrow down.
*/
'icon' => function (string $icon = 'checklist') {
'icon' => function (string $icon = null) {
return $icon;
},
],
'methods' => [
'toValues' => function ($value) {
if (is_null($value) === true) {
return [];
}
if (is_array($value) === false) {
$value = Str::split($value, $this->separator());
}
return $this->sanitizeOptions($value);
}
],
/**
* 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

@@ -8,7 +8,7 @@ return [
* Default number that will be saved when a new page/user/file is created
*/
'default' => function ($default = null) {
return $this->toNumber($default) ?? '';
return $this->toNumber($default);
},
/**
* The lowest allowed number
@@ -26,10 +26,10 @@ return [
* Allowed incremental steps between numbers (i.e `0.5`)
*/
'step' => function ($step = null) {
return $this->toNumber($step) ?? '';
return $this->toNumber($step);
},
'value' => function ($value = null) {
return $this->toNumber($value) ?? '';
return $this->toNumber($value);
}
],
'methods' => [

View File

@@ -1,104 +0,0 @@
<?php
use Kirby\Data\Data;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Form\Form;
use Kirby\Toolkit\I18n;
return [
'props' => [
/**
* Unset inherited props
*/
'after' => null,
'before' => null,
'autofocus' => null,
'icon' => null,
'placeholder' => null,
/**
* Set the default values for the object
*/
'default' => function ($default = null) {
return $default;
},
/**
* The placeholder text if no information has been added yet
*/
'empty' => function ($empty = null) {
return I18n::translate($empty, $empty);
},
/**
* Fields setup for the object form. Works just like fields in regular forms.
*/
'fields' => function (array $fields = []) {
return $fields;
}
],
'computed' => [
'default' => function () {
if (empty($this->default) === true) {
return '';
}
return $this->form($this->default)->values();
},
'fields' => function () {
if (empty($this->fields) === true) {
return [];
}
return $this->form()->fields()->toArray();
},
'value' => function () {
$data = Data::decode($this->value, 'yaml');
if (empty($data) === true) {
return '';
}
return $this->form($data)->values();
}
],
'methods' => [
'form' => function (array $values = []) {
return new Form([
'fields' => $this->attrs['fields'],
'values' => $values,
'model' => $this->model
]);
},
],
'save' => function ($value) {
if (empty($value) === true) {
return '';
}
return $this->form($value)->content();
},
'validations' => [
'object' => function ($value) {
if (empty($value) === true) {
return true;
}
$errors = $this->form($value)->errors();
if (empty($errors) === false) {
// use the first error for details
$name = array_key_first($errors);
$error = $errors[$name];
throw new InvalidArgumentException([
'key' => 'object.validation',
'data' => [
'label' => $error['label'] ?? $name,
'message' => implode("\n", $error['message'])
]
]);
}
}
]
];

View File

@@ -67,7 +67,7 @@ return [
foreach (Data::decode($value, 'yaml') as $id) {
if (is_array($id) === true) {
$id = $id['uuid'] ?? $id['id'] ?? null;
$id = $id['id'] ?? null;
}
if ($id !== null && ($page = $kirby->page($id))) {
@@ -102,7 +102,7 @@ return [
];
},
'save' => function ($value = null) {
return A::pluck($value, $this->store);
return A::pluck($value, 'id');
},
'validations' => [
'max',

View File

@@ -1,7 +1,5 @@
<?php
use Kirby\Toolkit\I18n;
return [
'extends' => 'number',
'props' => [
@@ -20,13 +18,6 @@ return [
* Enables/disables the tooltip and set the before and after values
*/
'tooltip' => function ($tooltip = true) {
if (is_array($tooltip) === true) {
$after = $tooltip['after'] ?? null;
$before = $tooltip['before'] ?? null;
$tooltip['after'] = I18n::translate($after, $after);
$tooltip['before'] = I18n::translate($before, $before);
}
return $tooltip;
},
]

View File

@@ -1,7 +1,5 @@
<?php
use Kirby\Field\FieldOptions;
return [
'extends' => 'radio',
'props' => [
@@ -22,16 +20,5 @@ return [
'placeholder' => function (string $placeholder = '—') {
return $placeholder;
},
],
'methods' => [
'getOptions' => function () {
$props = FieldOptions::polyfill($this->props);
// disable safe mode as the select field does not
// render HTML for the option text
$options = FieldOptions::factory($props['options'], false);
return $options->render($this->model());
}
]
];

View File

@@ -1,11 +1,8 @@
<?php
use Kirby\Data\Data;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Form\Form;
use Kirby\Toolkit\A;
use Kirby\Toolkit\I18n;
use Kirby\Toolkit\Str;
return [
'mixins' => ['min'],
@@ -52,7 +49,7 @@ return [
/**
* Fields setup for the structure form. Works just like fields in regular forms.
*/
'fields' => function (array $fields = []) {
'fields' => function (array $fields) {
return $fields;
},
/**
@@ -102,54 +99,54 @@ return [
},
'fields' => function () {
if (empty($this->fields) === true) {
return [];
throw new Exception('Please provide some fields for the structure');
}
return $this->form()->fields()->toArray();
},
'columns' => function () {
$columns = [];
$blueprint = $this->columns;
$columns = [];
$mobile = 0;
// if no custom columns have been defined,
// gather all fields as columns
if (empty($blueprint) === true) {
// skip hidden fields
$fields = array_filter(
$this->fields,
fn ($field) =>
$field['type'] !== 'hidden' && $field['hidden'] !== true
);
$fields = array_column($fields, 'name');
$blueprint = array_fill_keys($fields, true);
}
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;
}
foreach ($blueprint as $name => $column) {
$field = $this->fields[$name] ?? null;
// Skip empty and unsaveable fields
// They should never be included as column
if (
empty($field) === true ||
$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 = [];
}
if (is_array($column) === false) {
$column = [];
$field = $this->fields[$columnName] ?? null;
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']
]);
}
$column['type'] ??= $field['type'];
$column['label'] ??= $field['label'] ?? $name;
$column['label'] = I18n::translate($column['label'], $column['label']);
$columns[$name] = $column;
}
// make the first column visible on mobile
// if no other mobile columns are defined
if (in_array(true, array_column($columns, 'mobile')) === false) {
if ($mobile === 0) {
$columns[array_key_first($columns)]['mobile'] = true;
}
@@ -173,53 +170,34 @@ return [
},
'form' => function (array $values = []) {
return new Form([
'fields' => $this->attrs['fields'] ?? [],
'fields' => $this->attrs['fields'],
'values' => $values,
'model' => $this->model
]);
},
],
'api' => function () {
return [
[
'pattern' => 'validate',
'method' => 'ALL',
'action' => function () {
return array_values($this->field()->form($this->requestBody())->errors());
}
]
];
},
'save' => function ($value) {
$data = [];
foreach ($value as $row) {
$row = $this->form($row)->content();
// remove frontend helper id
unset($row['_id']);
$data[] = $row;
$data[] = $this->form($row)->content();
}
return $data;
},
'validations' => [
'min',
'max',
'structure' => function ($value) {
if (empty($value) === true) {
return true;
}
$values = A::wrap($value);
foreach ($values as $index => $value) {
$form = $this->form($value);
foreach ($form->fields() as $field) {
$errors = $field->errors();
if (empty($errors) === false) {
throw new InvalidArgumentException([
'key' => 'structure.validation',
'data' => [
'field' => $field->label() ?? Str::ucfirst($field->name()),
'index' => $index + 1
]
]);
}
}
}
}
'max'
]
];

View File

@@ -31,7 +31,7 @@ return [
* Set to `list` to display each tag with 100% width,
* otherwise the tags are displayed inline
*/
'layout' => function (string|null $layout = null) {
'layout' => function (?string $layout = null) {
return $layout;
},
/**
@@ -46,56 +46,52 @@ return [
'max' => function (int $max = null) {
return $max;
},
/**
* 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 (bool|array $search = true) {
return $search;
},
/**
* Custom tags separator, which will be used to store tags in the content file
*/
'separator' => function (string $separator = ',') {
return $separator;
},
/**
* If `true`, selected entries will be sorted
* according to their position in the dropdown
*/
'sort' => function (bool $sort = false) {
return $sort;
},
],
'computed' => [
'default' => function (): array {
return $this->toValues($this->default);
return $this->toTags($this->default);
},
'value' => function (): array {
return $this->toValues($this->value);
return $this->toTags($this->value);
}
],
'methods' => [
'toValues' => function ($value) {
'toTags' => function ($value) {
if (is_null($value) === true) {
return [];
}
if (is_array($value) === false) {
$value = Str::split($value, $this->separator());
}
$options = $this->options();
if ($this->accept === 'options') {
$value = $this->sanitizeOptions($value);
}
// 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;
}
return $value;
$index = array_search($option, array_column($options, 'value'));
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(
$value,
A::pluck($value, 'value'),
$this->separator() . ' '
);
},

View File

@@ -10,7 +10,7 @@ return [
* 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 && array_key_exists($value, $this->converters()) === false) {
if ($value !== null && in_array($value, array_keys($this->converters())) === false) {
throw new InvalidArgumentException([
'key' => 'field.converter.invalid',
'data' => ['converter' => $value]
@@ -27,13 +27,6 @@ return [
return $counter;
},
/**
* Sets the font family (sans or monospace)
*/
'font' => function (string $font = null) {
return $font === 'monospace' ? 'monospace' : 'sans-serif';
},
/**
* Maximum number of allowed characters
*/

View File

@@ -15,7 +15,7 @@ return [
/**
* Sets the default time when a new page/file/user is created
*/
'default' => function ($default = null): string|null {
'default' => function ($default = null): ?string {
return $default;
},
@@ -36,13 +36,13 @@ return [
/**
* Latest time, which can be selected/saved (H:i or H:i:s)
*/
'max' => function (string $max = null): string|null {
'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|null {
'min' => function (string $min = null): ?string {
return Date::optional($min);
},
@@ -62,7 +62,7 @@ return [
'unit' => 'minute',
]);
},
'value' => function ($value = null): string|null {
'value' => function ($value = null): ?string {
return $value;
}
],
@@ -80,7 +80,7 @@ return [
'format' => function () {
return $this->props['format'] ?? 'H:i:s';
},
'value' => function (): string|null {
'value' => function (): ?string {
return $this->toDatetime($this->value, 'H:i:s') ?? '';
}
],

View File

@@ -49,9 +49,9 @@ return [
'value' => function () {
if ($this->props['value'] === null) {
return $this->default();
} else {
return $this->toBool($this->props['value']);
}
return $this->toBool($this->props['value']);
}
],
'methods' => [

View File

@@ -24,8 +24,18 @@ return [
/**
* Default selected user(s) when a new page/file/user is created
*/
'default' => function (string|array|bool|null $default = null) {
return $default;
'default' => function ($default = null) {
if ($default === false) {
return [];
}
if ($default === null && $user = $this->kirby()->user()) {
return [
$this->userResponse($user)
];
}
return $this->toUsers($default);
},
'value' => function ($value = null) {
@@ -33,22 +43,10 @@ return [
},
],
'computed' => [
'default' => function (): array {
if ($this->default === false) {
return [];
}
if (
$this->default === true &&
$user = $this->kirby()->user()
) {
return [
$this->userResponse($user)
];
}
return $this->toUsers($this->default);
}
/**
* Unset inherited computed
*/
'default' => null
],
'methods' => [
'userResponse' => function ($user) {
@@ -59,7 +57,7 @@ return [
'text' => $this->text,
]);
},
'toUsers' => function ($value = null): array {
'toUsers' => function ($value = null) {
$users = [];
$kirby = App::instance();
@@ -98,7 +96,7 @@ return [
];
},
'save' => function ($value = null) {
return A::pluck($value, $this->store);
return A::pluck($value, 'id');
},
'validations' => [
'max',

View File

@@ -1,23 +1,9 @@
<?php
use Kirby\Exception\InvalidArgumentException;
use Kirby\Sane\Sane;
use Kirby\Toolkit\V;
return [
'props' => [
/**
* Enables/disables the character counter in the top right corner
*/
'counter' => function (bool $counter = true) {
return $counter;
},
/**
* Available heading levels
*/
'headings' => function (array|null $headings = null) {
return array_intersect($headings ?? range(1, 6), range(1, 6));
},
/**
* Enables inline mode, which will not wrap new lines in paragraphs and creates hard breaks instead.
*
@@ -27,37 +13,18 @@ return [
return $inline;
},
/**
* Sets the allowed HTML formats. Available formats: `bold`, `italic`, `underline`, `strike`, `code`, `link`, `email`. Activate/deactivate them all by passing `true`/`false`. Default marks are `bold`, `italic`, `underline`, `strike`, `link`, `email`
* 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 = null) {
'marks' => function ($marks = true) {
return $marks;
},
/**
* Maximum number of allowed characters
*/
'maxlength' => function (int $maxlength = null) {
return $maxlength;
},
/**
* Minimum number of required characters
*/
'minlength' => function (int $minlength = null) {
return $minlength;
},
/**
* Sets the allowed nodes. Available nodes: `paragraph`, `heading`, `bulletList`, `orderedList`, `quote`. Activate/deactivate them all by passing `true`/`false`. Default nodes are `paragraph`, `heading`, `bulletList`, `orderedList`.
* Sets the allowed nodes. Available nodes: `paragraph`, `heading`, `bulletList`, `orderedList`. Activate/deactivate them all by passing `true`/`false`. Default nodes are `paragraph`, `heading`, `bulletList`, `orderedList`.
* @param array|bool|null $nodes
*/
'nodes' => function ($nodes = null) {
return $nodes;
},
/**
* Toolbar options, incl. `marks` (to narrow down which marks should have toolbar buttons), `nodes` (to narrow down which nodes should have toolbar dropdown entries) and `inline` to set the position of the toolbar (false = sticking on top of the field)
*/
'toolbar' => function ($toolbar = null) {
return $toolbar;
}
],
'computed' => [
@@ -66,28 +33,4 @@ return [
return Sane::sanitize($value, 'html');
}
],
'validations' => [
'minlength' => function ($value) {
if (
$this->minlength &&
V::minLength(strip_tags($value), $this->minlength) === false
) {
throw new InvalidArgumentException([
'key' => 'validation.minlength',
'data' => ['min' => $this->minlength]
]);
}
},
'maxlength' => function ($value) {
if (
$this->maxlength &&
V::maxLength(strip_tags($value), $this->maxlength) === false
) {
throw new InvalidArgumentException([
'key' => 'validation.maxlength',
'data' => ['max' => $this->maxlength]
]);
}
},
]
];

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