Add blueprints and fake content

This commit is contained in:
Paul Nicoué 2021-11-18 17:44:47 +01:00
parent 1ff19bf38f
commit 8235816462
592 changed files with 22385 additions and 31535 deletions

View file

@ -1,14 +1,12 @@
<?php
// @codeCoverageIgnoreStart
return [
// cms classes
'asset' => 'Kirby\Cms\Asset',
'collection' => 'Kirby\Cms\Collection',
'dir' => 'Kirby\Cms\Dir',
'field' => 'Kirby\Cms\Field',
'file' => 'Kirby\Cms\File',
'files' => 'Kirby\Cms\Files',
'find' => 'Kirby\Cms\Find',
'html' => 'Kirby\Cms\Html',
'kirby' => 'Kirby\Cms\App',
'page' => 'Kirby\Cms\Page',
@ -17,6 +15,7 @@ return [
'r' => 'Kirby\Cms\R',
'response' => 'Kirby\Cms\Response',
's' => 'Kirby\Cms\S',
'sane' => 'Kirby\Sane\Sane',
'site' => 'Kirby\Cms\Site',
'structure' => 'Kirby\Cms\Structure',
'url' => 'Kirby\Cms\Url',
@ -29,6 +28,12 @@ return [
'json' => 'Kirby\Data\Json',
'yaml' => 'Kirby\Data\Yaml',
// file classes
'asset' => 'Kirby\Filesystem\Asset',
'dir' => 'Kirby\Filesystem\Dir',
'f' => 'Kirby\Filesystem\F',
'mime' => 'Kirby\Filesystem\Mime',
// data classes
'database' => 'Kirby\Database\Database',
'db' => 'Kirby\Database\Db',
@ -45,18 +50,31 @@ return [
// image classes
'dimensions' => 'Kirby\Image\Dimensions',
// panel classes
'panel' => 'Kirby\Panel\Panel',
// toolkit classes
'a' => 'Kirby\Toolkit\A',
'c' => 'Kirby\Toolkit\Config',
'config' => 'Kirby\Toolkit\Config',
'escape' => 'Kirby\Toolkit\Escape',
'f' => 'Kirby\Toolkit\F',
'i18n' => 'Kirby\Toolkit\I18n',
'mime' => 'Kirby\Toolkit\Mime',
'obj' => 'Kirby\Toolkit\Obj',
'str' => 'Kirby\Toolkit\Str',
'tpl' => 'Kirby\Toolkit\Tpl',
'v' => 'Kirby\Toolkit\V',
'xml' => 'Kirby\Toolkit\Xml'
'xml' => 'Kirby\Toolkit\Xml',
// TODO: remove in 4.0.0
'kirby\cms\asset' => 'Kirby\Filesystem\Asset',
'kirby\cms\dir' => 'Kirby\Filesystem\Dir',
'kirby\cms\filename' => 'Kirby\Filesystem\Filename',
'kirby\cms\filefoundation' => 'Kirby\Filesystem\IsFile',
'kirby\cms\form' => 'Kirby\Form\Form',
'kirby\cms\kirbytag' => 'Kirby\Text\KirbyTag',
'kirby\cms\kirbytags' => 'Kirby\Text\KirbyTags',
'kirby\toolkit\dir' => 'Kirby\Filesystem\Dir',
'kirby\toolkit\f' => 'Kirby\Filesystem\F',
'kirby\toolkit\file' => 'Kirby\Filesystem\File',
'kirby\toolkit\mime' => 'Kirby\Filesystem\Mime',
];
// @codeCoverageIgnoreEnd

View file

@ -7,7 +7,10 @@ return function () {
$allowImpersonation = $this->kirby()->option('api.allowImpersonation') ?? false;
// csrf token check
if ($auth->type($allowImpersonation) === 'session' && $auth->csrf() === false) {
if (
$auth->type($allowImpersonation) === 'session' &&
$auth->csrf() === false
) {
throw new PermissionException('Unauthenticated');
}

View file

@ -1,7 +1,7 @@
<?php
use Kirby\Cms\File;
use Kirby\Cms\Form;
use Kirby\Form\Form;
/**
* File
@ -18,7 +18,7 @@ return [
return $file->dimensions()->toArray();
},
'dragText' => function (File $file) {
return $file->dragText();
return $file->panel()->dragText();
},
'exists' => function (File $file) {
return $file->exists();
@ -33,7 +33,7 @@ return [
return $file->id();
},
'link' => function (File $file) {
return $file->panelUrl(true);
return $file->panel()->url(true);
},
'mime' => function (File $file) {
return $file->mime();
@ -48,7 +48,7 @@ return [
return $file->next();
},
'nextWithTemplate' => function (File $file) {
$files = $file->templateSiblings()->sort('sort', 'asc', 'filename', 'asc');
$files = $file->templateSiblings()->sorted();
$index = $files->indexOf($file);
return $files->nth($index + 1);
@ -57,22 +57,26 @@ return [
return $file->niceSize();
},
'options' => function (File $file) {
return $file->panelOptions();
return $file->panel()->options();
},
'panelIcon' => function (File $file) {
return $file->panelIcon();
// TODO: remove in 3.7.0
// @codeCoverageIgnoreStart
deprecated('The API field file.panelIcon has been deprecated and will be removed in 3.7.0. Use file.panelImage instead');
return $file->panel()->image();
// @codeCoverageIgnoreEnd
},
'panelImage' => function (File $file) {
return $file->panelImage();
return $file->panel()->image();
},
'panelUrl' => function (File $file) {
return $file->panelUrl(true);
return $file->panel()->url(true);
},
'prev' => function (File $file) {
return $file->prev();
},
'prevWithTemplate' => function (File $file) {
$files = $file->templateSiblings()->sort('sort', 'asc', 'filename', 'asc');
$files = $file->templateSiblings()->sorted();
$index = $files->indexOf($file);
return $files->nth($index - 1);
@ -106,7 +110,7 @@ return [
return $file->type();
},
'url' => function (File $file) {
return $file->url(true);
return $file->url();
},
],
'type' => 'Kirby\Cms\File',

View file

@ -41,7 +41,7 @@ return [
return $file->type();
},
'url' => function (FileVersion $file) {
return $file->url(true);
return $file->url();
},
],
'type' => 'Kirby\Cms\FileVersion',

View file

@ -1,7 +1,7 @@
<?php
use Kirby\Cms\Form;
use Kirby\Cms\Page;
use Kirby\Form\Form;
/**
* Page
@ -27,7 +27,7 @@ return [
return $page->errors();
},
'files' => function (Page $page) {
return $page->files()->sort('sort', 'asc', 'filename', 'asc');
return $page->files()->sorted();
},
'hasChildren' => function (Page $page) {
return $page->hasChildren();
@ -44,6 +44,12 @@ return [
'isSortable' => function (Page $page) {
return $page->isSortable();
},
/**
* @deprecated 3.6.0
* @todo Throw deprecated warning in 3.7.0
* @todo Remove in 3.8.0
* @codeCoverageIgnore
*/
'next' => function (Page $page) {
return $page
->nextAll()
@ -56,13 +62,18 @@ return [
return $page->num();
},
'options' => function (Page $page) {
return $page->panelOptions(['preview']);
return $page->panel()->options(['preview']);
},
/**
* @todo Remove in 3.7.0
* @codeCoverageIgnore
*/
'panelIcon' => function (Page $page) {
return $page->panelIcon();
deprecated('The API field page.panelIcon has been deprecated and will be removed in 3.7.0. Use page.panelImage instead');
return $page->panel()->image();
},
'panelImage' => function (Page $page) {
return $page->panelImage();
return $page->panel()->image();
},
'parent' => function (Page $page) {
return $page->parent();
@ -70,6 +81,12 @@ return [
'parents' => function (Page $page) {
return $page->parents()->flip();
},
/**
* @deprecated 3.6.0
* @todo Throw deprecated warning in 3.7.0
* @todo Remove in 3.8.0
* @codeCoverageIgnore
*/
'prev' => function (Page $page) {
return $page
->prevAll()

View file

@ -1,7 +1,7 @@
<?php
use Kirby\Cms\Form;
use Kirby\Cms\Site;
use Kirby\Form\Form;
/**
* Site
@ -24,7 +24,7 @@ return [
return $site->drafts();
},
'files' => function (Site $site) {
return $site->files()->sort('sort', 'asc', 'filename', 'asc');
return $site->files()->sorted();
},
'options' => function (Site $site) {
return $site->permissions()->toArray();

View file

@ -52,12 +52,8 @@ return [
'requirements' => function (System $system) {
return $system->toArray();
},
'site' => function () {
try {
return $this->site()->blueprint()->title();
} catch (Throwable $e) {
return $this->site()->title()->value();
}
'site' => function (System $system) {
return $system->title();
},
'slugs' => function () {
return Str::$language;
@ -87,7 +83,7 @@ return [
'version' => function () {
$user = $this->user();
if ($user && $user->role()->permissions()->for('access', 'settings') === true) {
if ($user && $user->role()->permissions()->for('access', 'system') === true) {
return $this->kirby()->version();
} else {
return null;

View file

@ -1,7 +1,7 @@
<?php
use Kirby\Cms\Form;
use Kirby\Cms\User;
use Kirby\Form\Form;
/**
* User
@ -24,7 +24,7 @@ return [
return $user->email();
},
'files' => function (User $user) {
return $user->files()->sort('sort', 'asc', 'filename', 'asc');
return $user->files()->sorted();
},
'id' => function (User $user) {
return $user->id();
@ -39,7 +39,10 @@ return [
return $user->next();
},
'options' => function (User $user) {
return $user->panelOptions();
return $user->panel()->options();
},
'panelImage' => function (User $user) {
return $user->panel()->image();
},
'permissions' => function (User $user) {
return $user->role()->permissions()->toArray();

View file

@ -1,12 +1,15 @@
<?php
// routing pattern to match all models with files
$pattern = '(account|pages/[^/]+|site|users/[^/]+)';
/**
* Files Routes
*/
return [
[
'pattern' => '(:all)/files/(:any)/sections/(:any)',
'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)) {
@ -15,7 +18,7 @@ return [
}
],
[
'pattern' => '(:all)/files/(:any)/fields/(:any)/(:all?)',
'pattern' => $pattern . '/files/(:any)/fields/(:any)/(:all?)',
'method' => 'ALL',
'action' => function (string $parent, string $filename, string $fieldName, string $path = null) {
if ($file = $this->file($parent, $filename)) {
@ -24,27 +27,33 @@ return [
}
],
[
'pattern' => '(:all)/files',
'pattern' => $pattern . '/files',
'method' => 'GET',
'action' => function (string $path) {
return $this->parent($path)->files()->sort('sort', 'asc', 'filename', 'asc');
return $this->parent($path)->files()->sorted();
}
],
[
'pattern' => '(:all)/files',
'pattern' => $pattern . '/files',
'method' => 'POST',
'action' => function (string $path) {
// move_uploaded_file() not working with unit test
// @codeCoverageIgnoreStart
return $this->upload(function ($source, $filename) use ($path) {
return $this->parent($path)->createFile([
'content' => [
'sort' => $this->requestBody('sort')
],
'source' => $source,
'template' => $this->requestBody('template'),
'filename' => $filename
]);
});
// @codeCoverageIgnoreEnd
}
],
[
'pattern' => '(:all)/files/search',
'pattern' => $pattern . '/files/search',
'method' => 'GET|POST',
'action' => function (string $path) {
$files = $this->parent($path)->files();
@ -57,7 +66,7 @@ return [
}
],
[
'pattern' => '(:all)/files/sort',
'pattern' => $pattern . '/files/sort',
'method' => 'PATCH',
'action' => function (string $path) {
return $this->parent($path)->files()->changeSort(
@ -67,21 +76,21 @@ return [
}
],
[
'pattern' => '(:all)/files/(:any)',
'pattern' => $pattern . '/files/(:any)',
'method' => 'GET',
'action' => function (string $path, string $filename) {
return $this->file($path, $filename);
}
],
[
'pattern' => '(:all)/files/(:any)',
'pattern' => $pattern . '/files/(:any)',
'method' => 'PATCH',
'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->update($this->requestBody(), $this->language(), true);
}
],
[
'pattern' => '(:all)/files/(:any)',
'pattern' => $pattern . '/files/(:any)',
'method' => 'POST',
'action' => function (string $path, string $filename) {
return $this->upload(function ($source) use ($path, $filename) {
@ -90,14 +99,14 @@ return [
}
],
[
'pattern' => '(:all)/files/(:any)',
'pattern' => $pattern . '/files/(:any)',
'method' => 'DELETE',
'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->delete();
}
],
[
'pattern' => '(:all)/files/(:any)/name',
'pattern' => $pattern . '/files/(:any)/name',
'method' => 'PATCH',
'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->changeName($this->requestBody('name'));

View file

@ -1,6 +1,5 @@
<?php
use Kirby\Exception\Exception;
/**
* Content Lock Routes
@ -9,7 +8,13 @@ return [
[
'pattern' => '(:all)/lock',
'method' => 'GET',
/**
* @deprecated 3.6.0
* @todo Remove in 3.7.0
*/
'action' => function (string $path) {
deprecated('The `GET (:all)/lock` API endpoint has been deprecated and will be removed in 3.7.0');
if ($lock = $this->parent($path)->lock()) {
return [
'supported' => true,
@ -30,11 +35,6 @@ return [
if ($lock = $this->parent($path)->lock()) {
return $lock->create();
}
throw new Exception([
'key' => 'lock.notImplemented',
'httpCode' => 501
]);
}
],
[
@ -44,17 +44,19 @@ return [
if ($lock = $this->parent($path)->lock()) {
return $lock->remove();
}
throw new Exception([
'key' => 'lock.notImplemented',
'httpCode' => 501
]);
}
],
[
'pattern' => '(:all)/unlock',
'method' => 'GET',
/**
* @deprecated 3.6.0
* @todo Remove in 3.7.0
*/
'action' => function (string $path) {
deprecated('The `GET (:all)/unlock` API endpoint has been deprecated and will be removed in 3.7.0');
if ($lock = $this->parent($path)->lock()) {
return [
'supported' => true,
@ -75,11 +77,6 @@ return [
if ($lock = $this->parent($path)->lock()) {
return $lock->unlock();
}
throw new Exception([
'key' => 'lock.notImplemented',
'httpCode' => 501
]);
}
],
[
@ -89,11 +86,6 @@ return [
if ($lock = $this->parent($path)->lock()) {
return $lock->resolve();
}
throw new Exception([
'key' => 'lock.notImplemented',
'httpCode' => 501
]);
}
],
];

View file

@ -38,12 +38,17 @@ return [
'pages/(:any)/blueprints',
/**
* @deprecated
* @todo remove in 3.6.0
* @todo remove in 3.7.0
*/
'pages/(:any)/children/blueprints',
],
'method' => 'GET',
'action' => function (string $id) {
// @codeCoverageIgnoreStart
if ($this->route->pattern() === 'pages/([a-zA-Z0-9\.\-_%= \+\@\(\)]+)/children/blueprints') {
deprecated('`GET pages/(:any)/children/blueprints` API endpoint has been deprecated and will be removed in 3.7.0. Use `GET pages/(:any)/blueprints` instead');
}
// @codeCoverageIgnoreEnd
return $this->page($id)->blueprints($this->requestQuery('section'));
}
],

View file

@ -51,12 +51,17 @@ return [
'site/blueprints',
/**
* @deprecated
* @todo remove in 3.6.0
* @todo remove in 3.7.0
*/
'site/children/blueprints',
],
'method' => 'GET',
'action' => function () {
// @codeCoverageIgnoreStart
if ($this->route->pattern() === 'site/children/blueprints') {
deprecated('`GET site/children/blueprints` API endpoint has been deprecated and will be removed in 3.7.0. Use `GET site/blueprints` instead.');
}
// @codeCoverageIgnoreEnd
return $this->site()->blueprints($this->requestQuery('section'));
}
],

View file

@ -1,12 +1,11 @@
<?php
use Kirby\Toolkit\F;
use Kirby\Filesystem\F;
/**
* User Routes
*/
return [
[
'pattern' => 'users',
'method' => 'GET',
@ -33,35 +32,51 @@ return [
}
],
[
'pattern' => 'users/(:any)',
'pattern' => [
'(account)',
'users/(:any)',
],
'method' => 'GET',
'action' => function (string $id) {
return $this->user($id);
}
],
[
'pattern' => 'users/(:any)',
'pattern' => [
'(account)',
'users/(:any)',
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->update($this->requestBody(), $this->language(), true);
}
],
[
'pattern' => 'users/(:any)',
'pattern' => [
'(account)',
'users/(:any)',
],
'method' => 'DELETE',
'action' => function (string $id) {
return $this->user($id)->delete();
}
],
[
'pattern' => 'users/(:any)/avatar',
'pattern' => [
'(account)/avatar',
'users/(:any)/avatar',
],
'method' => 'GET',
'action' => function (string $id) {
return $this->user($id)->avatar();
}
],
// @codeCoverageIgnoreStart
[
'pattern' => 'users/(:any)/avatar',
'pattern' => [
'(account)/avatar',
'users/(:any)/avatar',
],
'method' => 'POST',
'action' => function (string $id) {
if ($avatar = $this->user($id)->avatar()) {
@ -77,79 +92,101 @@ return [
}, $single = true);
}
],
// @codeCoverageIgnoreEnd
[
'pattern' => 'users/(:any)/avatar',
'pattern' => [
'(account)/avatar',
'users/(:any)/avatar',
],
'method' => 'DELETE',
'action' => function (string $id) {
return $this->user($id)->avatar()->delete();
}
],
[
'pattern' => 'users/(:any)/blueprint',
'pattern' => [
'(account)/blueprint',
'users/(:any)/blueprint',
],
'method' => 'GET',
'action' => function (string $id) {
return $this->user($id)->blueprint();
}
],
[
'pattern' => 'users/(:any)/blueprints',
'pattern' => [
'(account)/blueprints',
'users/(:any)/blueprints',
],
'method' => 'GET',
'action' => function (string $id) {
return $this->user($id)->blueprints($this->requestQuery('section'));
}
],
[
'pattern' => 'users/(:any)/email',
'pattern' => [
'(account)/email',
'users/(:any)/email',
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changeEmail($this->requestBody('email'));
}
],
[
'pattern' => 'users/(:any)/fields/(:any)/(:all?)',
'method' => 'ALL',
'action' => function (string $id, string $fieldName, string $path = null) {
if ($user = $this->user($id)) {
return $this->fieldApi($user, $fieldName, $path);
}
}
],
[
'pattern' => 'users/(:any)/language',
'pattern' => [
'(account)/language',
'users/(:any)/language',
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changeLanguage($this->requestBody('language'));
}
],
[
'pattern' => 'users/(:any)/name',
'pattern' => [
'(account)/name',
'users/(:any)/name',
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changeName($this->requestBody('name'));
}
],
[
'pattern' => 'users/(:any)/password',
'pattern' => [
'(account)/password',
'users/(:any)/password',
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changePassword($this->requestBody('password'));
}
],
[
'pattern' => 'users/(:any)/role',
'pattern' => [
'(account)/role',
'users/(:any)/role',
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changeRole($this->requestBody('role'));
}
],
[
'pattern' => 'users/(:any)/roles',
'pattern' => [
'(account)/roles',
'users/(:any)/roles',
],
'action' => function (string $id) {
return $this->user($id)->roles();
}
],
[
'pattern' => 'users/(:any)/sections/(:any)',
'pattern' => [
'(account)/sections/(:any)',
'users/(:any)/sections/(:any)',
],
'method' => 'GET',
'action' => function (string $id, string $sectionName) {
if ($section = $this->user($id)->blueprint()->section($sectionName)) {
@ -157,4 +194,14 @@ return [
}
}
],
[
'pattern' => [
'(account)/fields/(:any)/(:all?)',
'users/(:any)/fields/(:any)/(:all?)',
],
'method' => 'ALL',
'action' => function (string $id, string $fieldName, string $path = null) {
return $this->fieldApi($this->user($id), $fieldName, $path);
}
],
];

View file

@ -0,0 +1,12 @@
<?php
return function () {
return [
'icon' => 'account',
'label' => t('view.account'),
'search' => 'users',
'dialogs' => require __DIR__ . '/account/dialogs.php',
'dropdowns' => require __DIR__ . '/account/dropdowns.php',
'views' => require __DIR__ . '/account/views.php'
];
};

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,9 @@
<?php
use Kirby\Cms\Find;
return [
'file' => function (string $parent, string $filename) {
return Find::file($parent, $filename)->panel()->dropdown();
}
];

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,547 @@
<?php
use Kirby\Cms\Find;
use Kirby\Exception\Exception;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\PermissionException;
use Kirby\Panel\Field;
use Kirby\Panel\Panel;
use Kirby\Toolkit\Str;
$files = require __DIR__ . '/../files/dialogs.php';
return [
// change page position
'page.changeSort' => [
'pattern' => 'pages/(:any)/changeSort',
'load' => function (string $id) {
$page = Find::page($id);
$position = null;
if ($page->blueprint()->num() !== 'default') {
throw new PermissionException([
'key' => 'page.sort.permission',
'data' => [
'slug' => $page->slug()
]
]);
}
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'position' => Field::pagePosition($page),
],
'submitButton' => t('change'),
'value' => [
'position' => $page->panel()->position()
]
]
];
},
'submit' => function (string $id) {
Find::page($id)->changeStatus('listed', get('position'));
return [
'event' => 'page.sort',
];
}
],
// change page status
'page.changeStatus' => [
'pattern' => 'pages/(:any)/changeStatus',
'load' => function (string $id) {
$page = Find::page($id);
$blueprint = $page->blueprint();
$status = $page->status();
$states = [];
$position = null;
foreach ($blueprint->status() as $key => $state) {
$states[] = [
'value' => $key,
'text' => $state['label'],
'info' => $state['text'],
];
}
if ($status === 'draft') {
$errors = $page->errors();
// switch to the error dialog if there are
// errors and the draft cannot be published
if (count($errors) > 0) {
return [
'component' => 'k-error-dialog',
'props' => [
'message' => t('error.page.changeStatus.incomplete'),
'details' => $errors,
]
];
}
}
$fields = [
'status' => [
'label' => t('page.changeStatus.select'),
'type' => 'radio',
'required' => true,
'options' => $states
]
];
if ($blueprint->num() === 'default') {
$fields['position'] = Field::pagePosition($page, [
'when' => [
'status' => 'listed'
]
]);
$position = $page->panel()->position();
}
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => $fields,
'submitButton' => t('change'),
'value' => [
'status' => $status,
'position' => $position
]
]
];
},
'submit' => function (string $id) {
Find::page($id)->changeStatus(get('status'), get('position'));
return [
'event' => 'page.changeStatus',
];
}
],
// change template
'page.changeTemplate' => [
'pattern' => 'pages/(:any)/changeTemplate',
'load' => function (string $id) {
$page = Find::page($id);
$blueprints = $page->blueprints();
if (count($blueprints) <= 1) {
throw new Exception([
'key' => 'page.changeTemplate.invalid',
'data' => [
'slug' => $id
]
]);
}
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'template' => Field::template($blueprints, [
'required' => true
])
],
'submitButton' => t('change'),
'value' => [
'template' => $page->intendedTemplate()->name()
]
]
];
},
'submit' => function (string $id) {
Find::page($id)->changeTemplate(get('template'));
return [
'event' => 'page.changeTemplate',
];
}
],
// change title
'page.changeTitle' => [
'pattern' => 'pages/(:any)/changeTitle',
'load' => function (string $id) {
$page = Find::page($id);
$permissions = $page->permissions();
$select = get('select', 'title');
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'title' => Field::title([
'required' => true,
'preselect' => $select === 'title',
'disabled' => $permissions->can('changeTitle') === false
]),
'slug' => Field::slug([
'required' => true,
'preselect' => $select === 'slug',
'path' => $page->parent() ? '/' . $page->parent()->id() . '/' : '/',
'disabled' => $permissions->can('changeSlug') === false,
'wizard' => [
'text' => t('page.changeSlug.fromTitle'),
'field' => 'title'
]
])
],
'autofocus' => false,
'submitButton' => t('change'),
'value' => [
'title' => $page->title()->value(),
'slug' => $page->slug(),
]
]
];
},
'submit' => function (string $id) {
$page = Find::page($id);
$title = trim(get('title'));
$slug = trim(get('slug'));
// basic input validation before we move on
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) {
return true;
}
// prepare the response
$response = [
'event' => []
];
// the page title changed
if ($page->title()->value() !== $title) {
$page->changeTitle($title);
$response['event'][] = 'page.changeTitle';
}
// the slug changed
if ($page->slug() !== $slug) {
$newPage = $page->changeSlug($slug);
$response['event'][] = 'page.changeSlug';
$response['dispatch'] = [
'content/move' => [
$oldUrl = $page->panel()->url(true),
$newUrl = $newPage->panel()->url(true)
]
];
// check for a necessary redirect after the slug has changed
if (Panel::referrer() === $oldUrl && $oldUrl !== $newUrl) {
$response['redirect'] = $newUrl;
}
}
return $response;
}
],
// create a new page
'page.create' => [
'pattern' => 'pages/create',
'load' => function () {
// the parent model for the new page
$parent = 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 = get('view', $parent);
// templates will be fetched depending on the
// section settings in the blueprint
$section = 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 || option('debug') === true) {
$fields['template'] = Field::template($blueprints, [
'required' => true
]);
}
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => $fields,
'submitButton' => t('page.draft.create'),
'value' => [
'parent' => $parent,
'slug' => '',
'template' => $template,
'title' => '',
]
]
];
},
'submit' => function () {
$title = trim(get('title'));
if (Str::length($title) === 0) {
throw new InvalidArgumentException([
'key' => 'page.changeTitle.empty'
]);
}
$page = Find::parent(get('parent', 'site'))->createChild([
'content' => ['title' => $title],
'slug' => get('slug'),
'template' => get('template'),
]);
return [
'event' => 'page.create',
'redirect' => $page->panel()->url(true)
];
}
],
// delete page
'page.delete' => [
'pattern' => 'pages/(:any)/delete',
'load' => function (string $id) {
$page = Find::page($id);
$text = tt('page.delete.confirm', [
'title' => Escape::html($page->title()->value())
]);
if ($page->childrenAndDrafts()->count() > 0) {
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'info' => [
'type' => 'info',
'theme' => 'negative',
'text' => t('page.delete.confirm.subpages')
],
'check' => [
'label' => t('page.delete.confirm.title'),
'type' => 'text',
'counter' => false
]
],
'size' => 'medium',
'submitButton' => t('delete'),
'text' => $text,
'theme' => 'negative',
]
];
}
return [
'component' => 'k-remove-dialog',
'props' => [
'text' => $text
]
];
},
'submit' => function (string $id) {
$page = Find::page($id);
$redirect = false;
$referrer = Panel::referrer();
$url = $page->panel()->url(true);
if ($page->childrenAndDrafts()->count() > 0 && get('check') !== $page->title()->value()) {
throw new InvalidArgumentException(['key' => 'page.delete.confirm']);
}
$page->delete(true);
// redirect to the parent model URL
// if the dialog has been opened in the page view
if ($referrer === $url) {
$redirect = $page->parentModel()->panel()->url(true);
}
return [
'event' => 'page.delete',
'dispatch' => ['content/remove' => [$url]],
'redirect' => $redirect
];
}
],
// duplicate page
'page.duplicate' => [
'pattern' => 'pages/(:any)/duplicate',
'load' => function (string $id) {
$page = Find::page($id);
$hasChildren = $page->hasChildren();
$hasFiles = $page->hasFiles();
$toggleWidth = '1/' . count(array_filter([$hasChildren, $hasFiles]));
$fields = [
'title' => Field::title([
'required' => true
]),
'slug' => Field::slug([
'required' => true,
'path' => $page->parent() ? '/' . $page->parent()->id() . '/' : '/',
'wizard' => [
'text' => t('page.changeSlug.fromTitle'),
'field' => 'title'
]
])
];
if ($hasFiles === true) {
$fields['files'] = [
'label' => t('page.duplicate.files'),
'type' => 'toggle',
'required' => true,
'width' => $toggleWidth
];
}
if ($hasChildren === true) {
$fields['children'] = [
'label' => t('page.duplicate.pages'),
'type' => 'toggle',
'required' => true,
'width' => $toggleWidth
];
}
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => $fields,
'submitButton' => t('duplicate'),
'value' => [
'children' => false,
'files' => false,
'slug' => $page->slug() . '-' . Str::slug(t('page.duplicate.appendix')),
'title' => $page->title() . ' ' . t('page.duplicate.appendix')
]
]
];
},
'submit' => function (string $id) {
$newPage = Find::page($id)->duplicate(get('slug'), [
'children' => (bool)get('children'),
'files' => (bool)get('files'),
'title' => (string)get('title'),
]);
return [
'event' => 'page.duplicate',
'redirect' => $newPage->panel()->url(true)
];
}
],
// change filename
'page.file.changeName' => [
'pattern' => '(pages/.*?)/files/(:any)/changeName',
'load' => $files['changeName']['load'],
'submit' => $files['changeName']['submit'],
],
// change sort
'page.file.changeSort' => [
'pattern' => '(pages/.*?)/files/(:any)/changeSort',
'load' => $files['changeSort']['load'],
'submit' => $files['changeSort']['submit'],
],
// delete
'page.file.delete' => [
'pattern' => '(pages/.*?)/files/(:any)/delete',
'load' => $files['delete']['load'],
'submit' => $files['delete']['submit'],
],
// change site title
'site.changeTitle' => [
'pattern' => 'site/changeTitle',
'load' => function () {
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'title' => Field::title([
'required' => true,
'preselect' => true
])
],
'submitButton' => t('rename'),
'value' => [
'title' => site()->title()->value()
]
]
];
},
'submit' => function () {
site()->changeTitle(get('title'));
return [
'event' => 'site.changeTitle',
];
}
],
// change filename
'site.file.changeName' => [
'pattern' => '(site)/files/(:any)/changeName',
'load' => $files['changeName']['load'],
'submit' => $files['changeName']['submit'],
],
// change sort
'site.file.changeSort' => [
'pattern' => '(site)/files/(:any)/changeSort',
'load' => $files['changeSort']['load'],
'submit' => $files['changeSort']['submit'],
],
// delete
'site.file.delete' => [
'pattern' => '(site)/files/(:any)/delete',
'load' => $files['delete']['load'],
'submit' => $files['delete']['submit'],
],
];

View file

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

View file

@ -0,0 +1,55 @@
<?php
use Kirby\Toolkit\Escape;
return [
'pages' => [
'label' => t('pages'),
'icon' => 'page',
'query' => function (string $query = null) {
$pages = site()
->index(true)
->search($query)
->filter('isReadable', true)
->limit(10);
$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())
];
}
return $results;
}
],
'files' => [
'label' => t('files'),
'icon' => 'image',
'query' => function (string $query = null) {
$files = site()
->index(true)
->filter('isReadable', true)
->files()
->search($query)
->limit(10);
$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())
];
}
return $results;
}
]
];

View file

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

View file

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

View file

@ -0,0 +1,43 @@
<?php
use Kirby\Panel\Field;
return [
// license registration
'registration' => [
'load' => function () {
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'license' => [
'label' => t('license.register.label'),
'type' => 'text',
'required' => true,
'counter' => false,
'placeholder' => 'K3-',
'help' => t('license.register.help')
],
'email' => Field::email([
'required' => true
])
],
'submitButton' => t('license.register'),
'value' => [
'license' => null,
'email' => null
]
]
];
},
'submit' => function () {
// @codeCoverageIgnoreStart
kirby()->system()->register(get('license'), get('email'));
return [
'event' => 'system.register',
'message' => t('license.register.success')
];
// @codeCoverageIgnoreEnd
}
],
];

View file

@ -0,0 +1,46 @@
<?php
return [
'system' => [
'pattern' => 'system',
'action' => function () {
$kirby = kirby();
$system = $kirby->system();
$license = $system->license();
// @codeCoverageIgnoreStart
if ($license === true) {
// valid license, but user is not admin
$license = 'Kirby 3';
} elseif ($license === false) {
// no valid license
$license = null;
}
// @codeCoverageIgnoreEnd
$plugins = $system->plugins()->values(function ($plugin) {
return [
'author' => $plugin->authorsNames(),
'license' => $plugin->license(),
'link' => $plugin->link(),
'name' => $plugin->name(),
'version' => $plugin->version(),
];
});
return [
'component' => 'k-system-view',
'props' => [
'debug' => $kirby->option('debug', false),
'license' => $license,
'plugins' => $plugins,
'php' => phpversion(),
'server' => $system->serverSoftware(),
'ssl' => Server::https(),
'version' => $kirby->version(),
]
];
}
],
];

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1 @@
<hr />

View file

@ -0,0 +1,4 @@
name: field.blocks.line.name
icon: divider
preview: line
wysiwyg: true

View file

@ -1,7 +1,7 @@
<?php /** @var \Kirby\Cms\Block $block */ ?>
<?php if ($block->url()->isNotEmpty()): ?>
<?php if ($video = video($block->url())): ?>
<figure>
<?= video($block->url()) ?>
<?= $video ?>
<?php if ($block->caption()->isNotEmpty()): ?>
<figcaption><?= $block->caption() ?></figcaption>
<?php endif ?>

View file

@ -1,26 +0,0 @@
<?php
$blocksRoot = __DIR__ . '/blocks';
return [
// blocks
'blocks/code' => $blocksRoot . '/code/code.yml',
'blocks/gallery' => $blocksRoot . '/gallery/gallery.yml',
'blocks/heading' => $blocksRoot . '/heading/heading.yml',
'blocks/image' => $blocksRoot . '/image/image.yml',
'blocks/list' => $blocksRoot . '/list/list.yml',
'blocks/markdown' => $blocksRoot . '/markdown/markdown.yml',
'blocks/quote' => $blocksRoot . '/quote/quote.yml',
'blocks/table' => $blocksRoot . '/table/table.yml',
'blocks/text' => $blocksRoot . '/text/text.yml',
'blocks/video' => $blocksRoot . '/video/video.yml',
// file blueprints
'files/default' => __DIR__ . '/blueprints/files/default.yml',
// page blueprints
'pages/default' => __DIR__ . '/blueprints/pages/default.yml',
// site blueprints
'site' => __DIR__ . '/blueprints/site.yml'
];

View file

@ -5,7 +5,6 @@ fields:
level:
type: select
width: 1/2
default: 1
empty: false
default: "2"
options:

View file

@ -3,10 +3,12 @@
use Kirby\Cms\App;
use Kirby\Cms\Collection;
use Kirby\Cms\File;
use Kirby\Cms\Filename;
use Kirby\Cms\FileVersion;
use Kirby\Cms\Template;
use Kirby\Data\Data;
use Kirby\Email\PHPMailer as Emailer;
use Kirby\Filesystem\F;
use Kirby\Filesystem\Filename;
use Kirby\Http\Server;
use Kirby\Http\Uri;
use Kirby\Http\Url;
@ -14,7 +16,6 @@ use Kirby\Image\Darkroom;
use Kirby\Text\Markdown;
use Kirby\Text\SmartyPants;
use Kirby\Toolkit\A;
use Kirby\Toolkit\F;
use Kirby\Toolkit\Str;
use Kirby\Toolkit\Tpl as Snippet;
@ -55,6 +56,17 @@ return [
return $output;
},
/**
* Add your own email provider
*
* @param \Kirby\Cms\App $kirby Kirby instance
* @param array $props
* @param bool $debug
*/
'email' => function (App $kirby, array $props = [], bool $debug = false) {
return new Emailer($props, $debug);
},
/**
* Modify URLs for file objects
*
@ -70,7 +82,7 @@ return [
* Adapt file characteristics
*
* @param \Kirby\Cms\App $kirby Kirby instance
* @param \Kirby\Cms\File|\Kirby\Cms\FileModifications $file The file object
* @param \Kirby\Cms\File|\Kirby\Filesystem\Asset $file The file object
* @param array $options All thumb options (width, height, crop, blur, grayscale)
* @return \Kirby\Cms\File|\Kirby\Cms\FileVersion
*/
@ -233,7 +245,7 @@ return [
}
}
return $item->searchHits > 0 ? true : false;
return $item->searchHits > 0;
});
return $results->sort('searchScore', 'desc');
@ -305,15 +317,18 @@ return [
* Add your own thumb generator
*
* @param \Kirby\Cms\App $kirby Kirby instance
* @param string $src The root of the original file
* @param string $template The template for the root to the desired destination
* @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 $template, array $options): string {
$darkroom = Darkroom::factory(option('thumbs.driver', 'gd'), option('thumbs', []));
'thumb' => function (App $kirby, string $src, string $dst, array $options): string {
$darkroom = Darkroom::factory(
option('thumbs.driver', 'gd'),
option('thumbs', [])
);
$options = $darkroom->preprocess($src, $options);
$root = (new Filename($src, $template, $options))->toString();
$root = (new Filename($src, $dst, $options))->toString();
F::copy($src, $root, true);
$darkroom->process($root, $options);
@ -327,13 +342,9 @@ return [
* @param \Kirby\Cms\App $kirby Kirby instance
* @param string $path URL path
* @param array|string|null $options Array of options for the Uri class
* @param Closure $originalHandler Deprecated: Callback function to the original URL handler with `$path` and `$options` as parameters
* Use `$kirby->nativeComponent('url')` inside your URL component instead.
* @return string
*
* @todo Remove $originalHandler parameter in 3.6.0
*/
'url' => function (App $kirby, string $path = null, $options = null, Closure $originalHandler = null): string {
'url' => function (App $kirby, string $path = null, $options = null): string {
$language = null;
// get language from simple string option

View file

@ -1,32 +0,0 @@
<?php
return [
'blocks' => 'Kirby\Form\Field\BlocksField',
'checkboxes' => __DIR__ . '/fields/checkboxes.php',
'date' => __DIR__ . '/fields/date.php',
'email' => __DIR__ . '/fields/email.php',
'files' => __DIR__ . '/fields/files.php',
'gap' => __DIR__ . '/fields/gap.php',
'headline' => __DIR__ . '/fields/headline.php',
'hidden' => __DIR__ . '/fields/hidden.php',
'info' => __DIR__ . '/fields/info.php',
'layout' => 'Kirby\Form\Field\LayoutField',
'line' => __DIR__ . '/fields/line.php',
'list' => __DIR__ . '/fields/list.php',
'multiselect' => __DIR__ . '/fields/multiselect.php',
'number' => __DIR__ . '/fields/number.php',
'pages' => __DIR__ . '/fields/pages.php',
'radio' => __DIR__ . '/fields/radio.php',
'range' => __DIR__ . '/fields/range.php',
'select' => __DIR__ . '/fields/select.php',
'structure' => __DIR__ . '/fields/structure.php',
'tags' => __DIR__ . '/fields/tags.php',
'tel' => __DIR__ . '/fields/tel.php',
'text' => __DIR__ . '/fields/text.php',
'textarea' => __DIR__ . '/fields/textarea.php',
'time' => __DIR__ . '/fields/time.php',
'toggle' => __DIR__ . '/fields/toggle.php',
'url' => __DIR__ . '/fields/url.php',
'users' => __DIR__ . '/fields/users.php',
'writer' => __DIR__ . '/fields/writer.php'
];

View file

@ -5,9 +5,10 @@ use Kirby\Toolkit\A;
return [
'mixins' => [
'picker',
'filepicker',
'layout',
'min',
'picker',
'upload'
],
'props' => [
@ -27,20 +28,6 @@ return [
return $default;
},
/**
* Changes the layout of the selected files. Available layouts: `list`, `cards`
*/
'layout' => function (string $layout = 'list') {
return $layout;
},
/**
* Layout size for cards: `tiny`, `small`, `medium`, `large` or `huge`
*/
'size' => function (string $size = 'auto') {
return $size;
},
'value' => function ($value = null) {
return $value;
}
@ -68,11 +55,12 @@ return [
],
'methods' => [
'fileResponse' => function ($file) {
return $file->panelPickerData([
'image' => $this->image,
'info' => $this->info ?? false,
'model' => $this->model(),
'text' => $this->text,
return $file->panel()->pickerData([
'image' => $this->image,
'info' => $this->info ?? false,
'layout' => $this->layout,
'model' => $this->model(),
'text' => $this->text,
]);
},
'toFiles' => function ($value = null) {
@ -101,6 +89,7 @@ return [
return $field->filepicker([
'image' => $field->image(),
'info' => $field->info(),
'layout' => $field->layout(),
'limit' => $field->limit(),
'page' => $this->requestQuery('page'),
'query' => $field->query(),
@ -116,14 +105,18 @@ return [
$field = $this->field();
$uploads = $field->uploads();
// move_uploaded_file() not working with unit test
// @codeCoverageIgnoreStart
return $field->upload($this, $uploads, function ($file, $parent) use ($field) {
return $file->panelPickerData([
'image' => $field->image(),
'info' => $field->info(),
'model' => $field->model(),
'text' => $field->text(),
return $file->panel()->pickerData([
'image' => $field->image(),
'info' => $field->info(),
'layout' => $field->layout(),
'model' => $field->model(),
'text' => $field->text(),
]);
});
// @codeCoverageIgnoreEnd
}
]
];

View file

@ -34,7 +34,7 @@ return [
'computed' => [
'text' => function () {
if ($text = $this->text) {
$text = $this->model()->toString($text);
$text = $this->model()->toSafeString($text);
$text = $this->kirby()->kirbytext($text);
return $text;
}

View file

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

View file

@ -27,6 +27,7 @@ return [
if ($template) {
$file = new File([
'filename' => 'tmp',
'parent' => $this->model(),
'template' => $template
]);

View file

@ -4,7 +4,12 @@ use Kirby\Data\Data;
use Kirby\Toolkit\A;
return [
'mixins' => ['min', 'pagepicker', 'picker'],
'mixins' => [
'layout',
'min',
'pagepicker',
'picker',
],
'props' => [
/**
* Unset inherited props
@ -22,13 +27,6 @@ return [
return $this->toPages($default);
},
/**
* Changes the layout of the selected files. Available layouts: `list`, `cards`
*/
'layout' => function (string $layout = 'list') {
return $layout;
},
/**
* Optional query to select a specific set of pages
*/
@ -36,13 +34,6 @@ return [
return $query;
},
/**
* Layout size for cards: `tiny`, `small`, `medium`, `large` or `huge`
*/
'size' => function (string $size = 'auto') {
return $size;
},
/**
* Optionally include subpages of pages
*/
@ -62,10 +53,11 @@ return [
],
'methods' => [
'pageResponse' => function ($page) {
return $page->panelPickerData([
'image' => $this->image,
'info' => $this->info,
'text' => $this->text,
return $page->panel()->pickerData([
'image' => $this->image,
'info' => $this->info,
'layout' => $this->layout,
'text' => $this->text,
]);
},
'toPages' => function ($value = null) {
@ -95,6 +87,7 @@ return [
return $field->pagepicker([
'image' => $field->image(),
'info' => $field->info(),
'layout' => $field->layout(),
'limit' => $field->limit(),
'page' => $this->requestQuery('page'),
'parent' => $this->requestQuery('parent'),

View file

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

View file

@ -1,7 +1,7 @@
<?php
use Kirby\Cms\Form;
use Kirby\Data\Data;
use Kirby\Form\Form;
use Kirby\Toolkit\I18n;
return [

View file

@ -109,7 +109,7 @@ return [
return [
'filename' => $file->filename(),
'dragText' => $file->dragText('auto', $absolute),
'dragText' => $file->panel()->dragText('auto', $absolute),
];
});
}

View file

@ -1,6 +1,7 @@
<?php
use Kirby\Exception\InvalidArgumentException;
use Kirby\Toolkit\A;
use Kirby\Toolkit\I18n;
return [
@ -20,19 +21,25 @@ return [
* Sets the text next to the toggle. The text can be a string or an array of two options. The first one is the negative text and the second one the positive. The text will automatically switch when the toggle is triggered.
*/
'text' => function ($value = null) {
$model = $this->model();
if (is_array($value) === true) {
if (A::isAssociative($value) === true) {
return I18n::translate($value, $value);
return $model->toSafeString(I18n::translate($value, $value));
}
foreach ($value as $key => $val) {
$value[$key] = I18n::translate($val, $val);
$value[$key] = $model->toSafeString(I18n::translate($val, $val));
}
return $value;
}
return I18n::translate($value, $value);
if (empty($value) === false) {
return $model->toSafeString(I18n::translate($value, $value));
}
return $value;
},
],
'computed' => [

View file

@ -4,7 +4,12 @@ use Kirby\Data\Data;
use Kirby\Toolkit\A;
return [
'mixins' => ['min', 'picker', 'userpicker'],
'mixins' => [
'layout',
'min',
'picker',
'userpicker'
],
'props' => [
/**
* Unset inherited props
@ -44,10 +49,11 @@ return [
],
'methods' => [
'userResponse' => function ($user) {
return $user->panelPickerData([
'info' => $this->info,
'image' => $this->image,
'text' => $this->text,
return $user->panel()->pickerData([
'info' => $this->info,
'image' => $this->image,
'layout' => $this->layout,
'text' => $this->text,
]);
},
'toUsers' => function ($value = null) {
@ -77,6 +83,7 @@ return [
return $field->userpicker([
'image' => $field->image(),
'info' => $field->info(),
'layout' => $field->layout(),
'limit' => $field->limit(),
'page' => $this->requestQuery('page'),
'query' => $field->query(),

View file

@ -1,5 +1,7 @@
<?php
use Kirby\Sane\Sane;
return [
'props' => [
/**
@ -11,14 +13,14 @@ return [
return $inline;
},
/**
* Sets the allowed HTML formats. Available formats: `bold`, `italic`, `underline`, `strike`, `code`, `link`. Activate them all by passing `true`. Deactivate them all by passing `false`
* Sets the allowed HTML formats. Available formats: `bold`, `italic`, `underline`, `strike`, `code`, `link`, `email`. Activate them all by passing `true`. Deactivate them all by passing `false`
* @param array|bool $marks
*/
'marks' => function ($marks = true) {
return $marks;
},
/**
* Sets the allowed nodes. Available nodes: `bulletList`, `orderedList`, `heading`, `horizontalRule`, `listItem`. Activate/deactivate them all by passing `true`/`false`. Default nodes are `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) {
@ -27,7 +29,7 @@ return [
],
'computed' => [
'value' => function () {
return trim($this->value);
return Sane::sanitize(trim($this->value), 'html');
}
],
];

View file

@ -1,13 +1,14 @@
<?php
use Kirby\Cms\App;
use Kirby\Cms\Asset;
use Kirby\Cms\Html;
use Kirby\Cms\Response;
use Kirby\Cms\Url;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Filesystem\Asset;
use Kirby\Filesystem\F;
use Kirby\Http\Router;
use Kirby\Toolkit\Escape;
use Kirby\Toolkit\F;
use Kirby\Toolkit\I18n;
use Kirby\Toolkit\Str;
use Kirby\Toolkit\V;
@ -16,7 +17,7 @@ use Kirby\Toolkit\V;
* Helper to create an asset object
*
* @param string $path
* @return \Kirby\Cms\Asset
* @return \Kirby\Filesystem\Asset
*/
function asset(string $path)
{
@ -26,12 +27,12 @@ function asset(string $path)
/**
* Generates a list of HTML attributes
*
* @param array $attr A list of attributes as key/value array
* @param string $before An optional string that will be prepended if the result is not empty
* @param string $after An optional string that will be appended if the result is not empty
* @return string
* @param array|null $attr A list of attributes as key/value array
* @param string|null $before An optional string that will be prepended if the result is not empty
* @param string|null $after An optional string that will be appended if the result is not empty
* @return string|null
*/
function attr(array $attr = null, $before = null, $after = null)
function attr(?array $attr = null, ?string $before = null, ?string $after = null): ?string
{
if ($attrs = Html::attr($attr)) {
return $before . $attrs . $after;
@ -54,28 +55,33 @@ function collection(string $name)
/**
* Checks / returns a CSRF token
*
* @param string $check Pass a token here to compare it to the one in the session
* @param string|null $check Pass a token here to compare it to the one in the session
* @return string|bool Either the token or a boolean check result
*/
function csrf(string $check = null)
function csrf(?string $check = null)
{
$session = App::instance()->session();
// check explicitly if there have been no arguments at all;
// no arguments, generate/return a token
// (check explicitly if there have been no arguments at all;
// checking for null introduces a security issue because null could come
// from user input or bugs in the calling code!
// from user input or bugs in the calling code!)
if (func_num_args() === 0) {
// no arguments, generate/return a token
$token = $session->get('kirby.csrf');
if (is_string($token) !== true) {
$token = bin2hex(random_bytes(32));
$session->set('kirby.csrf', $token);
}
return $token;
} elseif (is_string($check) === true && is_string($session->get('kirby.csrf')) === true) {
// argument has been passed, check the token
}
// argument has been passed, check the token
if (
is_string($check) === true &&
is_string($session->get('kirby.csrf')) === true
) {
return hash_equals($session->get('kirby.csrf'), $check) === true;
}
@ -174,7 +180,7 @@ if (function_exists('e') === false) {
* @param string $context Location of output (`html`, `attr`, `js`, `css`, `url` or `xml`)
* @return string Escaped data
*/
function esc($string, $context = 'html')
function esc(string $string, string $context = 'html'): string
{
if (method_exists('Kirby\Toolkit\Escape', $context) === true) {
return Escape::$context($string);
@ -200,10 +206,10 @@ function get($key = null, $default = null)
* Embeds a Github Gist
*
* @param string $url
* @param string $file
* @param string|null $file
* @return string
*/
function gist(string $url, string $file = null): string
function gist(string $url, ?string $file = null): string
{
return kirbytag([
'gist' => $url,
@ -255,10 +261,10 @@ function html(?string $string, bool $keepTags = false)
* Example:
* <?= image('some/page/myimage.jpg') ?>
*
* @param string $path
* @param string|null $path
* @return \Kirby\Cms\File|null
*/
function image(string $path = null)
function image(?string $path = null)
{
if ($path === null) {
return page()->image();
@ -296,9 +302,9 @@ function image(string $path = null)
* @param array $data
* @param array $rules
* @param array $messages
* @return false|array
* @return array
*/
function invalid(array $data = [], array $rules = [], array $messages = [])
function invalid(array $data = [], array $rules = [], array $messages = []): array
{
$errors = [];
@ -407,12 +413,12 @@ function kirby()
* Makes it possible to use any defined Kirbytag as standalone function
*
* @param string|array $type
* @param string $value
* @param string|null $value
* @param array $attr
* @param array $data
* @return string
*/
function kirbytag($type, string $value = null, array $attr = [], array $data = []): string
function kirbytag($type, ?string $value = null, array $attr = [], array $data = []): string
{
if (is_array($type) === true) {
$kirbytag = $type;
@ -434,11 +440,11 @@ function kirbytag($type, string $value = null, array $attr = [], array $data = [
* Parses KirbyTags in the given string. Shortcut
* for `$kirby->kirbytags($text, $data)`
*
* @param string $text
* @param string|null $text
* @param array $data
* @return string
*/
function kirbytags(string $text = null, array $data = []): string
function kirbytags(?string $text = null, array $data = []): string
{
return App::instance()->kirbytags($text, $data);
}
@ -447,11 +453,11 @@ function kirbytags(string $text = null, array $data = []): string
* Parses KirbyTags and Markdown in the
* given string. Shortcut for `$kirby->kirbytext()`
*
* @param string $text
* @param string|null $text
* @param array $data
* @return string
*/
function kirbytext(string $text = null, array $data = []): string
function kirbytext(?string $text = null, array $data = []): string
{
return App::instance()->kirbytext($text, $data);
}
@ -461,11 +467,11 @@ function kirbytext(string $text = null, array $data = []): string
* given string.
* @since 3.1.0
*
* @param string $text
* @param string|null $text
* @param array $data
* @return string
*/
function kirbytextinline(string $text = null, array $data = []): string
function kirbytextinline(?string $text = null, array $data = []): string
{
return App::instance()->kirbytext($text, $data, true);
}
@ -473,11 +479,11 @@ function kirbytextinline(string $text = null, array $data = []): string
/**
* Shortcut for `kirbytext()` helper
*
* @param string $text
* @param string|null $text
* @param array $data
* @return string
*/
function kt(string $text = null, array $data = []): string
function kt(?string $text = null, array $data = []): string
{
return kirbytext($text, $data);
}
@ -486,11 +492,11 @@ function kt(string $text = null, array $data = []): string
* Shortcut for `kirbytextinline()` helper
* @since 3.1.0
*
* @param string $text
* @param string|null $text
* @param array $data
* @return string
*/
function kti(string $text = null, array $data = []): string
function kti(?string $text = null, array $data = []): string
{
return kirbytextinline($text, $data);
}
@ -499,10 +505,10 @@ function kti(string $text = null, array $data = []): string
* A super simple class autoloader
*
* @param array $classmap
* @param string $base
* @param string|null $base
* @return void
*/
function load(array $classmap, string $base = null)
function load(array $classmap, ?string $base = null)
{
// convert all classnames to lowercase
$classmap = array_change_key_case($classmap);
@ -526,10 +532,10 @@ function load(array $classmap, string $base = null)
* Parses markdown in the given string. Shortcut for
* `$kirby->markdown($text)`
*
* @param string $text
* @param string|null $text
* @return string
*/
function markdown(string $text = null): string
function markdown(?string $text = null): string
{
return App::instance()->markdown($text);
}
@ -551,7 +557,8 @@ function option(string $key, $default = null)
* id or the current page when no id is specified
*
* @param string|array ...$id
* @return \Kirby\Cms\Page|null
* @return \Kirby\Cms\Page|\Kirby\Cms\Pages|null
* @todo reduce to one parameter in 3.7.0 (also change return and return type)
*/
function page(...$id)
{
@ -559,6 +566,12 @@ function page(...$id)
return App::instance()->site()->page();
}
if (count($id) > 1) {
// @codeCoverageIgnoreStart
deprecated('Passing multiple parameters to the `page()` helper has been deprecated. Please use the `pages()` helper instead.');
// @codeCoverageIgnoreEnd
}
return App::instance()->site()->find(...$id);
}
@ -566,10 +579,17 @@ function page(...$id)
* Helper to build page collections
*
* @param string|array ...$id
* @return \Kirby\Cms\Pages
* @return \Kirby\Cms\Page|\Kirby\Cms\Pages|null
* @todo return only Pages|null in 3.7.0, wrap in Pages for single passed id
*/
function pages(...$id)
{
if (count($id) === 1) {
// @codeCoverageIgnoreStart
deprecated('Passing a single id to the `pages()` helper will return a Kirby\Cms\Pages collection with a single element instead of the single Kirby\Cms\Page object itself - starting in 3.7.0.');
// @codeCoverageIgnoreEnd
}
return App::instance()->site()->find(...$id);
}
@ -577,10 +597,10 @@ function pages(...$id)
* Returns a single param from the URL
*
* @param string $key
* @param string $fallback
* @param string|null $fallback
* @return string|null
*/
function param(string $key, string $fallback = null): ?string
function param(string $key, ?string $fallback = null): ?string
{
return App::instance()->request()->url()->params()->$key ?? $fallback;
}
@ -608,6 +628,22 @@ function r($condition, $value, $alternative = null)
return $condition ? $value : $alternative;
}
/**
* Creates a micro-router and executes
* the routing action immediately
* @since 3.6.0
*
* @param string|null $path
* @param string $method
* @param array $routes
* @param \Closure|null $callback
* @return mixed
*/
function router(?string $path = null, string $method = 'GET', array $routes = [], ?Closure $callback = null)
{
return (new Router($routes))->call($path, $method, $callback);
}
/**
* Returns the current site object
*
@ -623,6 +659,7 @@ function site()
*
* @param mixed $value
* @return int
* @throws \Kirby\Exception\InvalidArgumentException
*/
function size($value): int
{
@ -655,10 +692,10 @@ function size($value): int
* Enhances the given string with
* smartypants. Shortcut for `$kirby->smartypants($text)`
*
* @param string $text
* @param string|null $text
* @return string
*/
function smartypants(string $text = null): string
function smartypants(?string $text = null): string
{
return App::instance()->smartypants($text);
}
@ -725,7 +762,7 @@ function svg($file)
*
* @param string|array $key
* @param string|null $fallback
* @return mixed
* @return array|string|null
*/
function t($key, string $fallback = null)
{
@ -735,11 +772,11 @@ function t($key, string $fallback = null)
/**
* Translates a count
*
* @param string|array $key
* @param string $key
* @param int $count
* @return mixed
*/
function tc($key, int $count)
function tc(string $key, int $count)
{
return I18n::translateCount($key, $count);
}
@ -748,11 +785,11 @@ function tc($key, int $count)
* Rounds the minutes of the given date
* by the defined step
*
* @param string $date
* @param int $step array of `unit` and `size` to round to nearest
* @param string|null $date
* @param int|array|null $step array of `unit` and `size` to round to nearest
* @return int|null
*/
function timestamp(string $date = null, $step = null): ?int
function timestamp(?string $date = null, $step = null): ?int
{
if (V::date($date) === false) {
return null;
@ -807,7 +844,7 @@ function timestamp(string $date = null, $step = null): ?int
);
// on error, convert `false` into `null`
return $timestamp ? $timestamp : null;
return $timestamp ?? null;
}
/**
@ -815,12 +852,12 @@ function timestamp(string $date = null, $step = null): ?int
* placeholders in the text
*
* @param string $key
* @param string $fallback
* @param array $replace
* @param string $locale
* @param string|array|null $fallback
* @param array|null $replace
* @param string|null $locale
* @return string
*/
function tt(string $key, $fallback = null, array $replace = null, string $locale = null)
function tt(string $key, $fallback = null, ?array $replace = null, ?string $locale = null)
{
return I18n::template($key, $fallback, $replace, $locale);
}
@ -829,12 +866,12 @@ function tt(string $key, $fallback = null, array $replace = null, string $locale
* Builds a Twitter link
*
* @param string $username
* @param string $text
* @param string $title
* @param string $class
* @param string|null $text
* @param string|null $title
* @param string|null $class
* @return string
*/
function twitter(string $username, string $text = null, string $title = null, string $class = null): string
function twitter(string $username, ?string $text = null, ?string $title = null, ?string $class = null): string
{
return kirbytag([
'twitter' => $username,
@ -847,11 +884,11 @@ function twitter(string $username, string $text = null, string $title = null, st
/**
* Shortcut for url()
*
* @param string $path
* @param string|null $path
* @param array|string|null $options
* @return string
*/
function u(string $path = null, $options = null): string
function u(?string $path = null, $options = null): string
{
return Url::to($path, $options);
}
@ -859,11 +896,11 @@ function u(string $path = null, $options = null): string
/**
* Builds an absolute URL for a given path
*
* @param string $path
* @param string|null $path
* @param array|string|null $options
* @return string
*/
function url(string $path = null, $options = null): string
function url(?string $path = null, $options = null): string
{
return Url::to($path, $options);
}
@ -906,9 +943,9 @@ function uuid(): string
* @param string $url
* @param array $options
* @param array $attr
* @return string
* @return string|null
*/
function video(string $url, array $options = [], array $attr = []): string
function video(string $url, array $options = [], array $attr = []): ?string
{
return Html::video($url, $options, $attr);
}
@ -919,9 +956,9 @@ function video(string $url, array $options = [], array $attr = []): string
* @param string $url
* @param array $options
* @param array $attr
* @return string
* @return string|null
*/
function vimeo(string $url, array $options = [], array $attr = []): string
function vimeo(string $url, array $options = [], array $attr = []): ?string
{
return Html::vimeo($url, $options, $attr);
}
@ -945,9 +982,9 @@ function widont(string $string = null): string
* @param string $url
* @param array $options
* @param array $attr
* @return string
* @return string|null
*/
function youtube(string $url, array $options = [], array $attr = []): string
function youtube(string $url, array $options = [], array $attr = []): ?string
{
return Html::youtube($url, $options, $attr);
}

View file

@ -366,7 +366,7 @@ return function (App $app) {
* @return \Kirby\Cms\Field
*/
'html' => function (Field $field) {
$field->value = htmlentities($field->value, ENT_COMPAT, 'utf-8');
$field->value = Html::encode($field->value);
return $field;
},
@ -498,13 +498,14 @@ return function (App $app) {
*/
'replace' => function (Field $field, array $data = [], string $fallback = '') use ($app) {
if ($parent = $field->parent()) {
$field->value = $field->parent()->toString($field->value, $data, $fallback);
// never pass `null` as the $template to avoid the fallback to the model ID
$field->value = $parent->toString($field->value ?? '', $data, $fallback);
} else {
$field->value = Str::template($field->value, array_replace([
'kirby' => $app,
'site' => $app->site(),
'page' => $app->page()
], $data), $fallback);
], $data), ['fallback' => $fallback]);
}
return $field;

View file

@ -1,93 +0,0 @@
<?php
return [
// kirby
'kirby' => function (array $roots) {
return realpath(__DIR__ . '/../');
},
// i18n
'i18n' => function (array $roots) {
return $roots['kirby'] . '/i18n';
},
'i18n:translations' => function (array $roots) {
return $roots['i18n'] . '/translations';
},
'i18n:rules' => function (array $roots) {
return $roots['i18n'] . '/rules';
},
// index
'index' => function (array $roots) {
return realpath(__DIR__ . '/../../');
},
// assets
'assets' => function (array $roots) {
return $roots['index'] . '/assets';
},
// content
'content' => function (array $roots) {
return $roots['index'] . '/content';
},
// media
'media' => function (array $roots) {
return $roots['index'] . '/media';
},
// panel
'panel' => function (array $roots) {
return $roots['kirby'] . '/panel';
},
// site
'site' => function (array $roots) {
return $roots['index'] . '/site';
},
'accounts' => function (array $roots) {
return $roots['site'] . '/accounts';
},
'blueprints' => function (array $roots) {
return $roots['site'] . '/blueprints';
},
'cache' => function (array $roots) {
return $roots['site'] . '/cache';
},
'collections' => function (array $roots) {
return $roots['site'] . '/collections';
},
'config' => function (array $roots) {
return $roots['site'] . '/config';
},
'controllers' => function (array $roots) {
return $roots['site'] . '/controllers';
},
'languages' => function (array $roots) {
return $roots['site'] . '/languages';
},
'logs' => function (array $roots) {
return $roots['site'] . '/logs';
},
'models' => function (array $roots) {
return $roots['site'] . '/models';
},
'plugins' => function (array $roots) {
return $roots['site'] . '/plugins';
},
'sessions' => function (array $roots) {
return $roots['site'] . '/sessions';
},
'snippets' => function (array $roots) {
return $roots['site'] . '/snippets';
},
'templates' => function (array $roots) {
return $roots['site'] . '/templates';
},
// blueprints
'roles' => function (array $roots) {
return $roots['blueprints'] . '/users';
},
];

View file

@ -2,9 +2,9 @@
use Kirby\Cms\LanguageRoutes;
use Kirby\Cms\Media;
use Kirby\Cms\Panel;
use Kirby\Cms\PanelPlugins;
use Kirby\Cms\PluginAssets;
use Kirby\Panel\Panel;
use Kirby\Panel\Plugins;
use Kirby\Toolkit\Str;
return function ($kirby) {
@ -50,7 +50,7 @@ return function ($kirby) {
'pattern' => $media . '/plugins/index.(css|js)',
'env' => 'media',
'action' => function (string $type) use ($kirby) {
$plugins = new PanelPlugins();
$plugins = new Plugins();
return $kirby
->response()
@ -65,17 +65,6 @@ return function ($kirby) {
return PluginAssets::resolve($provider . '/' . $pluginName, $filename . '.' . $extension);
}
],
[
'pattern' => $panel . '/(:all?)',
'env' => 'panel',
'action' => function () use ($kirby) {
if ($kirby->option('panel') === false) {
return null;
}
return Panel::render($kirby);
}
],
[
'pattern' => $media . '/pages/(:all)/(:any)/(:any)',
'env' => 'media',
@ -103,7 +92,15 @@ return function ($kirby) {
'action' => function ($path, $hash, $filename) {
return Media::thumb($path, $hash, $filename);
}
]
],
[
'pattern' => $panel . '/(:all?)',
'method' => 'ALL',
'env' => 'panel',
'action' => function ($path = null) {
return Panel::router($path);
}
],
];
// Multi-language setup

View file

@ -1,6 +1,6 @@
<?php
use Kirby\Cms\Form;
use Kirby\Form\Form;
return [
'props' => [

View file

@ -1,7 +1,6 @@
<?php
use Kirby\Cms\File;
use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
return [
@ -29,7 +28,7 @@ return [
return $image ?? [];
},
/**
* Optional info text setup. Info text is shown on the right (lists) or below (cards) the filename.
* Optional info text setup. Info text is shown on the right (lists, cardlets) or below (cards) the filename.
*/
'info' => function ($info = null) {
return I18n::translate($info, $info);
@ -70,6 +69,7 @@ return [
if ($this->template) {
$file = new File([
'filename' => 'tmp',
'parent' => $this->model(),
'template' => $this->template
]);
@ -90,7 +90,7 @@ return [
if ($this->sortBy) {
$files = $files->sort(...$files::sortArgs($this->sortBy));
} else {
$files = $files->sort('sort', 'asc', 'filename', 'asc');
$files = $files->sorted();
}
// flip
@ -115,28 +115,21 @@ return [
$dragTextAbsolute = $this->model->is($this->parent) === false;
foreach ($this->files as $file) {
$image = $file->panelImage($this->image);
// escape the default text
// TODO: no longer needed in 3.6
$text = $file->toString($this->text);
if ($this->text === '{{ file.filename }}') {
$text = Escape::html($text);
}
$panel = $file->panel();
$data[] = [
'dragText' => $file->dragText('auto', $dragTextAbsolute),
'dragText' => $panel->dragText('auto', $dragTextAbsolute),
'extension' => $file->extension(),
'filename' => $file->filename(),
'id' => $file->id(),
'icon' => $file->panelIcon($image),
'image' => $image,
'info' => $file->toString($this->info ?? false),
'link' => $file->panelUrl(true),
'mime' => $file->mime(),
'parent' => $file->parent()->panelPath(),
'text' => $text,
'url' => $file->url(),
'filename' => $file->filename(),
'id' => $file->id(),
'image' => $panel->image($this->image, $this->layout),
'info' => $file->toSafeString($this->info ?? false),
'link' => $panel->url(true),
'mime' => $file->mime(),
'parent' => $file->parent()->panel()->path(),
'template' => $file->template(),
'text' => $file->toSafeString($this->text),
'url' => $file->url(),
];
}
@ -174,8 +167,8 @@ return [
];
},
'link' => function () {
$modelLink = $this->model->panelUrl(true);
$parentLink = $this->parent->panelUrl(true);
$modelLink = $this->model->panel()->url(true);
$parentLink = $this->parent->panel()->url(true);
if ($modelLink !== $parentLink) {
return $parentLink;
@ -222,6 +215,7 @@ return [
'max' => $max,
'api' => $this->parent->apiUrl(true) . '/files',
'attributes' => array_filter([
'sort' => $this->sortable === true ? $total + 1 : null,
'template' => $template
])
];

View file

@ -17,7 +17,7 @@ return [
'computed' => [
'text' => function () {
if ($this->text) {
$text = $this->model()->toString($this->text);
$text = $this->model()->toSafeString($this->text);
$text = $this->kirby()->kirbytext($text);
return $text;
}
@ -25,11 +25,9 @@ return [
],
'toArray' => function () {
return [
'options' => [
'headline' => $this->headline,
'text' => $this->text,
'theme' => $this->theme
]
'headline' => $this->headline,
'text' => $this->text,
'theme' => $this->theme
];
}
];

View file

@ -14,7 +14,7 @@ return [
'computed' => [
'empty' => function () {
if ($this->empty) {
return $this->model()->toString($this->empty);
return $this->model()->toSafeString($this->empty);
}
}
]

View file

@ -14,7 +14,7 @@ return [
'computed' => [
'help' => function () {
if ($this->help) {
$help = $this->model()->toString($this->help);
$help = $this->model()->toSafeString($this->help);
$help = $this->kirby()->kirbytext($help);
return $help;
}

View file

@ -3,10 +3,12 @@
return [
'props' => [
/**
* Section layout. Available layout methods: `list`, `cards`.
* Section layout.
* Available layout methods: `list`, `cardlets`, `cards`.
*/
'layout' => function (string $layout = 'list') {
return $layout === 'cards' ? 'cards' : 'list';
$layouts = ['list', 'cardlets', 'cards'];
return in_array($layout, $layouts) ? $layout : 'list';
}
]
];

View file

@ -1,8 +1,8 @@
<?php
use Kirby\Cms\Blueprint;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Toolkit\A;
use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
return [
@ -89,7 +89,13 @@ return [
],
'computed' => [
'parent' => function () {
return $this->parentModel();
$parent = $this->parentModel();
if (is_a($parent, 'Kirby\Cms\Site') === false && is_a($parent, 'Kirby\Cms\Page') === false) {
throw new InvalidArgumentException('The parent is invalid. You must choose the site or a page as parent.');
}
return $parent;
},
'pages' => function () {
switch ($this->status) {
@ -151,32 +157,25 @@ return [
$data = [];
foreach ($this->pages as $item) {
$panel = $item->panel();
$permissions = $item->permissions();
$image = $item->panelImage($this->image);
// escape the default text
// TODO: no longer needed in 3.6
$text = $item->toString($this->text);
if ($this->text === '{{ page.title }}') {
$text = Escape::html($text);
}
$data[] = [
'dragText' => $panel->dragText(),
'id' => $item->id(),
'dragText' => $item->dragText(),
'text' => $text,
'info' => $item->toString($this->info ?? false),
'image' => $panel->image($this->image, $this->layout),
'info' => $item->toSafeString($this->info ?? false),
'link' => $panel->url(true),
'parent' => $item->parentId(),
'icon' => $item->panelIcon($image),
'image' => $image,
'link' => $item->panelUrl(true),
'status' => $item->status(),
'permissions' => [
'sort' => $permissions->can('sort'),
'changeSlug' => $permissions->can('changeSlug'),
'changeStatus' => $permissions->can('changeStatus'),
'changeTitle' => $permissions->can('changeTitle')
]
'changeTitle' => $permissions->can('changeTitle'),
],
'status' => $item->status(),
'template' => $item->intendedTemplate()->name(),
'text' => $item->toSafeString($this->text),
];
}
@ -226,8 +225,8 @@ return [
return true;
},
'link' => function () {
$modelLink = $this->model->panelUrl(true);
$parentLink = $this->parent->panelUrl(true);
$modelLink = $this->model->panel()->url(true);
$parentLink = $this->parent->panel()->url(true);
if ($modelLink !== $parentLink) {
return $parentLink;

View file

@ -1,17 +0,0 @@
<?php
$blocksRoot = __DIR__ . '/blocks';
return [
// blocks
'blocks/code' => $blocksRoot . '/code/code.php',
'blocks/gallery' => $blocksRoot . '/gallery/gallery.php',
'blocks/heading' => $blocksRoot . '/heading/heading.php',
'blocks/image' => $blocksRoot . '/image/image.php',
'blocks/list' => $blocksRoot . '/list/list.php',
'blocks/markdown' => $blocksRoot . '/markdown/markdown.php',
'blocks/quote' => $blocksRoot . '/quote/quote.php',
'blocks/table' => $blocksRoot . '/table/table.php',
'blocks/text' => $blocksRoot . '/text/text.php',
'blocks/video' => $blocksRoot . '/video/video.php',
];

View file

@ -2,8 +2,6 @@
use Kirby\Cms\Html;
use Kirby\Cms\Url;
use Kirby\Text\KirbyTag;
use Kirby\Toolkit\F;
use Kirby\Toolkit\Str;
/**
@ -252,29 +250,6 @@ return [
'width',
],
'html' => function ($tag) {
// all available video tag attributes
$availableAttrs = KirbyTag::$types[$tag->type]['attr'];
// global video tag options
$attrs = $tag->kirby()->option('kirbytext.video', []);
$options = $attrs['options'] ?? [];
// removes options from attributes
if (isset($attrs['options']) === true) {
unset($attrs['options']);
}
// injects default values from global options
// applies only defined attributes to safely update tag props
foreach ($attrs as $key => $value) {
if (
in_array($key, $availableAttrs) === true &&
(isset($tag->{$key}) === false || $tag->{$key} === null)
) {
$tag->{$key} = $value;
}
}
// checks and gets if poster is local file
if (
empty($tag->poster) === false &&
@ -286,26 +261,41 @@ return [
}
}
// converts tag attributes to supported formats (listed below) to output correct html
// booleans: autoplay, controls, loop, muted
// strings : height, poster, preload, width
// for ex : `autoplay` will not work if `false` is a `string` instead of a `boolean`
$attrs = [
'autoplay' => $autoplay = Str::toType($tag->autoplay, 'bool'),
'controls' => Str::toType($tag->controls ?? true, 'bool'),
'height' => $tag->height,
'loop' => Str::toType($tag->loop, 'bool'),
'muted' => Str::toType($tag->muted ?? $autoplay, 'bool'),
'poster' => $tag->poster,
'preload' => $tag->preload,
'width' => $tag->width
];
// handles local and remote video file
if (
// checks video is local or provider(remote)
$isLocalVideo = (
Str::startsWith($tag->value, 'http://') !== true &&
Str::startsWith($tag->value, 'https://') !== true
) {
);
$isProviderVideo = (
$isLocalVideo === false &&
(
Str::contains($tag->value, 'youtu', true) === true ||
Str::contains($tag->value, 'vimeo', true) === true
)
);
// default attributes for local and remote videos
$attrs = [
'height' => $tag->height,
'width' => $tag->width
];
// don't use attributes that iframe doesn't support
if ($isProviderVideo === false) {
// converts tag attributes to supported formats (listed below) to output correct html
// booleans: autoplay, controls, loop, muted
// strings : poster, preload
// for ex : `autoplay` will not work if `false` is a `string` instead of a `boolean`
$attrs['autoplay'] = $autoplay = Str::toType($tag->autoplay, 'bool');
$attrs['controls'] = Str::toType($tag->controls ?? true, 'bool');
$attrs['loop'] = Str::toType($tag->loop, 'bool');
$attrs['muted'] = Str::toType($tag->muted ?? $autoplay, 'bool');
$attrs['poster'] = $tag->poster;
$attrs['preload'] = $tag->preload;
}
// handles local and remote video file
if ($isLocalVideo === true) {
// handles local video file
if ($tag->file = $tag->file($tag->value)) {
$source = Html::tag('source', null, [
@ -315,32 +305,11 @@ return [
$video = Html::tag('video', [$source], $attrs);
}
} else {
// firstly handles supported video providers as youtube, vimeo, etc
try {
$video = Html::video(
$tag->value,
$options,
// providers only support width and height attributes
[
'height' => $tag->height,
'width' => $tag->width
]
);
} catch (Exception $e) {
// if not one of the supported video providers
// it checks if there is a valid remote video file
$extension = F::extension($tag->value);
$type = F::extensionToType($extension);
$mime = F::extensionToMime($extension);
if ($type === 'video') {
$source = Html::tag('source', null, [
'src' => $tag->value,
'type' => $mime
]);
$video = Html::tag('video', [$source], $attrs);
}
}
$video = Html::video(
$tag->value,
$tag->kirby()->option('kirbytext.video.options', []),
$attrs
);
}
return Html::figure([$video ?? ''], $tag->caption, [

View file

@ -1,8 +0,0 @@
<?php
// @codeCoverageIgnoreStart
return [
'emails/auth/login' => __DIR__ . '/templates/emails/auth/login.php',
'emails/auth/password-reset' => __DIR__ . '/templates/emails/auth/password-reset.php'
];
// @codeCoverageIgnoreEnd

View file

@ -1,8 +1,16 @@
<?php
use Kirby\Toolkit\I18n;
/**
* @var \Kirby\Cms\User $user
* @var string $site
* @var string $code
* @var int $timeout
*/
echo I18n::template('login.email.login.body', null, compact('user', 'code', 'timeout'), $user->language());
echo I18n::template(
'login.email.login.body',
null,
compact('user', 'site', 'code', 'timeout'),
$user->language()
);

View file

@ -1,8 +1,16 @@
<?php
use Kirby\Toolkit\I18n;
/**
* @var \Kirby\Cms\User $user
* @var string $site
* @var string $code
* @var int $timeout
*/
echo I18n::template('login.email.password-reset.body', null, compact('user', 'code', 'timeout'), $user->language());
echo I18n::template(
'login.email.password-reset.body',
null,
compact('user', 'site', 'code', 'timeout'),
$user->language()
);

View file

@ -1,33 +0,0 @@
<?php
use Kirby\Cms\Url;
return [
'index' => function () {
return Url::index();
},
'base' => function (array $urls) {
return rtrim($urls['index'], '/');
},
'current' => function (array $urls) {
$path = trim($this->path(), '/');
if (empty($path) === true) {
return $urls['index'];
} else {
return $urls['base'] . '/' . $path;
}
},
'assets' => function (array $urls) {
return $urls['base'] . '/assets';
},
'api' => function (array $urls) {
return $urls['base'] . '/' . ($this->options['api']['slug'] ?? 'api');
},
'media' => function (array $urls) {
return $urls['base'] . '/media';
},
'panel' => function (array $urls) {
return $urls['base'] . '/' . ($this->options['panel']['slug'] ?? 'panel');
}
];