first version
This commit is contained in:
65
kirby/config/aliases.php
Executable file
65
kirby/config/aliases.php
Executable file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
$aliases = [
|
||||
|
||||
// cms classes
|
||||
'collection' => 'Kirby\Cms\Collection',
|
||||
'dir' => 'Kirby\Cms\Dir',
|
||||
'field' => 'Kirby\Cms\Field',
|
||||
'file' => 'Kirby\Cms\File',
|
||||
'files' => 'Kirby\Cms\Files',
|
||||
'html' => 'Kirby\Cms\Html',
|
||||
'kirby' => 'Kirby\Cms\App',
|
||||
'page' => 'Kirby\Cms\Page',
|
||||
'pages' => 'Kirby\Cms\Pages',
|
||||
'pagination' => 'Kirby\Cms\Pagination',
|
||||
'r' => 'Kirby\Cms\R',
|
||||
'response' => 'Kirby\Cms\Response',
|
||||
's' => 'Kirby\Cms\S',
|
||||
'site' => 'Kirby\Cms\Site',
|
||||
'structure' => 'Kirby\Cms\Structure',
|
||||
'url' => 'Kirby\Cms\Url',
|
||||
'user' => 'Kirby\Cms\User',
|
||||
'users' => 'Kirby\Cms\Users',
|
||||
'visitor' => 'Kirby\Cms\Visitor',
|
||||
|
||||
// data handler
|
||||
'data' => 'Kirby\Data\Data',
|
||||
'json' => 'Kirby\Data\Json',
|
||||
'yaml' => 'Kirby\Data\Yaml',
|
||||
|
||||
// data classes
|
||||
'database' => 'Kirby\Database\Database',
|
||||
'db' => 'Kirby\Database\Db',
|
||||
|
||||
// http classes
|
||||
'cookie' => 'Kirby\Http\Cookie',
|
||||
'header' => 'Kirby\Http\Header',
|
||||
'remote' => 'Kirby\Http\Remote',
|
||||
'server' => 'Kirby\Http\Server',
|
||||
|
||||
// image classes
|
||||
'dimensions' => 'Kirby\Image\Dimensions',
|
||||
|
||||
// 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'
|
||||
];
|
||||
|
||||
spl_autoload_register(function ($class) use ($aliases) {
|
||||
$class = strtolower($class);
|
||||
|
||||
if (isset($aliases[$class]) === true) {
|
||||
class_alias($aliases[$class], $class);
|
||||
}
|
||||
});
|
25
kirby/config/api/authentication.php
Executable file
25
kirby/config/api/authentication.php
Executable file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\Auth;
|
||||
use Kirby\Exception\PermissionException;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
return function () {
|
||||
$auth = $this->kirby()->auth();
|
||||
|
||||
// csrf token check
|
||||
if ($auth->type() === 'session' && $auth->csrf() === false) {
|
||||
throw new PermissionException('Unauthenticated', 403);
|
||||
}
|
||||
|
||||
// get user from session or basic auth
|
||||
if ($user = $auth->user()) {
|
||||
if ($user->role()->permissions()->for('access', 'panel') === false) {
|
||||
throw new PermissionException(['key' => 'access.panel']);
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
throw new PermissionException('Unauthenticated', 403);
|
||||
};
|
80
kirby/config/api/collections.php
Executable file
80
kirby/config/api/collections.php
Executable file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\Files;
|
||||
use Kirby\Cms\Languages;
|
||||
use Kirby\Cms\Pages;
|
||||
use Kirby\Cms\Roles;
|
||||
use Kirby\Cms\Translations;
|
||||
use Kirby\Cms\Users;
|
||||
|
||||
/**
|
||||
* Api Collection Definitions
|
||||
*/
|
||||
return [
|
||||
|
||||
/**
|
||||
* Children
|
||||
*/
|
||||
'children' => [
|
||||
'model' => 'page',
|
||||
'type' => Pages::class,
|
||||
'view' => 'compact'
|
||||
],
|
||||
|
||||
/**
|
||||
* Files
|
||||
*/
|
||||
'files' => [
|
||||
'model' => 'file',
|
||||
'type' => Files::class
|
||||
],
|
||||
|
||||
/**
|
||||
* Languages
|
||||
*/
|
||||
'languages' => [
|
||||
'model' => 'language',
|
||||
'type' => Languages::class,
|
||||
'view' => 'compact'
|
||||
],
|
||||
|
||||
/**
|
||||
* Pages
|
||||
*/
|
||||
'pages' => [
|
||||
'model' => 'page',
|
||||
'type' => Pages::class,
|
||||
'view' => 'compact'
|
||||
],
|
||||
|
||||
/**
|
||||
* Roles
|
||||
*/
|
||||
'roles' => [
|
||||
'model' => 'role',
|
||||
'type' => Roles::class,
|
||||
'view' => 'compact'
|
||||
],
|
||||
|
||||
/**
|
||||
* Translations
|
||||
*/
|
||||
'translations' => [
|
||||
'model' => 'translation',
|
||||
'type' => Translations::class,
|
||||
'view' => 'compact'
|
||||
],
|
||||
|
||||
/**
|
||||
* Users
|
||||
*/
|
||||
'users' => [
|
||||
'default' => function () {
|
||||
return $this->users();
|
||||
},
|
||||
'model' => 'user',
|
||||
'type' => Users::class,
|
||||
'view' => 'compact'
|
||||
]
|
||||
|
||||
];
|
24
kirby/config/api/models.php
Executable file
24
kirby/config/api/models.php
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\File;
|
||||
use Kirby\Cms\Page;
|
||||
use Kirby\Cms\Site;
|
||||
|
||||
/**
|
||||
* Api Model Definitions
|
||||
*/
|
||||
return [
|
||||
'File' => include __DIR__ . '/models/File.php',
|
||||
'FileBlueprint' => include __DIR__ . '/models/FileBlueprint.php',
|
||||
'FileVersion' => include __DIR__ . '/models/FileVersion.php',
|
||||
'Language' => include __DIR__ . '/models/Language.php',
|
||||
'Page' => include __DIR__ . '/models/Page.php',
|
||||
'PageBlueprint' => include __DIR__ . '/models/PageBlueprint.php',
|
||||
'Role' => include __DIR__ . '/models/Role.php',
|
||||
'Site' => include __DIR__ . '/models/Site.php',
|
||||
'SiteBlueprint' => include __DIR__ . '/models/SiteBlueprint.php',
|
||||
'System' => include __DIR__ . '/models/System.php',
|
||||
'Translation' => include __DIR__ . '/models/Translation.php',
|
||||
'User' => include __DIR__ . '/models/User.php',
|
||||
'UserBlueprint' => include __DIR__ . '/models/UserBlueprint.php',
|
||||
];
|
160
kirby/config/api/models/File.php
Executable file
160
kirby/config/api/models/File.php
Executable file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\File;
|
||||
use Kirby\Cms\Form;
|
||||
|
||||
/**
|
||||
* File
|
||||
*/
|
||||
return [
|
||||
'fields' => [
|
||||
'blueprint' => function (File $file) {
|
||||
return $file->blueprint();
|
||||
},
|
||||
'content' => function (File $file) {
|
||||
return Form::for($file)->values();
|
||||
},
|
||||
'dimensions' => function (File $file) {
|
||||
return $file->dimensions()->toArray();
|
||||
},
|
||||
'exists' => function (File $file) {
|
||||
return $file->exists();
|
||||
},
|
||||
'extension' => function (File $file) {
|
||||
return $file->extension();
|
||||
},
|
||||
'filename' => function (File $file) {
|
||||
return $file->filename();
|
||||
},
|
||||
'id' => function (File $file) {
|
||||
return $file->id();
|
||||
},
|
||||
'link' => function (File $file) {
|
||||
return $file->panelUrl(true);
|
||||
},
|
||||
'mime' => function (File $file) {
|
||||
return $file->mime();
|
||||
},
|
||||
'modified' => function (File $file) {
|
||||
return $file->modified('c');
|
||||
},
|
||||
'name' => function (File $file) {
|
||||
return $file->name();
|
||||
},
|
||||
'next' => function (File $file) {
|
||||
return $file->next();
|
||||
},
|
||||
'nextWithTemplate' => function (File $file) {
|
||||
$files = $file->templateSiblings()->sortBy('sort', 'asc');
|
||||
$index = $files->indexOf($file);
|
||||
|
||||
return $files->nth($index + 1);
|
||||
},
|
||||
'options' => function (File $file) {
|
||||
return $file->permissions()->toArray();
|
||||
},
|
||||
'prev' => function (File $file) {
|
||||
return $file->prev();
|
||||
},
|
||||
'prevWithTemplate' => function (File $file) {
|
||||
$files = $file->templateSiblings()->sortBy('sort', 'asc');
|
||||
$index = $files->indexOf($file);
|
||||
|
||||
return $files->nth($index - 1);
|
||||
},
|
||||
'niceSize' => function (File $file) {
|
||||
return $file->niceSize();
|
||||
},
|
||||
'panelIcon' => function (File $file) {
|
||||
return $file->panelIcon();
|
||||
},
|
||||
'panelImage' => function (File $file) {
|
||||
return $file->panelImage();
|
||||
},
|
||||
'parent' => function (File $file) {
|
||||
return $file->parent();
|
||||
},
|
||||
'parents' => function (File $file) {
|
||||
return $file->parents()->flip();
|
||||
},
|
||||
'template' => function (File $file) {
|
||||
return $file->template();
|
||||
},
|
||||
'size' => function (File $file) {
|
||||
return $file->size();
|
||||
},
|
||||
'thumbs' => function ($file) {
|
||||
if ($file->isResizable() === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'tiny' => $file->resize(128)->url(),
|
||||
'small' => $file->resize(256)->url(),
|
||||
'medium' => $file->resize(512)->url(),
|
||||
'large' => $file->resize(768)->url(),
|
||||
'huge' => $file->resize(1024)->url(),
|
||||
];
|
||||
},
|
||||
'type' => function (File $file) {
|
||||
return $file->type();
|
||||
},
|
||||
'url' => function (File $file) {
|
||||
return $file->url(true);
|
||||
},
|
||||
],
|
||||
'type' => File::class,
|
||||
'views' => [
|
||||
'default' => [
|
||||
'content',
|
||||
'dimensions',
|
||||
'exists',
|
||||
'extension',
|
||||
'filename',
|
||||
'id',
|
||||
'link',
|
||||
'mime',
|
||||
'modified',
|
||||
'name',
|
||||
'next' => 'compact',
|
||||
'niceSize',
|
||||
'parent' => 'compact',
|
||||
'options',
|
||||
'prev' => 'compact',
|
||||
'size',
|
||||
'template',
|
||||
'type',
|
||||
'url'
|
||||
],
|
||||
'compact' => [
|
||||
'filename',
|
||||
'id',
|
||||
'link',
|
||||
'type',
|
||||
'url',
|
||||
],
|
||||
'panel' => [
|
||||
'blueprint',
|
||||
'content',
|
||||
'dimensions',
|
||||
'extension',
|
||||
'filename',
|
||||
'id',
|
||||
'link',
|
||||
'mime',
|
||||
'modified',
|
||||
'name',
|
||||
'nextWithTemplate' => 'compact',
|
||||
'niceSize',
|
||||
'options',
|
||||
'panelIcon',
|
||||
'panelImage',
|
||||
'parent' => 'compact',
|
||||
'parents' => ['id', 'slug', 'title'],
|
||||
'prevWithTemplate' => 'compact',
|
||||
'template',
|
||||
'type',
|
||||
'url'
|
||||
]
|
||||
],
|
||||
];
|
26
kirby/config/api/models/FileBlueprint.php
Executable file
26
kirby/config/api/models/FileBlueprint.php
Executable file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\FileBlueprint;
|
||||
|
||||
/**
|
||||
* FileBlueprint
|
||||
*/
|
||||
return [
|
||||
'fields' => [
|
||||
'name' => function (FileBlueprint $blueprint) {
|
||||
return $blueprint->name();
|
||||
},
|
||||
'options' => function (FileBlueprint $blueprint) {
|
||||
return $blueprint->options();
|
||||
},
|
||||
'tabs' => function (FileBlueprint $blueprint) {
|
||||
return $blueprint->tabs();
|
||||
},
|
||||
'title' => function (FileBlueprint $blueprint) {
|
||||
return $blueprint->title();
|
||||
},
|
||||
],
|
||||
'type' => FileBlueprint::class,
|
||||
'views' => [
|
||||
],
|
||||
];
|
83
kirby/config/api/models/FileVersion.php
Executable file
83
kirby/config/api/models/FileVersion.php
Executable file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\FileVersion;
|
||||
|
||||
/**
|
||||
* FileVersion
|
||||
*/
|
||||
return [
|
||||
'fields' => [
|
||||
'dimensions' => function (FileVersion $file) {
|
||||
return $file->dimensions()->toArray();
|
||||
},
|
||||
'exists' => function (FileVersion $file) {
|
||||
return $file->exists();
|
||||
},
|
||||
'extension' => function (FileVersion $file) {
|
||||
return $file->extension();
|
||||
},
|
||||
'filename' => function (FileVersion $file) {
|
||||
return $file->filename();
|
||||
},
|
||||
'id' => function (FileVersion $file) {
|
||||
return $file->id();
|
||||
},
|
||||
'mime' => function (FileVersion $file) {
|
||||
return $file->mime();
|
||||
},
|
||||
'modified' => function (FileVersion $file) {
|
||||
return $file->modified('c');
|
||||
},
|
||||
'name' => function (FileVersion $file) {
|
||||
return $file->name();
|
||||
},
|
||||
'niceSize' => function (FileVersion $file) {
|
||||
return $file->niceSize();
|
||||
},
|
||||
'size' => function (FileVersion $file) {
|
||||
return $file->size();
|
||||
},
|
||||
'type' => function (FileVersion $file) {
|
||||
return $file->type();
|
||||
},
|
||||
'url' => function (FileVersion $file) {
|
||||
return $file->url(true);
|
||||
},
|
||||
],
|
||||
'type' => FileVersion::class,
|
||||
'views' => [
|
||||
'default' => [
|
||||
'dimensions',
|
||||
'exists',
|
||||
'extension',
|
||||
'filename',
|
||||
'id',
|
||||
'mime',
|
||||
'modified',
|
||||
'name',
|
||||
'niceSize',
|
||||
'size',
|
||||
'type',
|
||||
'url'
|
||||
],
|
||||
'compact' => [
|
||||
'filename',
|
||||
'id',
|
||||
'type',
|
||||
'url',
|
||||
],
|
||||
'panel' => [
|
||||
'dimensions',
|
||||
'extension',
|
||||
'filename',
|
||||
'id',
|
||||
'mime',
|
||||
'modified',
|
||||
'name',
|
||||
'niceSize',
|
||||
'template',
|
||||
'type',
|
||||
'url'
|
||||
]
|
||||
],
|
||||
];
|
37
kirby/config/api/models/Language.php
Executable file
37
kirby/config/api/models/Language.php
Executable file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\Language;
|
||||
|
||||
/**
|
||||
* Language
|
||||
*/
|
||||
return [
|
||||
'fields' => [
|
||||
'code' => function (Language $language) {
|
||||
return $language->code();
|
||||
},
|
||||
'default' => function (Language $language) {
|
||||
return $language->isDefault();
|
||||
},
|
||||
'direction' => function (Language $language) {
|
||||
return $language->direction();
|
||||
},
|
||||
'locale' => function (Language $language) {
|
||||
return $language->locale();
|
||||
},
|
||||
'name' => function (Language $language) {
|
||||
return $language->name();
|
||||
},
|
||||
'url' => function (Language $language) {
|
||||
return $language->url();
|
||||
},
|
||||
],
|
||||
'type' => Language::class,
|
||||
'views' => [
|
||||
'compact' => [
|
||||
'code',
|
||||
'default',
|
||||
'name',
|
||||
]
|
||||
]
|
||||
];
|
155
kirby/config/api/models/Page.php
Executable file
155
kirby/config/api/models/Page.php
Executable file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\Form;
|
||||
use Kirby\Cms\Page;
|
||||
|
||||
/**
|
||||
* Page
|
||||
*/
|
||||
return [
|
||||
'fields' => [
|
||||
'blueprint' => function (Page $page) {
|
||||
return $page->blueprint();
|
||||
},
|
||||
'blueprints' => function (Page $page) {
|
||||
return $page->blueprints();
|
||||
},
|
||||
'children' => function (Page $page) {
|
||||
return $page->children();
|
||||
},
|
||||
'content' => function (Page $page) {
|
||||
return Form::for($page)->values();
|
||||
},
|
||||
'drafts' => function (Page $page) {
|
||||
return $page->drafts();
|
||||
},
|
||||
'errors' => function (Page $page) {
|
||||
return $page->errors();
|
||||
},
|
||||
'files' => function (Page $page) {
|
||||
return $page->files();
|
||||
},
|
||||
'hasChildren' => function (Page $page) {
|
||||
return $page->hasChildren();
|
||||
},
|
||||
'hasDrafts' => function (Page $page) {
|
||||
return $page->hasDrafts();
|
||||
},
|
||||
'id' => function (Page $page) {
|
||||
return $page->id();
|
||||
},
|
||||
'isSortable' => function (Page $page) {
|
||||
return $page->isSortable();
|
||||
},
|
||||
'next' => function (Page $page) {
|
||||
return $page
|
||||
->nextAll()
|
||||
->filterBy('intendedTemplate', $page->intendedTemplate())
|
||||
->filterBy('status', $page->status())
|
||||
->filterBy('isReadable', true)
|
||||
->first();
|
||||
},
|
||||
'num' => function (Page $page) {
|
||||
return $page->num();
|
||||
},
|
||||
'options' => function (Page $page) {
|
||||
return $page->permissions()->toArray();
|
||||
},
|
||||
'panelIcon' => function (Page $page) {
|
||||
return $page->panelIcon();
|
||||
},
|
||||
'panelImage' => function (Page $page) {
|
||||
return $page->panelImage();
|
||||
},
|
||||
'parent' => function (Page $page) {
|
||||
return $page->parent();
|
||||
},
|
||||
'parents' => function (Page $page) {
|
||||
return $page->parents()->flip();
|
||||
},
|
||||
'prev' => function (Page $page) {
|
||||
return $page
|
||||
->prevAll()
|
||||
->filterBy('intendedTemplate', $page->intendedTemplate())
|
||||
->filterBy('status', $page->status())
|
||||
->filterBy('isReadable', true)
|
||||
->last();
|
||||
},
|
||||
'previewUrl' => function (Page $page) {
|
||||
return $page->previewUrl();
|
||||
},
|
||||
'siblings' => function (Page $page) {
|
||||
if ($page->isDraft() === true) {
|
||||
return $page->parentModel()->children()->not($page);
|
||||
} else {
|
||||
return $page->siblings();
|
||||
}
|
||||
},
|
||||
'slug' => function (Page $page) {
|
||||
return $page->slug();
|
||||
},
|
||||
'status' => function (Page $page) {
|
||||
return $page->status();
|
||||
},
|
||||
'template' => function (Page $page) {
|
||||
return $page->intendedTemplate()->name();
|
||||
},
|
||||
'title' => function (Page $page) {
|
||||
return $page->title()->value();
|
||||
},
|
||||
'url' => function (Page $page) {
|
||||
return $page->url();
|
||||
},
|
||||
],
|
||||
'type' => Page::class,
|
||||
'views' => [
|
||||
'compact' => [
|
||||
'id',
|
||||
'title',
|
||||
'url',
|
||||
'num'
|
||||
],
|
||||
'default' => [
|
||||
'content',
|
||||
'id',
|
||||
'status',
|
||||
'num',
|
||||
'options',
|
||||
'parent' => 'compact',
|
||||
'slug',
|
||||
'template',
|
||||
'title',
|
||||
'url'
|
||||
],
|
||||
'panel' => [
|
||||
'id',
|
||||
'blueprint',
|
||||
'content',
|
||||
'errors',
|
||||
'status',
|
||||
'options',
|
||||
'next' => ['id', 'slug', 'title'],
|
||||
'parents' => ['id', 'slug', 'title'],
|
||||
'prev' => ['id', 'slug', 'title'],
|
||||
'previewUrl',
|
||||
'slug',
|
||||
'title',
|
||||
'url'
|
||||
],
|
||||
'selector' => [
|
||||
'id',
|
||||
'title',
|
||||
'parent' => [
|
||||
'id',
|
||||
'title'
|
||||
],
|
||||
'children' => [
|
||||
'hasChildren',
|
||||
'id',
|
||||
'panelIcon',
|
||||
'panelImage',
|
||||
'title',
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
35
kirby/config/api/models/PageBlueprint.php
Executable file
35
kirby/config/api/models/PageBlueprint.php
Executable file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\PageBlueprint;
|
||||
|
||||
/**
|
||||
* PageBlueprint
|
||||
*/
|
||||
return [
|
||||
'fields' => [
|
||||
'name' => function (PageBlueprint $blueprint) {
|
||||
return $blueprint->name();
|
||||
},
|
||||
'num' => function (PageBlueprint $blueprint) {
|
||||
return $blueprint->num();
|
||||
},
|
||||
'options' => function (PageBlueprint $blueprint) {
|
||||
return $blueprint->options();
|
||||
},
|
||||
'preview' => function (PageBlueprint $blueprint) {
|
||||
return $blueprint->preview();
|
||||
},
|
||||
'status' => function (PageBlueprint $blueprint) {
|
||||
return $blueprint->status();
|
||||
},
|
||||
'tabs' => function (PageBlueprint $blueprint) {
|
||||
return $blueprint->tabs();
|
||||
},
|
||||
'title' => function (PageBlueprint $blueprint) {
|
||||
return $blueprint->title();
|
||||
},
|
||||
],
|
||||
'type' => PageBlueprint::class,
|
||||
'views' => [
|
||||
],
|
||||
];
|
31
kirby/config/api/models/Role.php
Executable file
31
kirby/config/api/models/Role.php
Executable file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\Role;
|
||||
|
||||
/**
|
||||
* Role
|
||||
*/
|
||||
return [
|
||||
'fields' => [
|
||||
'description' => function (Role $role) {
|
||||
return $role->description();
|
||||
},
|
||||
'name' => function (Role $role) {
|
||||
return $role->name();
|
||||
},
|
||||
'permissions' => function (Role $role) {
|
||||
return $role->permissions()->toArray();
|
||||
},
|
||||
'title' => function (Role $role) {
|
||||
return $role->title();
|
||||
},
|
||||
],
|
||||
'type' => Role::class,
|
||||
'views' => [
|
||||
'compact' => [
|
||||
'description',
|
||||
'name',
|
||||
'title'
|
||||
]
|
||||
]
|
||||
];
|
65
kirby/config/api/models/Site.php
Executable file
65
kirby/config/api/models/Site.php
Executable file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\Form;
|
||||
use Kirby\Cms\Site;
|
||||
|
||||
/**
|
||||
* Site
|
||||
*/
|
||||
return [
|
||||
'default' => function () {
|
||||
return $this->site();
|
||||
},
|
||||
'fields' => [
|
||||
'blueprint' => function (Site $site) {
|
||||
return $site->blueprint();
|
||||
},
|
||||
'children' => function (Site $site) {
|
||||
return $site->children();
|
||||
},
|
||||
'content' => function (Site $site) {
|
||||
return Form::for($site)->values();
|
||||
},
|
||||
'files' => function (Site $site) {
|
||||
return $site->files();
|
||||
},
|
||||
'options' => function (Site $site) {
|
||||
return $site->permissions()->toArray();
|
||||
},
|
||||
'title' => function (Site $site) {
|
||||
return $site->title()->value();
|
||||
},
|
||||
'url' => function (Site $site) {
|
||||
return $site->url();
|
||||
},
|
||||
],
|
||||
'type' => Site::class,
|
||||
'views' => [
|
||||
'compact' => [
|
||||
'title',
|
||||
'url'
|
||||
],
|
||||
'default' => [
|
||||
'content',
|
||||
'options',
|
||||
'title',
|
||||
'url'
|
||||
],
|
||||
'panel' => [
|
||||
'title',
|
||||
'blueprint',
|
||||
'content',
|
||||
'options',
|
||||
'url'
|
||||
],
|
||||
'selector' => [
|
||||
'title',
|
||||
'children' => [
|
||||
'id',
|
||||
'title',
|
||||
'panelIcon',
|
||||
'hasChildren'
|
||||
],
|
||||
]
|
||||
]
|
||||
];
|
26
kirby/config/api/models/SiteBlueprint.php
Executable file
26
kirby/config/api/models/SiteBlueprint.php
Executable file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\SiteBlueprint;
|
||||
|
||||
/**
|
||||
* SiteBlueprint
|
||||
*/
|
||||
return [
|
||||
'fields' => [
|
||||
'name' => function (SiteBlueprint $blueprint) {
|
||||
return $blueprint->name();
|
||||
},
|
||||
'options' => function (SiteBlueprint $blueprint) {
|
||||
return $blueprint->options();
|
||||
},
|
||||
'tabs' => function (SiteBlueprint $blueprint) {
|
||||
return $blueprint->tabs();
|
||||
},
|
||||
'title' => function (SiteBlueprint $blueprint) {
|
||||
return $blueprint->title();
|
||||
},
|
||||
],
|
||||
'type' => SiteBlueprint::class,
|
||||
'views' => [
|
||||
],
|
||||
];
|
95
kirby/config/api/models/System.php
Executable file
95
kirby/config/api/models/System.php
Executable file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\System;
|
||||
|
||||
/**
|
||||
* System
|
||||
*/
|
||||
return [
|
||||
'fields' => [
|
||||
'isOk' => function (System $system) {
|
||||
return $system->isOk();
|
||||
},
|
||||
'isInstallable' => function (System $system) {
|
||||
return $system->isInstallable();
|
||||
},
|
||||
'isInstalled' => function (System $system) {
|
||||
return $system->isInstalled();
|
||||
},
|
||||
'isLocal' => function (System $system) {
|
||||
return $system->isLocal();
|
||||
},
|
||||
'multilang' => function () {
|
||||
return $this->kirby()->option('languages', false) !== false;
|
||||
},
|
||||
'languages' => function () {
|
||||
return $this->kirby()->languages();
|
||||
},
|
||||
'license' => function (System $system) {
|
||||
return $system->license();
|
||||
},
|
||||
'requirements' => function (System $system) {
|
||||
return $system->toArray();
|
||||
},
|
||||
'breadcrumbTitle' => function () {
|
||||
return $this->site()->blueprint()->title();
|
||||
},
|
||||
'title' => function () {
|
||||
return $this->site()->title()->value();
|
||||
},
|
||||
'translation' => function () {
|
||||
if ($user = $this->user()) {
|
||||
$translationCode = $user->language();
|
||||
} else {
|
||||
$translationCode = $this->kirby()->option('panel.language', 'en');
|
||||
}
|
||||
|
||||
if ($translation = $this->kirby()->translation($translationCode)) {
|
||||
return $translation;
|
||||
} else {
|
||||
return $this->kirby()->translation('en');
|
||||
}
|
||||
},
|
||||
'kirbytext' => function () {
|
||||
return $this->kirby()->option('panel')['kirbytext'] ?? true;
|
||||
},
|
||||
'user' => function () {
|
||||
return $this->user();
|
||||
},
|
||||
'version' => function () {
|
||||
return $this->kirby()->version();
|
||||
}
|
||||
],
|
||||
'type' => System::class,
|
||||
'views' => [
|
||||
'login' => [
|
||||
'isOk',
|
||||
'isInstalled',
|
||||
'title',
|
||||
'translation'
|
||||
],
|
||||
'troubleshooting' => [
|
||||
'isOk',
|
||||
'isInstallable',
|
||||
'isInstalled',
|
||||
'title',
|
||||
'translation',
|
||||
'requirements'
|
||||
],
|
||||
'panel' => [
|
||||
'breadcrumbTitle',
|
||||
'isOk',
|
||||
'isInstalled',
|
||||
'isLocal',
|
||||
'kirbytext',
|
||||
'languages' => 'compact',
|
||||
'license',
|
||||
'multilang',
|
||||
'requirements',
|
||||
'title',
|
||||
'translation',
|
||||
'user' => 'auth',
|
||||
'version'
|
||||
]
|
||||
],
|
||||
];
|
34
kirby/config/api/models/Translation.php
Executable file
34
kirby/config/api/models/Translation.php
Executable file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\Translation;
|
||||
|
||||
/**
|
||||
* Translation
|
||||
*/
|
||||
return [
|
||||
'fields' => [
|
||||
'author' => function (Translation $translation) {
|
||||
return $translation->author();
|
||||
},
|
||||
'data' => function (Translation $translation) {
|
||||
return $translation->dataWithFallback();
|
||||
},
|
||||
'direction' => function (Translation $translation) {
|
||||
return $translation->direction();
|
||||
},
|
||||
'id' => function (Translation $translation) {
|
||||
return $translation->id();
|
||||
},
|
||||
'name' => function (Translation $translation) {
|
||||
return $translation->name();
|
||||
},
|
||||
],
|
||||
'type' => Translation::class,
|
||||
'views' => [
|
||||
'compact' => [
|
||||
'direction',
|
||||
'id',
|
||||
'name'
|
||||
]
|
||||
]
|
||||
];
|
102
kirby/config/api/models/User.php
Executable file
102
kirby/config/api/models/User.php
Executable file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\Form;
|
||||
use Kirby\Cms\User;
|
||||
|
||||
/**
|
||||
* User
|
||||
*/
|
||||
return [
|
||||
'default' => function () {
|
||||
return $this->user();
|
||||
},
|
||||
'fields' => [
|
||||
'avatar' => function (User $user) {
|
||||
return $user->avatar() ? $user->avatar()->crop(512) : null;
|
||||
},
|
||||
'blueprint' => function (User $user) {
|
||||
return $user->blueprint();
|
||||
},
|
||||
'content' => function (User $user) {
|
||||
return Form::for($user)->values();
|
||||
},
|
||||
'email' => function (User $user) {
|
||||
return $user->email();
|
||||
},
|
||||
'id' => function (User $user) {
|
||||
return $user->id();
|
||||
},
|
||||
'language' => function (User $user) {
|
||||
return $user->language();
|
||||
},
|
||||
'name' => function (User $user) {
|
||||
return $user->name()->value();
|
||||
},
|
||||
'next' => function (User $user) {
|
||||
return $user->next();
|
||||
},
|
||||
'options' => function (User $user) {
|
||||
return $user->permissions()->toArray();
|
||||
},
|
||||
'permissions' => function (User $user) {
|
||||
return $user->role()->permissions()->toArray();
|
||||
},
|
||||
'prev' => function (User $user) {
|
||||
return $user->prev();
|
||||
},
|
||||
'role' => function (User $user) {
|
||||
return $user->role();
|
||||
},
|
||||
'username' => function (User $user) {
|
||||
return $user->username();
|
||||
}
|
||||
],
|
||||
'type' => User::class,
|
||||
'views' => [
|
||||
'default' => [
|
||||
'avatar',
|
||||
'content',
|
||||
'email',
|
||||
'id',
|
||||
'language',
|
||||
'name',
|
||||
'next' => 'compact',
|
||||
'options',
|
||||
'prev' => 'compact',
|
||||
'role',
|
||||
'username'
|
||||
],
|
||||
'compact' => [
|
||||
'avatar' => 'compact',
|
||||
'id',
|
||||
'email',
|
||||
'language',
|
||||
'name',
|
||||
'role' => 'compact',
|
||||
'username'
|
||||
],
|
||||
'auth' => [
|
||||
'avatar' => 'compact',
|
||||
'permissions',
|
||||
'email',
|
||||
'id',
|
||||
'name',
|
||||
'role',
|
||||
'language'
|
||||
],
|
||||
'panel' => [
|
||||
'avatar' => 'compact',
|
||||
'blueprint',
|
||||
'content',
|
||||
'email',
|
||||
'id',
|
||||
'language',
|
||||
'name',
|
||||
'next' => ['id', 'name'],
|
||||
'options',
|
||||
'prev' => ['id', 'name'],
|
||||
'role',
|
||||
'username',
|
||||
],
|
||||
]
|
||||
];
|
26
kirby/config/api/models/UserBlueprint.php
Executable file
26
kirby/config/api/models/UserBlueprint.php
Executable file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\UserBlueprint;
|
||||
|
||||
/**
|
||||
* UserBlueprint
|
||||
*/
|
||||
return [
|
||||
'fields' => [
|
||||
'name' => function (UserBlueprint $blueprint) {
|
||||
return $blueprint->name();
|
||||
},
|
||||
'options' => function (UserBlueprint $blueprint) {
|
||||
return $blueprint->options();
|
||||
},
|
||||
'tabs' => function (UserBlueprint $blueprint) {
|
||||
return $blueprint->tabs();
|
||||
},
|
||||
'title' => function (UserBlueprint $blueprint) {
|
||||
return $blueprint->title();
|
||||
},
|
||||
],
|
||||
'type' => UserBlueprint::class,
|
||||
'views' => [
|
||||
],
|
||||
];
|
25
kirby/config/api/routes.php
Executable file
25
kirby/config/api/routes.php
Executable file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Api Routes Definitions
|
||||
*/
|
||||
return function ($kirby) {
|
||||
$routes = array_merge(
|
||||
include __DIR__ . '/routes/auth.php',
|
||||
include __DIR__ . '/routes/pages.php',
|
||||
include __DIR__ . '/routes/roles.php',
|
||||
include __DIR__ . '/routes/site.php',
|
||||
include __DIR__ . '/routes/users.php',
|
||||
include __DIR__ . '/routes/files.php',
|
||||
include __DIR__ . '/routes/system.php',
|
||||
include __DIR__ . '/routes/translations.php'
|
||||
);
|
||||
|
||||
// only add the language routes if the
|
||||
// multi language setup is activated
|
||||
if ($kirby->option('languages', false) !== false) {
|
||||
$routes = array_merge($routes, include __DIR__ . '/routes/languages.php');
|
||||
}
|
||||
|
||||
return $routes;
|
||||
};
|
58
kirby/config/api/routes/auth.php
Executable file
58
kirby/config/api/routes/auth.php
Executable file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Exception\NotFoundException;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Exception\PermissionException;
|
||||
|
||||
/**
|
||||
* Authentication
|
||||
*/
|
||||
return [
|
||||
[
|
||||
'pattern' => 'auth',
|
||||
'method' => 'GET',
|
||||
'action' => function () {
|
||||
if ($user = $this->kirby()->auth()->user()) {
|
||||
return $this->resolve($user)->view('auth');
|
||||
}
|
||||
|
||||
throw new NotFoundException('The user cannot be found');
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'auth/login',
|
||||
'method' => 'POST',
|
||||
'auth' => false,
|
||||
'action' => function () {
|
||||
$auth = $this->kirby()->auth();
|
||||
|
||||
// csrf token check
|
||||
if ($auth->type() === 'session' && $auth->csrf() === false) {
|
||||
throw new InvalidArgumentException('Invalid CSRF token');
|
||||
}
|
||||
|
||||
$email = $this->requestBody('email');
|
||||
$long = $this->requestBody('long');
|
||||
$password = $this->requestBody('password');
|
||||
|
||||
if ($user = $this->kirby()->auth()->login($email, $password, $long)) {
|
||||
return [
|
||||
'code' => 200,
|
||||
'status' => 'ok',
|
||||
'user' => $this->resolve($user)->view('auth')->toArray()
|
||||
];
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('Invalid email or password');
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'auth/logout',
|
||||
'method' => 'POST',
|
||||
'auth' => false,
|
||||
'action' => function () {
|
||||
$this->kirby()->auth()->logout();
|
||||
return true;
|
||||
}
|
||||
],
|
||||
];
|
100
kirby/config/api/routes/files.php
Executable file
100
kirby/config/api/routes/files.php
Executable file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Files Routes
|
||||
*/
|
||||
return [
|
||||
|
||||
[
|
||||
'pattern' => '(:all)/files',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $path) {
|
||||
return $this->parent($path)->files();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/files',
|
||||
'method' => 'POST',
|
||||
'action' => function (string $path) {
|
||||
return $this->upload(function ($source, $filename) use ($path) {
|
||||
return $this->parent($path)->createFile([
|
||||
'source' => $source,
|
||||
'template' => $this->requestBody('template'),
|
||||
'filename' => $filename
|
||||
]);
|
||||
});
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/files/search',
|
||||
'method' => 'POST',
|
||||
'action' => function (string $path) {
|
||||
return $this->parent($path)->files()->query($this->requestBody());
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/files/sort',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $path) {
|
||||
return $this->parent($path)->files()->changeSort($this->requestBody('files'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/files/(:any)',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $path, string $filename) {
|
||||
return $this->file($path, $filename);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/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)',
|
||||
'method' => 'POST',
|
||||
'action' => function (string $path, string $filename) {
|
||||
return $this->upload(function ($source) use ($path, $filename) {
|
||||
return $this->file($path, $filename)->replace($source);
|
||||
});
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/files/(:any)',
|
||||
'method' => 'DELETE',
|
||||
'action' => function (string $path, string $filename) {
|
||||
return $this->file($path, $filename)->delete();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/files/(:any)/name',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $path, string $filename) {
|
||||
return $this->file($path, $filename)->changeName($this->requestBody('name'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/files/(:any)/sections/(:any)',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $path, string $filename, string $sectionName) {
|
||||
if ($section = $this->file($path, $filename)->blueprint()->section($sectionName)) {
|
||||
return $section->toResponse();
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/files/(:any)/fields/(:any)/(:all?)',
|
||||
'method' => 'ALL',
|
||||
'action' => function (string $parent, string $filename, string $fieldName, string $path = null) {
|
||||
if ($file = $this->file($parent, $filename)) {
|
||||
return $this->fieldApi($file, $fieldName, $path);
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
];
|
46
kirby/config/api/routes/languages.php
Executable file
46
kirby/config/api/routes/languages.php
Executable file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Roles Routes
|
||||
*/
|
||||
return [
|
||||
[
|
||||
'pattern' => 'languages',
|
||||
'method' => 'GET',
|
||||
'action' => function () {
|
||||
return $this->kirby()->languages();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'languages',
|
||||
'method' => 'POST',
|
||||
'action' => function () {
|
||||
return $this->kirby()->languages()->create($this->requestBody());
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'languages/(:any)',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $code) {
|
||||
return $this->kirby()->languages()->find($code);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'languages/(:any)',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $code) {
|
||||
if ($language = $this->kirby()->languages()->find($code)) {
|
||||
return $language->update($this->requestBody());
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'languages/(:any)',
|
||||
'method' => 'DELETE',
|
||||
'action' => function (string $code) {
|
||||
if ($language = $this->kirby()->languages()->find($code)) {
|
||||
return $language->delete();
|
||||
}
|
||||
}
|
||||
]
|
||||
];
|
105
kirby/config/api/routes/pages.php
Executable file
105
kirby/config/api/routes/pages.php
Executable file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Page Routes
|
||||
*/
|
||||
return [
|
||||
|
||||
[
|
||||
'pattern' => 'pages/(:any)',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $id) {
|
||||
return $this->page($id);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'pages/(:any)',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $id) {
|
||||
return $this->page($id)->update($this->requestBody(), $this->language(), true);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'pages/(:any)',
|
||||
'method' => 'DELETE',
|
||||
'action' => function (string $id) {
|
||||
return $this->page($id)->delete($this->requestBody('force', false));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'pages/(:any)/children',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $id) {
|
||||
return $this->page($id)->children();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'pages/(:any)/children',
|
||||
'method' => 'POST',
|
||||
'action' => function (string $id) {
|
||||
return $this->page($id)->createChild($this->requestBody());
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'pages/(:any)/children/blueprints',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $id) {
|
||||
return $this->page($id)->blueprints($this->requestQuery('section'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'pages/(:any)/children/search',
|
||||
'method' => 'POST',
|
||||
'action' => function (string $id) {
|
||||
return $this->page($id)->children()->query($this->requestBody());
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'pages/(:any)/slug',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $id) {
|
||||
return $this->page($id)->changeSlug($this->requestBody('slug'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'pages/(:any)/status',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $id) {
|
||||
return $this->page($id)->changeStatus($this->requestBody('status'), $this->requestBody('position'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'pages/(:any)/template',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $id) {
|
||||
return $this->page($id)->changeTemplate($this->requestBody('template'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'pages/(:any)/title',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $id) {
|
||||
return $this->page($id)->changeTitle($this->requestBody('title'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'pages/(:any)/sections/(:any)',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $id, string $sectionName) {
|
||||
if ($section = $this->page($id)->blueprint()->section($sectionName)) {
|
||||
return $section->toResponse();
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'pages/(:any)/fields/(:any)/(:all?)',
|
||||
'method' => 'ALL',
|
||||
'action' => function (string $id, string $fieldName, string $path = null) {
|
||||
if ($page = $this->page($id)) {
|
||||
return $this->fieldApi($page, $fieldName, $path);
|
||||
}
|
||||
}
|
||||
],
|
||||
];
|
21
kirby/config/api/routes/roles.php
Executable file
21
kirby/config/api/routes/roles.php
Executable file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Roles Routes
|
||||
*/
|
||||
return [
|
||||
[
|
||||
'pattern' => 'roles',
|
||||
'method' => 'GET',
|
||||
'action' => function () {
|
||||
return $this->kirby()->roles();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'roles/(:any)',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $name) {
|
||||
return $this->kirby()->roles()->find($name);
|
||||
}
|
||||
]
|
||||
];
|
95
kirby/config/api/routes/site.php
Executable file
95
kirby/config/api/routes/site.php
Executable file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Site Routes
|
||||
*/
|
||||
return [
|
||||
|
||||
[
|
||||
'pattern' => 'site',
|
||||
'action' => function () {
|
||||
return $this->site();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'site',
|
||||
'method' => 'PATCH',
|
||||
'action' => function () {
|
||||
return $this->site()->update($this->requestBody(), $this->language(), true);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'site/children',
|
||||
'method' => 'GET',
|
||||
'action' => function () {
|
||||
return $this->site()->children();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'site/children',
|
||||
'method' => 'POST',
|
||||
'action' => function () {
|
||||
return $this->site()->createChild($this->requestBody());
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'site/children/blueprints',
|
||||
'method' => 'GET',
|
||||
'action' => function () {
|
||||
return $this->site()->blueprints($this->requestQuery('section'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'site/children/search',
|
||||
'method' => 'POST',
|
||||
'action' => function () {
|
||||
return $this->site()->children()->query($this->requestBody());
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'site/find',
|
||||
'method' => 'POST',
|
||||
'action' => function () {
|
||||
return $this->site()->find(false, ...$this->requestBody());
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'site/title',
|
||||
'method' => 'PATCH',
|
||||
'action' => function () {
|
||||
return $this->site()->changeTitle($this->requestBody('title'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'site/search',
|
||||
'method' => 'GET',
|
||||
'action' => function () {
|
||||
return $this->site()
|
||||
->index(true)
|
||||
->filterBy('isReadable', true)
|
||||
->search($this->requestQuery('q'), [
|
||||
'score' => [
|
||||
'id' => 64,
|
||||
'title' => 64,
|
||||
]
|
||||
]);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'site/sections/(:any)',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $sectionName) {
|
||||
if ($section = $this->site()->blueprint()->section($sectionName)) {
|
||||
return $section->toResponse();
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'site/fields/(:any)/(:all?)',
|
||||
'method' => 'ALL',
|
||||
'action' => function (string $fieldName, string $path = null) {
|
||||
return $this->fieldApi($this->site(), $fieldName, $path);
|
||||
}
|
||||
]
|
||||
|
||||
];
|
72
kirby/config/api/routes/system.php
Executable file
72
kirby/config/api/routes/system.php
Executable file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* System Routes
|
||||
*/
|
||||
return [
|
||||
|
||||
[
|
||||
'pattern' => 'system',
|
||||
'method' => 'GET',
|
||||
'auth' => false,
|
||||
'action' => function () {
|
||||
$system = $this->kirby()->system();
|
||||
|
||||
if ($this->kirby()->user()) {
|
||||
return $system;
|
||||
} else {
|
||||
if ($system->isOk() === true) {
|
||||
$info = $this->resolve($system)->view('login')->toArray();
|
||||
} else {
|
||||
$info = $this->resolve($system)->view('troubleshooting')->toArray();
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => 'ok',
|
||||
'data' => $info,
|
||||
'type' => 'model'
|
||||
];
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'system/register',
|
||||
'method' => 'POST',
|
||||
'action' => function () {
|
||||
return $this->kirby()->system()->register($this->requestBody('license'), $this->requestBody('email'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'system/install',
|
||||
'method' => 'POST',
|
||||
'auth' => false,
|
||||
'action' => function () {
|
||||
$system = $this->kirby()->system();
|
||||
$auth = $this->kirby()->auth();
|
||||
|
||||
// csrf token check
|
||||
if ($auth->type() === 'session' && $auth->csrf() === false) {
|
||||
throw new InvalidArgumentException('Invalid CSRF token');
|
||||
}
|
||||
|
||||
if ($system->isOk() === false) {
|
||||
throw new Exception('The server is not setup correctly');
|
||||
}
|
||||
|
||||
if ($system->isInstalled() === true) {
|
||||
throw new Exception('The panel is already installed');
|
||||
}
|
||||
|
||||
// create the first user
|
||||
$user = $this->users()->create($this->requestBody());
|
||||
$token = $user->login($this->requestBody('password'));
|
||||
|
||||
return [
|
||||
'status' => 'ok',
|
||||
'token' => $token,
|
||||
'user' => $this->resolve($user)->view('auth')->toArray()
|
||||
];
|
||||
}
|
||||
]
|
||||
|
||||
];
|
24
kirby/config/api/routes/translations.php
Executable file
24
kirby/config/api/routes/translations.php
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Translations Routes
|
||||
*/
|
||||
return [
|
||||
[
|
||||
'pattern' => 'translations',
|
||||
'method' => 'GET',
|
||||
'auth' => false,
|
||||
'action' => function () {
|
||||
return $this->kirby()->translations();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'translations/(:any)',
|
||||
'method' => 'GET',
|
||||
'auth' => false,
|
||||
'action' => function (string $code) {
|
||||
return $this->kirby()->translations()->find($code);
|
||||
}
|
||||
]
|
||||
|
||||
];
|
137
kirby/config/api/routes/users.php
Executable file
137
kirby/config/api/routes/users.php
Executable file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Toolkit\F;
|
||||
|
||||
/**
|
||||
* User Routes
|
||||
*/
|
||||
return [
|
||||
|
||||
[
|
||||
'pattern' => 'users',
|
||||
'method' => 'GET',
|
||||
'action' => function () {
|
||||
return $this->users();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users',
|
||||
'method' => 'POST',
|
||||
'action' => function () {
|
||||
return $this->users()->create($this->requestBody());
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users/search',
|
||||
'method' => 'POST',
|
||||
'action' => function () {
|
||||
return $this->users()->query($this->requestBody());
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users/(:any)',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $id) {
|
||||
return $this->user($id);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users/(:any)',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $id) {
|
||||
return $this->user($id)->update($this->requestBody(), $this->language(), true);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users/(:any)',
|
||||
'method' => 'DELETE',
|
||||
'action' => function (string $id) {
|
||||
return $this->user($id)->delete();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users/(:any)/avatar',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $id) {
|
||||
return $this->user($id)->avatar();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users/(:any)/avatar',
|
||||
'method' => 'POST',
|
||||
'action' => function (string $id) {
|
||||
if ($avatar = $this->user($id)->avatar()) {
|
||||
$avatar->delete();
|
||||
}
|
||||
|
||||
return $this->upload(function ($source, $filename) use ($id) {
|
||||
return $this->user($id)->createFile([
|
||||
'filename' => 'profile.' . F::extension($filename),
|
||||
'template' => 'avatar',
|
||||
'source' => $source
|
||||
]);
|
||||
}, $single = true);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users/(:any)/avatar',
|
||||
'method' => 'DELETE',
|
||||
'action' => function (string $id) {
|
||||
return $this->user($id)->avatar()->delete();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users/(:any)/email',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $id) {
|
||||
return $this->user($id)->changeEmail($this->requestBody('email'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users/(:any)/language',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $id) {
|
||||
return $this->user($id)->changeLanguage($this->requestBody('language'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users/(:any)/name',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $id) {
|
||||
return $this->user($id)->changeName($this->requestBody('name'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users/(:any)/password',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $id) {
|
||||
return $this->user($id)->changePassword($this->requestBody('password'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users/(:any)/role',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $id) {
|
||||
return $this->user($id)->changeRole($this->requestBody('role'));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'users/(:any)/sections/(:any)',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $id, string $sectionName) {
|
||||
if ($section = $this->user($id)->blueprint()->section($sectionName)) {
|
||||
return $section->toResponse();
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'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);
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
];
|
7
kirby/config/blueprints.php
Executable file
7
kirby/config/blueprints.php
Executable file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'files/default' => __DIR__ . '/blueprints/file.yml',
|
||||
'pages/default' => __DIR__ . '/blueprints/page.yml',
|
||||
'site' => __DIR__ . '/blueprints/site.yml'
|
||||
];
|
2
kirby/config/blueprints/file.yml
Executable file
2
kirby/config/blueprints/file.yml
Executable file
@@ -0,0 +1,2 @@
|
||||
name: File
|
||||
title: file
|
3
kirby/config/blueprints/page.yml
Executable file
3
kirby/config/blueprints/page.yml
Executable file
@@ -0,0 +1,3 @@
|
||||
name: Page
|
||||
title: Page
|
||||
|
7
kirby/config/blueprints/site.yml
Executable file
7
kirby/config/blueprints/site.yml
Executable file
@@ -0,0 +1,7 @@
|
||||
name: Site
|
||||
title: Site
|
||||
sections:
|
||||
pages:
|
||||
headline: Pages
|
||||
type: pages
|
||||
|
95
kirby/config/components.php
Executable file
95
kirby/config/components.php
Executable file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Filename;
|
||||
use Kirby\Cms\FileVersion;
|
||||
use Kirby\Cms\Model;
|
||||
use Kirby\Cms\Response;
|
||||
use Kirby\Cms\Template;
|
||||
use Kirby\Data\Data;
|
||||
use Kirby\Exception\NotFoundException;
|
||||
use Kirby\Image\Darkroom;
|
||||
use Kirby\Text\SmartyPants;
|
||||
use Kirby\Toolkit\F;
|
||||
use Kirby\Toolkit\Tpl as Snippet;
|
||||
|
||||
return [
|
||||
'file::version' => function (App $kirby, Model $file, array $options = []) {
|
||||
if ($file->isResizable() === false) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
// pre-calculate all thumb attributes
|
||||
$darkroom = Darkroom::factory(option('thumbs.driver', 'gd'), option('thumbs', []));
|
||||
$attributes = $darkroom->preprocess($file->root(), $options);
|
||||
|
||||
// create url and root
|
||||
$parent = $file->parent();
|
||||
$mediaRoot = $parent->mediaRoot() . '/' . $file->mediaHash();
|
||||
$dst = $mediaRoot . '/{{ name }}{{ attributes }}.{{ extension }}';
|
||||
$thumbRoot = (new Filename($file->root(), $dst, $attributes))->toString();
|
||||
$thumbName = basename($thumbRoot);
|
||||
$job = $mediaRoot . '/.jobs/' . $thumbName . '.json';
|
||||
|
||||
if (file_exists($thumbRoot) === false) {
|
||||
try {
|
||||
Data::write($job, array_merge($attributes, [
|
||||
'filename' => $file->filename()
|
||||
]));
|
||||
} catch (Throwable $e) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
return new FileVersion([
|
||||
'modifications' => $options,
|
||||
'original' => $file,
|
||||
'root' => $thumbRoot,
|
||||
'url' => $parent->mediaUrl() . '/' . $file->mediaHash() . '/' . $thumbName,
|
||||
]);
|
||||
},
|
||||
'file::url' => function (App $kirby, Model $file) {
|
||||
return $file->mediaUrl();
|
||||
},
|
||||
'markdown' => function (App $kirby, string $text = null, array $options = []): string {
|
||||
static $markdown;
|
||||
|
||||
if (isset($markdown) === false) {
|
||||
$parser = ($options['extra'] ?? false) === true ? 'ParsedownExtra' : 'Parsedown';
|
||||
$markdown = new $parser;
|
||||
$markdown->setBreaksEnabled($options['breaks'] ?? true);
|
||||
}
|
||||
|
||||
// we need the @ here, because parsedown has some notice issues :(
|
||||
return @$markdown->text($text);
|
||||
},
|
||||
'smartypants' => function (App $kirby, string $text = null, array $options = []): string {
|
||||
static $smartypants;
|
||||
|
||||
$smartypants = $smartypants ?? new Smartypants($options);
|
||||
|
||||
return $smartypants->parse($text);
|
||||
},
|
||||
'snippet' => function (App $kirby, string $name, array $data = []) {
|
||||
$file = $kirby->root('snippets') . '/' . $name . '.php';
|
||||
|
||||
if (file_exists($file) === false) {
|
||||
$file = $kirby->extensions('snippets')[$name] ?? null;
|
||||
}
|
||||
|
||||
return Snippet::load($file, $data);
|
||||
},
|
||||
'template' => function (App $kirby, string $name, string $type = 'html', string $defaultType = 'html') {
|
||||
return new Template($name, $type, $defaultType);
|
||||
},
|
||||
'thumb' => function (App $kirby, string $src, string $dst, array $options) {
|
||||
$darkroom = Darkroom::factory(option('thumbs.driver', 'gd'), option('thumbs', []));
|
||||
$options = $darkroom->preprocess($src, $options);
|
||||
$root = (new Filename($src, $dst, $options))->toString();
|
||||
|
||||
F::copy($src, $root);
|
||||
$darkroom->process($root, $options);
|
||||
|
||||
return $root;
|
||||
},
|
||||
];
|
27
kirby/config/fields.php
Executable file
27
kirby/config/fields.php
Executable file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'checkboxes' => __DIR__ . '/fields/checkboxes.php',
|
||||
'date' => __DIR__ . '/fields/date.php',
|
||||
'email' => __DIR__ . '/fields/email.php',
|
||||
'files' => __DIR__ . '/fields/files.php',
|
||||
'headline' => __DIR__ . '/fields/headline.php',
|
||||
'hidden' => __DIR__ . '/fields/hidden.php',
|
||||
'info' => __DIR__ . '/fields/info.php',
|
||||
'line' => __DIR__ . '/fields/line.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'
|
||||
];
|
64
kirby/config/fields/checkboxes.php
Executable file
64
kirby/config/fields/checkboxes.php
Executable file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Toolkit\A;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
return [
|
||||
'mixins' => ['options'],
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'after' => null,
|
||||
'before' => null,
|
||||
'icon' => null,
|
||||
'placeholder' => null,
|
||||
|
||||
/**
|
||||
* Arranges the checkboxes in the given number of columns
|
||||
*/
|
||||
'columns' => function (int $columns = 1) {
|
||||
return $columns;
|
||||
},
|
||||
/**
|
||||
* Default value for the field, which will be used when a Page/File/User is created
|
||||
*/
|
||||
'default' => function ($default = null) {
|
||||
return Str::split($default, ',');
|
||||
},
|
||||
/**
|
||||
* Maximum number of checked boxes
|
||||
*/
|
||||
'max' => function (int $max = null) {
|
||||
return $max;
|
||||
},
|
||||
/**
|
||||
* Minimum number of checked boxes
|
||||
*/
|
||||
'min' => function (int $min = null) {
|
||||
return $min;
|
||||
},
|
||||
'value' => function ($value = null) {
|
||||
return Str::split($value, ',');
|
||||
},
|
||||
],
|
||||
'computed' => [
|
||||
'options' => function (): array {
|
||||
return $this->getOptions();
|
||||
},
|
||||
'default' => function () {
|
||||
return $this->sanitizeOptions($this->default);
|
||||
},
|
||||
'value' => function () {
|
||||
return $this->sanitizeOptions($this->value);
|
||||
},
|
||||
],
|
||||
'save' => function ($value): string {
|
||||
return A::join($value, ', ');
|
||||
},
|
||||
'validations' => [
|
||||
'options',
|
||||
'max',
|
||||
'min'
|
||||
]
|
||||
];
|
76
kirby/config/fields/date.php
Executable file
76
kirby/config/fields/date.php
Executable file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Default date when a new Page/File/User gets created
|
||||
*/
|
||||
'default' => function ($default = null) {
|
||||
return $default;
|
||||
},
|
||||
/**
|
||||
* Changes the calendar icon to something custom
|
||||
*/
|
||||
'icon' => function (string $icon = "calendar") {
|
||||
return $icon;
|
||||
},
|
||||
/**
|
||||
* Youngest date, which can be selected/saved
|
||||
*/
|
||||
'max' => function (string $max = null) {
|
||||
return $this->toDate($max);
|
||||
},
|
||||
/**
|
||||
* Oldest date, which can be selected/saved
|
||||
*/
|
||||
'min' => function (string $min = null) {
|
||||
return $this->toDate($min);
|
||||
},
|
||||
/**
|
||||
* The placeholder is not available
|
||||
*/
|
||||
'placeholder' => null,
|
||||
/**
|
||||
* Pass true or an array of time field options to show the time selector.
|
||||
*/
|
||||
'time' => function ($time = false) {
|
||||
return $time;
|
||||
},
|
||||
/**
|
||||
* Must be a parseable date string
|
||||
*/
|
||||
'value' => function ($value = null) {
|
||||
return $value;
|
||||
},
|
||||
],
|
||||
'computed' => [
|
||||
'default' => function () {
|
||||
return $this->toDate($this->default);
|
||||
},
|
||||
'format' => function () {
|
||||
return $this->props['format'] ?? ($this->time() === false ? 'Y-m-d' : 'Y-m-d H:i');
|
||||
},
|
||||
'value' => function () {
|
||||
return $this->toDate($this->value);
|
||||
},
|
||||
],
|
||||
'methods' => [
|
||||
'toDate' => function ($value) {
|
||||
if ($timestamp = timestamp($value, $this->time['step'] ?? 5)) {
|
||||
return date(DATE_W3C, $timestamp);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
],
|
||||
'save' => function ($value) {
|
||||
if ($value !== null && $date = strtotime($value)) {
|
||||
return date($this->format(), $date);
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
'validations' => [
|
||||
'date'
|
||||
]
|
||||
];
|
38
kirby/config/fields/email.php
Executable file
38
kirby/config/fields/email.php
Executable file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'extends' => 'text',
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'converter' => null,
|
||||
'counter' => null,
|
||||
|
||||
/**
|
||||
* Sets the HTML5 autocomplete mode for the input
|
||||
*/
|
||||
'autocomplete' => function (string $autocomplete = 'email') {
|
||||
return $autocomplete;
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the email icon to something custom
|
||||
*/
|
||||
'icon' => function (string $icon = 'email') {
|
||||
return $icon;
|
||||
},
|
||||
|
||||
/**
|
||||
* Custom placeholder text, when the field is empty.
|
||||
*/
|
||||
'placeholder' => function ($value = null) {
|
||||
return I18n::translate($value, $value) ?? I18n::translate('email.placeholder');
|
||||
}
|
||||
],
|
||||
'validations' => [
|
||||
'minlength',
|
||||
'maxlength',
|
||||
'email'
|
||||
]
|
||||
];
|
188
kirby/config/fields/files.php
Executable file
188
kirby/config/fields/files.php
Executable file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Toolkit\A;
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'after' => null,
|
||||
'before' => null,
|
||||
'autofocus' => null,
|
||||
'icon' => null,
|
||||
'placeholder' => null,
|
||||
|
||||
/**
|
||||
* Sets the file(s), which are selected by default when a new page is created
|
||||
*/
|
||||
'default' => function ($default = null) {
|
||||
return $default;
|
||||
},
|
||||
|
||||
/**
|
||||
* The placeholder text if no pages have been selected yet
|
||||
*/
|
||||
'empty' => function ($empty = null) {
|
||||
return I18n::translate($empty, $empty);
|
||||
},
|
||||
|
||||
/**
|
||||
* Image settings for each item
|
||||
*/
|
||||
'image' => function (array $image = null) {
|
||||
return $image ?? [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Info text
|
||||
*/
|
||||
'info' => function (string $info = null) {
|
||||
return $info;
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the layout of the selected files. Available layouts: list, cards
|
||||
*/
|
||||
'layout' => function (string $layout = 'list') {
|
||||
return $layout;
|
||||
},
|
||||
|
||||
/**
|
||||
* Minimum number of required files
|
||||
*/
|
||||
'min' => function (int $min = null) {
|
||||
return $min;
|
||||
},
|
||||
|
||||
/**
|
||||
* Maximum number of allowed files
|
||||
*/
|
||||
'max' => function (int $max = null) {
|
||||
return $max;
|
||||
},
|
||||
|
||||
/**
|
||||
* If false, only a single file can be selected
|
||||
*/
|
||||
'multiple' => function (bool $multiple = true) {
|
||||
return $multiple;
|
||||
},
|
||||
|
||||
/**
|
||||
* Query for the files to be included
|
||||
*/
|
||||
'query' => function (string $query = 'page.files') {
|
||||
return $query;
|
||||
},
|
||||
|
||||
/**
|
||||
* Layout size for cards
|
||||
*/
|
||||
'size' => function (string $size = null) {
|
||||
return $size;
|
||||
},
|
||||
|
||||
/**
|
||||
* Main text
|
||||
*/
|
||||
'text' => function (string $text = '{{ file.filename }}') {
|
||||
return $text;
|
||||
},
|
||||
|
||||
'value' => function ($value = null) {
|
||||
return $value;
|
||||
}
|
||||
],
|
||||
'computed' => [
|
||||
'parentModel' => function () {
|
||||
if (is_string($this->parent) === true && $model = $this->model()->query($this->parent, 'Kirby\Cms\Model')) {
|
||||
return $model;
|
||||
}
|
||||
|
||||
return $this->model();
|
||||
},
|
||||
'parent' => function () {
|
||||
return $this->parentModel->apiUrl(true);
|
||||
},
|
||||
'default' => function () {
|
||||
return $this->toFiles($this->default);
|
||||
},
|
||||
'value' => function () {
|
||||
return $this->toFiles($this->value);
|
||||
},
|
||||
],
|
||||
'methods' => [
|
||||
'fileResponse' => function ($file) {
|
||||
if ($this->layout === 'list') {
|
||||
$thumb = [
|
||||
'width' => 100,
|
||||
'height' => 100
|
||||
];
|
||||
} else {
|
||||
$thumb = [
|
||||
'width' => 400,
|
||||
'height' => 400
|
||||
];
|
||||
}
|
||||
|
||||
$image = $file->panelImage($this->image, $thumb);
|
||||
$model = $this->model();
|
||||
$uuid = $file->parent() === $model ? $file->filename() : $file->id();
|
||||
|
||||
return [
|
||||
'filename' => $file->filename(),
|
||||
'text' => $file->toString($this->text),
|
||||
'link' => $file->panelUrl(true),
|
||||
'id' => $file->id(),
|
||||
'uuid' => $uuid,
|
||||
'url' => $file->url(),
|
||||
'info' => $file->toString($this->info ?? false),
|
||||
'image' => $image,
|
||||
'icon' => $file->panelIcon($image),
|
||||
'type' => $file->type(),
|
||||
];
|
||||
},
|
||||
'toFiles' => function ($value = null) {
|
||||
$files = [];
|
||||
$kirby = kirby();
|
||||
|
||||
foreach (Yaml::decode($value) as $id) {
|
||||
if (is_array($id) === true) {
|
||||
$id = $id['id'] ?? null;
|
||||
}
|
||||
|
||||
if ($id !== null && ($file = $this->kirby()->file($id, $this->model()))) {
|
||||
$files[] = $this->fileResponse($file);
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
],
|
||||
'api' => function () {
|
||||
return [
|
||||
[
|
||||
'pattern' => '/',
|
||||
'action' => function () {
|
||||
$field = $this->field();
|
||||
$files = $field->model()->query($field->query(), 'Kirby\Cms\Files');
|
||||
$data = [];
|
||||
|
||||
foreach ($files as $index => $file) {
|
||||
$data[] = $field->fileResponse($file);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
]
|
||||
];
|
||||
},
|
||||
'save' => function ($value = null) {
|
||||
return A::pluck($value, 'uuid');
|
||||
},
|
||||
'validations' => [
|
||||
'max',
|
||||
'min'
|
||||
]
|
||||
];
|
27
kirby/config/fields/headline.php
Executable file
27
kirby/config/fields/headline.php
Executable file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'save' => false,
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'after' => null,
|
||||
'autofocus' => null,
|
||||
'before' => null,
|
||||
'default' => null,
|
||||
'disabled' => null,
|
||||
'help' => null,
|
||||
'icon' => null,
|
||||
'placeholder' => null,
|
||||
'required' => null,
|
||||
'translate' => null,
|
||||
|
||||
/**
|
||||
* If false, the prepended number will be hidden
|
||||
*/
|
||||
'numbered' => function (bool $numbered = true) {
|
||||
return $numbered;
|
||||
}
|
||||
]
|
||||
];
|
3
kirby/config/fields/hidden.php
Executable file
3
kirby/config/fields/hidden.php
Executable file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
return [];
|
23
kirby/config/fields/info.php
Executable file
23
kirby/config/fields/info.php
Executable file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Toolkit\I18n;
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
'text' => function ($value = null) {
|
||||
return I18n::translate($value, $value);
|
||||
},
|
||||
],
|
||||
'computed' => [
|
||||
'text' => function () {
|
||||
$text = $this->text;
|
||||
|
||||
if ($model = $this->model()) {
|
||||
$text = $this->model()->toString($text);
|
||||
}
|
||||
|
||||
return kirbytext($text);
|
||||
}
|
||||
],
|
||||
'save' => false,
|
||||
];
|
5
kirby/config/fields/line.php
Executable file
5
kirby/config/fields/line.php
Executable file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'save' => false
|
||||
];
|
43
kirby/config/fields/mixins/options.php
Executable file
43
kirby/config/fields/mixins/options.php
Executable file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Form\Options;
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* API settings for options requests. This will only take affect when <code>options</code> is set to <code>api</code>.
|
||||
*/
|
||||
'api' => function ($api = null) {
|
||||
return $api;
|
||||
},
|
||||
/**
|
||||
* An array with options
|
||||
*/
|
||||
'options' => function ($options = []) {
|
||||
return $options;
|
||||
},
|
||||
/**
|
||||
* Query settings for options queries. This will only take affect when <code>options</code> is set to <code>query</code>.
|
||||
*/
|
||||
'query' => function ($query = null) {
|
||||
return $query;
|
||||
},
|
||||
],
|
||||
'methods' => [
|
||||
'getOptions' => function () {
|
||||
return Options::factory(
|
||||
$this->options(),
|
||||
$this->props,
|
||||
$this->model()
|
||||
);
|
||||
},
|
||||
'sanitizeOption' => function ($option) {
|
||||
$allowed = array_column($this->options(), 'value');
|
||||
return in_array($option, $allowed, true) === true ? $option : null;
|
||||
},
|
||||
'sanitizeOptions' => function ($options) {
|
||||
$allowed = array_column($this->options(), 'value');
|
||||
return array_intersect($options, $allowed);
|
||||
},
|
||||
]
|
||||
];
|
25
kirby/config/fields/multiselect.php
Executable file
25
kirby/config/fields/multiselect.php
Executable file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'extends' => 'tags',
|
||||
'props' => [
|
||||
/**
|
||||
* Custom icon to replace the arrow down.
|
||||
*/
|
||||
'icon' => function (string $icon = null) {
|
||||
return $icon;
|
||||
},
|
||||
/**
|
||||
* Enable/disable the search in the dropdown
|
||||
*/
|
||||
'search' => function (bool $search = true) {
|
||||
return $search;
|
||||
},
|
||||
/**
|
||||
* If true, entries will be sorted alphabetically on selection
|
||||
*/
|
||||
'sort' => function (bool $sort = false) {
|
||||
return $sort;
|
||||
},
|
||||
]
|
||||
];
|
49
kirby/config/fields/number.php
Executable file
49
kirby/config/fields/number.php
Executable file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Default number that will be saved when a new Page/User/File is created
|
||||
*/
|
||||
'default' => function ($default = null) {
|
||||
return $this->toNumber($default);
|
||||
},
|
||||
/**
|
||||
* The lowest allowed number
|
||||
*/
|
||||
'min' => function (float $min = null) {
|
||||
return $min;
|
||||
},
|
||||
/**
|
||||
* The highest allowed number
|
||||
*/
|
||||
'max' => function (float $max = null) {
|
||||
return $max;
|
||||
},
|
||||
/**
|
||||
* Allowed incremental steps between numbers (i.e 0.5)
|
||||
*/
|
||||
'step' => function ($step = 1) {
|
||||
return $this->toNumber($step);
|
||||
},
|
||||
'value' => function ($value = null) {
|
||||
return $this->toNumber($value);
|
||||
}
|
||||
],
|
||||
'methods' => [
|
||||
'toNumber' => function ($value) {
|
||||
if ($this->isEmpty($value) === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$value = str_replace(',', '.', $value);
|
||||
$value = floatval($value);
|
||||
|
||||
return $value;
|
||||
}
|
||||
],
|
||||
'validations' => [
|
||||
'min',
|
||||
'max'
|
||||
]
|
||||
];
|
188
kirby/config/fields/pages.php
Executable file
188
kirby/config/fields/pages.php
Executable file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Toolkit\A;
|
||||
use Kirby\Toolkit\I18n;
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'after' => null,
|
||||
'autofocus' => null,
|
||||
'before' => null,
|
||||
'icon' => null,
|
||||
'placeholder' => null,
|
||||
|
||||
/**
|
||||
* Default selected page(s) when a new Page/File/User is created
|
||||
*/
|
||||
'default' => function ($default = null) {
|
||||
return $this->toPages($default);
|
||||
},
|
||||
|
||||
/**
|
||||
* The placeholder text if no pages have been selected yet
|
||||
*/
|
||||
'empty' => function ($empty = null) {
|
||||
return I18n::translate($empty, $empty);
|
||||
},
|
||||
|
||||
/**
|
||||
* Image settings for each item
|
||||
*/
|
||||
'image' => function (array $image = null) {
|
||||
return $image ?? [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Info text
|
||||
*/
|
||||
'info' => function (string $info = null) {
|
||||
return $info;
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the layout of the selected files. Available layouts: list, cards
|
||||
*/
|
||||
'layout' => function (string $layout = 'list') {
|
||||
return $layout;
|
||||
},
|
||||
|
||||
/**
|
||||
* The minimum number of required selected pages
|
||||
*/
|
||||
'min' => function (int $min = null) {
|
||||
return $min;
|
||||
},
|
||||
|
||||
/**
|
||||
* The maximum number of allowed selected pages
|
||||
*/
|
||||
'max' => function (int $max = null) {
|
||||
return $max;
|
||||
},
|
||||
|
||||
/**
|
||||
* If false, only a single page can be selected
|
||||
*/
|
||||
'multiple' => function (bool $multiple = true) {
|
||||
return $multiple;
|
||||
},
|
||||
|
||||
/**
|
||||
* Optional query to select a specific set of pages
|
||||
*/
|
||||
'query' => function (string $query = null) {
|
||||
return $query;
|
||||
},
|
||||
|
||||
/**
|
||||
* Layout size for cards
|
||||
*/
|
||||
'size' => function (string $size = null) {
|
||||
return $size;
|
||||
},
|
||||
|
||||
/**
|
||||
* Main text
|
||||
*/
|
||||
'text' => function (string $text = null) {
|
||||
return $text;
|
||||
},
|
||||
|
||||
'value' => function ($value = null) {
|
||||
return $this->toPages($value);
|
||||
},
|
||||
],
|
||||
'methods' => [
|
||||
'pageResponse' => function ($page) {
|
||||
if ($this->layout === 'list') {
|
||||
$thumb = [
|
||||
'width' => 100,
|
||||
'height' => 100
|
||||
];
|
||||
} else {
|
||||
$thumb = [
|
||||
'width' => 400,
|
||||
'height' => 400
|
||||
];
|
||||
}
|
||||
|
||||
$image = $page->panelImage($this->image, $thumb);
|
||||
$model = $this->model();
|
||||
|
||||
return [
|
||||
'text' => $page->toString($this->text ?? '{{ page.title }}'),
|
||||
'link' => $page->panelUrl(true),
|
||||
'id' => $page->id(),
|
||||
'info' => $page->toString($this->info ?? false),
|
||||
'image' => $image,
|
||||
'icon' => $page->panelIcon($image),
|
||||
'hasChildren' => $page->hasChildren(),
|
||||
];
|
||||
},
|
||||
'toPages' => function ($value = null) {
|
||||
$pages = [];
|
||||
$kirby = kirby();
|
||||
|
||||
foreach (Yaml::decode($value) as $id) {
|
||||
if (is_array($id) === true) {
|
||||
$id = $id['id'] ?? null;
|
||||
}
|
||||
|
||||
if ($id !== null && ($page = $kirby->page($id))) {
|
||||
$pages[] = $this->pageResponse($page);
|
||||
}
|
||||
}
|
||||
|
||||
return $pages;
|
||||
}
|
||||
],
|
||||
'api' => function () {
|
||||
return [
|
||||
[
|
||||
'pattern' => '/',
|
||||
'action' => function () {
|
||||
$field = $this->field();
|
||||
$query = $field->query();
|
||||
|
||||
if ($query) {
|
||||
$pages = $field->model()->query($query, 'Kirby\Cms\Pages');
|
||||
$model = null;
|
||||
} else {
|
||||
if (!$parent = $this->site()->find($this->requestQuery('parent'))) {
|
||||
$parent = $this->site();
|
||||
}
|
||||
|
||||
$pages = $parent->children();
|
||||
$model = [
|
||||
'id' => $parent->id() == '' ? null : $parent->id(),
|
||||
'title' => $parent->title()->value()
|
||||
];
|
||||
}
|
||||
|
||||
$children = [];
|
||||
|
||||
foreach ($pages as $index => $page) {
|
||||
if ($page->isReadable() === true) {
|
||||
$children[] = $field->pageResponse($page);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'model' => $model,
|
||||
'pages' => $children
|
||||
];
|
||||
}
|
||||
]
|
||||
];
|
||||
},
|
||||
'save' => function ($value = null) {
|
||||
return A::pluck($value, 'id');
|
||||
},
|
||||
'validations' => [
|
||||
'max',
|
||||
'min'
|
||||
]
|
||||
];
|
32
kirby/config/fields/radio.php
Executable file
32
kirby/config/fields/radio.php
Executable file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'mixins' => ['options'],
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'after' => null,
|
||||
'before' => null,
|
||||
'icon' => null,
|
||||
'placeholder' => null,
|
||||
|
||||
/**
|
||||
* Arranges the radio buttons in the given number of columns
|
||||
*/
|
||||
'columns' => function (int $columns = 1) {
|
||||
return $columns;
|
||||
},
|
||||
],
|
||||
'computed' => [
|
||||
'options' => function (): array {
|
||||
return $this->getOptions();
|
||||
},
|
||||
'default' => function () {
|
||||
return $this->sanitizeOption($this->default);
|
||||
},
|
||||
'value' => function () {
|
||||
return $this->sanitizeOption($this->value) ?? '';
|
||||
}
|
||||
]
|
||||
];
|
24
kirby/config/fields/range.php
Executable file
24
kirby/config/fields/range.php
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'extends' => 'number',
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'placeholder' => null,
|
||||
|
||||
/**
|
||||
* The maximum value on the slider
|
||||
*/
|
||||
'max' => function (float $max = 100) {
|
||||
return $max;
|
||||
},
|
||||
/**
|
||||
* Enables/disables the tooltip and set the before and after values
|
||||
*/
|
||||
'tooltip' => function ($tooltip = true) {
|
||||
return $tooltip;
|
||||
},
|
||||
]
|
||||
];
|
18
kirby/config/fields/select.php
Executable file
18
kirby/config/fields/select.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'extends' => 'radio',
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'columns' => null,
|
||||
|
||||
/**
|
||||
* Custom icon to replace the arrow down.
|
||||
*/
|
||||
'icon' => function (string $icon = null) {
|
||||
return $icon;
|
||||
},
|
||||
]
|
||||
];
|
159
kirby/config/fields/structure.php
Executable file
159
kirby/config/fields/structure.php
Executable file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Form\Form;
|
||||
use Kirby\Cms\Blueprint;
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'after' => null,
|
||||
'before' => null,
|
||||
'autofocus' => null,
|
||||
'icon' => null,
|
||||
'placeholder' => null,
|
||||
|
||||
/**
|
||||
* Optional columns definition to only show selected fields in the structure table.
|
||||
*/
|
||||
'columns' => function (array $columns = []) {
|
||||
// lower case all keys, because field names will
|
||||
// be lowercase as well.
|
||||
return array_change_key_case($columns);
|
||||
},
|
||||
/**
|
||||
* Fields setup for the structure form. Works just like fields in regular forms.
|
||||
*/
|
||||
'fields' => function (array $fields) {
|
||||
return $fields;
|
||||
},
|
||||
/**
|
||||
* The number of entries that will be displayed on a single page. Afterwards pagination kicks in.
|
||||
*/
|
||||
'limit' => function (int $limit = null) {
|
||||
return $limit;
|
||||
},
|
||||
/**
|
||||
* Maximum allowed entries in the structure. Afterwards the "Add" button will be switched off.
|
||||
*/
|
||||
'max' => function (int $max = null) {
|
||||
return $max;
|
||||
},
|
||||
/**
|
||||
* Minimum required entries in the structure
|
||||
*/
|
||||
'min' => function (int $min = null) {
|
||||
return $min;
|
||||
},
|
||||
/**
|
||||
* Toggles drag & drop sorting
|
||||
*/
|
||||
'sortable' => function (bool $sortable = null) {
|
||||
return $sortable;
|
||||
},
|
||||
/**
|
||||
* Sorts the entries by the given field and order (i.e. title desc)
|
||||
* Drag & drop is disabled in this case
|
||||
*/
|
||||
'sortBy' => function (string $sort = null) {
|
||||
return $sort;
|
||||
}
|
||||
],
|
||||
'computed' => [
|
||||
'default' => function () {
|
||||
return $this->rows($this->default);
|
||||
},
|
||||
'value' => function () {
|
||||
return $this->rows($this->value);
|
||||
},
|
||||
'fields' => function () {
|
||||
return $this->form()->fields()->toArray();
|
||||
},
|
||||
'columns' => function () {
|
||||
$columns = [];
|
||||
|
||||
if (empty($this->columns)) {
|
||||
foreach ($this->fields as $field) {
|
||||
|
||||
// Skip hidden fields.
|
||||
// They should never be included as column
|
||||
if ($field['type'] === 'hidden') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$columns[$field['name']] = [
|
||||
'type' => $field['type'],
|
||||
'label' => $field['label'] ?? $field['name']
|
||||
];
|
||||
}
|
||||
} else {
|
||||
foreach ($this->columns as $columnName => $columnProps) {
|
||||
if (is_array($columnProps) === false) {
|
||||
$columnProps = [];
|
||||
}
|
||||
|
||||
$field = $this->fields[$columnName] ?? null;
|
||||
|
||||
if (empty($field) === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$columns[$columnName] = array_merge($columnProps, [
|
||||
'type' => $field['type'],
|
||||
'label' => $field['label'] ?? $field['name']
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $columns;
|
||||
},
|
||||
],
|
||||
'methods' => [
|
||||
'rows' => function ($value) {
|
||||
$rows = Yaml::decode($value);
|
||||
$value = [];
|
||||
|
||||
foreach ($rows as $index => $row) {
|
||||
if (is_array($row) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value[] = $this->form($row)->values();
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
'form' => function (array $values = []) {
|
||||
return new Form([
|
||||
'fields' => $this->attrs['fields'],
|
||||
'values' => $values,
|
||||
'model' => $this->model
|
||||
]);
|
||||
},
|
||||
],
|
||||
'api' => function () {
|
||||
return [
|
||||
[
|
||||
'pattern' => 'validate',
|
||||
'method' => 'ALL',
|
||||
'action' => function () {
|
||||
return array_values($this->field()->form($this->requestBody())->errors());
|
||||
}
|
||||
]
|
||||
];
|
||||
},
|
||||
'save' => function () {
|
||||
$data = [];
|
||||
|
||||
foreach ($this->value() as $row) {
|
||||
$data[] = $this->form($row)->data();
|
||||
}
|
||||
|
||||
return $data;
|
||||
},
|
||||
'validations' => [
|
||||
'min',
|
||||
'max'
|
||||
]
|
||||
];
|
91
kirby/config/fields/tags.php
Executable file
91
kirby/config/fields/tags.php
Executable file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'mixins' => ['options'],
|
||||
'props' => [
|
||||
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'after' => null,
|
||||
'before' => null,
|
||||
'placeholder' => null,
|
||||
|
||||
/**
|
||||
* If set to <code>all</code>, any type of input is accepted. If set to <code>options</code> only the predefined options are accepted as input.
|
||||
*/
|
||||
'accept' => function ($value = 'all') {
|
||||
return V::in($value, ['all', 'options']) ? $value : 'all';
|
||||
},
|
||||
/**
|
||||
* Changes the tag icon
|
||||
*/
|
||||
'icon' => function ($icon = 'tag') {
|
||||
return $icon;
|
||||
},
|
||||
/**
|
||||
* Minimum number of required entries/tags
|
||||
*/
|
||||
'min' => function (int $min = null) {
|
||||
return $min;
|
||||
},
|
||||
/**
|
||||
* Maximum number of allowed entries/tags
|
||||
*/
|
||||
'max' => function (int $max = null) {
|
||||
return $max;
|
||||
},
|
||||
/**
|
||||
* Custom tags separator, which will be used to store tags in the content file
|
||||
*/
|
||||
'separator' => function (string $separator = ',') {
|
||||
return $separator;
|
||||
},
|
||||
],
|
||||
'computed' => [
|
||||
'options' => function () {
|
||||
return $this->getOptions();
|
||||
},
|
||||
'default' => function (): array {
|
||||
return $this->toTags($this->default);
|
||||
},
|
||||
'value' => function (): array {
|
||||
return $this->toTags($this->value);
|
||||
}
|
||||
],
|
||||
'methods' => [
|
||||
'toTags' => function ($value) {
|
||||
$options = $this->options();
|
||||
|
||||
// transform into value-text objects
|
||||
return array_map(function ($option) use ($options) {
|
||||
|
||||
// already a valid object
|
||||
if (is_array($option) === true && isset($option['value'], $option['text']) === true) {
|
||||
return $option;
|
||||
}
|
||||
|
||||
$index = array_search($option, array_column($options, 'value'));
|
||||
|
||||
if ($index !== false) {
|
||||
return $options[$index];
|
||||
}
|
||||
|
||||
return [
|
||||
'value' => $option,
|
||||
'text' => $option,
|
||||
];
|
||||
}, Str::split($value));
|
||||
}
|
||||
],
|
||||
'save' => function (array $value = null): string {
|
||||
return A::join(
|
||||
A::pluck($value, 'value'),
|
||||
$this->separator() . ' '
|
||||
);
|
||||
},
|
||||
'validations' => [
|
||||
'min',
|
||||
'max'
|
||||
]
|
||||
];
|
27
kirby/config/fields/tel.php
Executable file
27
kirby/config/fields/tel.php
Executable file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'extends' => 'text',
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'converter' => null,
|
||||
'counter' => null,
|
||||
'spellcheck' => null,
|
||||
|
||||
/**
|
||||
* Sets the HTML5 autocomplete attribute
|
||||
*/
|
||||
'autocomplete' => function (string $autocomplete = 'tel') {
|
||||
return $autocomplete;
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the phone icon
|
||||
*/
|
||||
'icon' => function (string $icon = 'phone') {
|
||||
return $icon;
|
||||
}
|
||||
]
|
||||
];
|
103
kirby/config/fields/text.php
Executable file
103
kirby/config/fields/text.php
Executable file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Toolkit\I18n;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
|
||||
/**
|
||||
* The field value will be converted with the selected converter before the value gets saved. Available converters: lower, upper, ucfirst, slug
|
||||
*/
|
||||
'converter' => function ($value = null) {
|
||||
if ($value !== null && in_array($value, array_keys($this->converters())) === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'field.converter.invalid',
|
||||
'data' => ['converter' => $value]
|
||||
]);
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows or hides the character counter in the top right corner
|
||||
*/
|
||||
'counter' => function (bool $counter = true) {
|
||||
return $counter;
|
||||
},
|
||||
|
||||
/**
|
||||
* Maximum number of allowed characters
|
||||
*/
|
||||
'maxlength' => function (int $maxlength = null) {
|
||||
return $maxlength;
|
||||
},
|
||||
|
||||
/**
|
||||
* Minimum number of required characters
|
||||
*/
|
||||
'minlength' => function (int $minlength = null) {
|
||||
return $minlength;
|
||||
},
|
||||
|
||||
/**
|
||||
* A regular expression, which will be used to validate the input
|
||||
*/
|
||||
'pattern' => function (string $pattern = null) {
|
||||
return $pattern;
|
||||
},
|
||||
|
||||
/**
|
||||
* If false, spellcheck will be switched off
|
||||
*/
|
||||
'spellcheck' => function (bool $spellcheck = false) {
|
||||
return $spellcheck;
|
||||
},
|
||||
],
|
||||
'computed' => [
|
||||
'default' => function () {
|
||||
return $this->convert($this->default);
|
||||
},
|
||||
'value' => function () {
|
||||
return (string)$this->convert($this->value);
|
||||
}
|
||||
],
|
||||
'methods' => [
|
||||
'convert' => function ($value) {
|
||||
if ($this->converter() === null) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$value = trim($value);
|
||||
$converter = $this->converters()[$this->converter()];
|
||||
|
||||
if (is_array($value) === true) {
|
||||
return array_map($converter, $value);
|
||||
}
|
||||
|
||||
return call_user_func($converter, $value);
|
||||
},
|
||||
'converters' => function (): array {
|
||||
return [
|
||||
'lower' => function ($value) {
|
||||
return Str::lower($value);
|
||||
},
|
||||
'slug' => function ($value) {
|
||||
return Str::slug($value);
|
||||
},
|
||||
'ucfirst' => function ($value) {
|
||||
return Str::ucfirst($value);
|
||||
},
|
||||
'upper' => function ($value) {
|
||||
return Str::upper($value);
|
||||
},
|
||||
];
|
||||
},
|
||||
],
|
||||
'validations' => [
|
||||
'minlength',
|
||||
'maxlength'
|
||||
]
|
||||
];
|
61
kirby/config/fields/textarea.php
Executable file
61
kirby/config/fields/textarea.php
Executable file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'after' => null,
|
||||
'before' => null,
|
||||
|
||||
/**
|
||||
* Enables/disables the format buttons. Can either be true/false or a list of allowed buttons. Available buttons: headlines, italic, bold, link, email, list, code, ul, ol
|
||||
*/
|
||||
'buttons' => function ($buttons = true) {
|
||||
return $buttons;
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables/disables the character counter in the top right corner
|
||||
*/
|
||||
'counter' => function (bool $counter = true) {
|
||||
return $counter;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the default text when a new Page/File/User is created
|
||||
*/
|
||||
'default' => function (string $default = null) {
|
||||
return trim($default);
|
||||
},
|
||||
|
||||
/**
|
||||
* Maximum number of allowed characters
|
||||
*/
|
||||
'maxlength' => function (int $maxlength = null) {
|
||||
return $maxlength;
|
||||
},
|
||||
|
||||
/**
|
||||
* Minimum number of required characters
|
||||
*/
|
||||
'minlength' => function (int $minlength = null) {
|
||||
return $minlength;
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the size of the textarea. Available sizes: small, medium, large, huge
|
||||
*/
|
||||
'size' => function (string $size = null) {
|
||||
return $size;
|
||||
},
|
||||
|
||||
'value' => function (string $value = null) {
|
||||
return trim($value);
|
||||
}
|
||||
],
|
||||
'validations' => [
|
||||
'minlength',
|
||||
'maxlength'
|
||||
]
|
||||
];
|
68
kirby/config/fields/time.php
Executable file
68
kirby/config/fields/time.php
Executable file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'placeholder' => null,
|
||||
|
||||
/**
|
||||
* Sets the default time when a new Page/File/User is created
|
||||
*/
|
||||
'default' => function ($default = null) {
|
||||
return $default;
|
||||
},
|
||||
/**
|
||||
* Changes the clock icon
|
||||
*/
|
||||
'icon' => function (string $icon = 'clock') {
|
||||
return $icon;
|
||||
},
|
||||
/**
|
||||
* 12 or 24 hour notation. If 12, an AM/PM selector will be shown.
|
||||
*/
|
||||
'notation' => function (int $value = 24) {
|
||||
return $value === 24 ? 24 : 12;
|
||||
},
|
||||
/**
|
||||
* The interval between minutes in the minutes select dropdown.
|
||||
*/
|
||||
'step' => function (int $step = 5) {
|
||||
return $step;
|
||||
},
|
||||
'value' => function ($value = null) {
|
||||
return $value;
|
||||
}
|
||||
],
|
||||
'computed' => [
|
||||
'default' => function () {
|
||||
return $this->toTime($this->default);
|
||||
},
|
||||
'format' => function () {
|
||||
return $this->notation === 24 ? 'H:i' : 'h:i a';
|
||||
},
|
||||
'value' => function () {
|
||||
return $this->toTime($this->value);
|
||||
}
|
||||
],
|
||||
'methods' => [
|
||||
'toTime' => function ($value) {
|
||||
if ($timestamp = timestamp($value, $this->step)) {
|
||||
return date('H:i', $timestamp);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
],
|
||||
'save' => function ($value): string {
|
||||
if ($timestamp = strtotime($value)) {
|
||||
return date($this->format, $timestamp);
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
'validations' => [
|
||||
'time',
|
||||
]
|
||||
];
|
64
kirby/config/fields/toggle.php
Executable file
64
kirby/config/fields/toggle.php
Executable file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'placeholder' => null,
|
||||
|
||||
/**
|
||||
* Default value which will be saved when a new Page/User/File is created
|
||||
*/
|
||||
'default' => function ($value = null) {
|
||||
return $this->toBool($value);
|
||||
},
|
||||
/**
|
||||
* 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) {
|
||||
if (is_array($value) === true) {
|
||||
if (A::isAssociative($value) === true) {
|
||||
return I18n::translate($value, $value);
|
||||
}
|
||||
|
||||
foreach ($value as $key => $val) {
|
||||
$value[$key] = I18n::translate($val, $val);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
return I18n::translate($value, $value);
|
||||
},
|
||||
],
|
||||
'computed' => [
|
||||
'value' => function () {
|
||||
if ($this->props['value'] === null) {
|
||||
return $this->default();
|
||||
} else {
|
||||
return $this->toBool($this->props['value']);
|
||||
}
|
||||
}
|
||||
],
|
||||
'methods' => [
|
||||
'toBool' => function ($value) {
|
||||
return in_array($value, [true, 'true', 1, '1', 'on'], true) === true;
|
||||
}
|
||||
],
|
||||
'save' => function (): string {
|
||||
return $this->value() === true ? 'true' : 'false';
|
||||
},
|
||||
'validations' => [
|
||||
'boolean',
|
||||
'required' => function ($value) {
|
||||
if ($this->isRequired() && ($value === false || $this->isEmpty($value))) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'form.field.required'
|
||||
]);
|
||||
}
|
||||
},
|
||||
]
|
||||
];
|
39
kirby/config/fields/url.php
Executable file
39
kirby/config/fields/url.php
Executable file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'extends' => 'text',
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'converter' => null,
|
||||
'counter' => null,
|
||||
'spellcheck' => null,
|
||||
|
||||
/**
|
||||
* Sets the HTML5 autocomplete attribute
|
||||
*/
|
||||
'autocomplete' => function (string $autocomplete = 'url') {
|
||||
return $autocomplete;
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the link icon
|
||||
*/
|
||||
'icon' => function (string $icon = 'url') {
|
||||
return $icon;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets custom placeholder text, when the field is empty
|
||||
*/
|
||||
'placeholder' => function ($value = null) {
|
||||
return I18n::translate($value, $value) ?? 'https://example.com';
|
||||
}
|
||||
],
|
||||
'validations' => [
|
||||
'minlength',
|
||||
'maxlength',
|
||||
'url'
|
||||
],
|
||||
];
|
95
kirby/config/fields/users.php
Executable file
95
kirby/config/fields/users.php
Executable file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Unset inherited props
|
||||
*/
|
||||
'after' => null,
|
||||
'autofocus' => null,
|
||||
'before' => null,
|
||||
'icon' => null,
|
||||
'placeholder' => null,
|
||||
|
||||
/**
|
||||
* Default selected user(s) when a new Page/File/User is created
|
||||
*/
|
||||
'default' => function ($default = null) {
|
||||
if ($default === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($default === null && $user = $this->kirby()->user()) {
|
||||
return [
|
||||
$this->userResponse($user)
|
||||
];
|
||||
}
|
||||
|
||||
return $this->toUsers($default);
|
||||
},
|
||||
/**
|
||||
* The minimum number of required selected users
|
||||
*/
|
||||
'min' => function (int $min = null) {
|
||||
return $min;
|
||||
},
|
||||
/**
|
||||
* The maximum number of allowed selected users
|
||||
*/
|
||||
'max' => function (int $max = null) {
|
||||
return $max;
|
||||
},
|
||||
/**
|
||||
* If false, only a single user can be selected
|
||||
*/
|
||||
'multiple' => function (bool $multiple = true) {
|
||||
return $multiple;
|
||||
},
|
||||
'value' => function ($value = null) {
|
||||
return $this->toUsers($value);
|
||||
},
|
||||
],
|
||||
'methods' => [
|
||||
'userResponse' => function ($user) {
|
||||
$avatar = function ($user) {
|
||||
if ($avatar = $user->avatar()) {
|
||||
return [
|
||||
'url' => $avatar->crop(512)->url()
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return [
|
||||
'username' => $user->username(),
|
||||
'id' => $user->id(),
|
||||
'email' => $user->email(),
|
||||
'avatar' => $avatar($user)
|
||||
];
|
||||
},
|
||||
'toUsers' => function ($value = null) {
|
||||
$users = [];
|
||||
$kirby = kirby();
|
||||
|
||||
foreach (Yaml::decode($value) as $email) {
|
||||
if (is_array($email) === true) {
|
||||
$email = $email['email'] ?? null;
|
||||
}
|
||||
|
||||
if ($email !== null && ($user = $kirby->user($email))) {
|
||||
$users[] = $this->userResponse($user);
|
||||
}
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
],
|
||||
'save' => function ($value = null) {
|
||||
return A::pluck($value, 'email');
|
||||
},
|
||||
'validations' => [
|
||||
'max',
|
||||
'min'
|
||||
]
|
||||
];
|
764
kirby/config/helpers.php
Executable file
764
kirby/config/helpers.php
Executable file
@@ -0,0 +1,764 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Html;
|
||||
use Kirby\Cms\Response;
|
||||
use Kirby\Cms\Url;
|
||||
use Kirby\Exception\Exception;
|
||||
use Kirby\Http\Server;
|
||||
use Kirby\Toolkit\Escape;
|
||||
use Kirby\Toolkit\F;
|
||||
use Kirby\Toolkit\I18n;
|
||||
use Kirby\Toolkit\View;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
function attr(array $attr = null, $before = null, $after = null)
|
||||
{
|
||||
if ($attrs = Html::attr($attr)) {
|
||||
return $before . $attrs . $after;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of a collection by name
|
||||
*
|
||||
* @param string $name
|
||||
* @return Collection|null
|
||||
*/
|
||||
function collection(string $name)
|
||||
{
|
||||
return App::instance()->collection($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks / returns a CSRF token
|
||||
*
|
||||
* @param string $check Pass a token here to compare it to the one in the session
|
||||
* @return string|boolean Either the token or a boolean check result
|
||||
*/
|
||||
function csrf(string $check = null)
|
||||
{
|
||||
$session = App::instance()->session();
|
||||
|
||||
// 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!
|
||||
if (func_num_args() === 0) {
|
||||
// no arguments, generate/return a token
|
||||
|
||||
$token = $session->get('csrf');
|
||||
if (is_string($token) !== true) {
|
||||
$token = bin2hex(random_bytes(32));
|
||||
$session->set('csrf', $token);
|
||||
}
|
||||
|
||||
return $token;
|
||||
} elseif (is_string($check) === true && is_string($session->get('csrf')) === true) {
|
||||
// argument has been passed, check the token
|
||||
return hash_equals($session->get('csrf'), $check) === true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates one or multiple CSS link tags
|
||||
*
|
||||
* @param string|array $url Relative or absolute URLs, an array of URLs or `@auto` for automatic template css loading
|
||||
* @param string|array $options Pass an array of attributes for the link tag or a media attribute string
|
||||
* @return string|null
|
||||
*/
|
||||
function css($url, $options = null)
|
||||
{
|
||||
if (is_array($url) === true) {
|
||||
$links = array_map(function ($url) use ($options) {
|
||||
return css($url, $options);
|
||||
}, $url);
|
||||
|
||||
return implode(PHP_EOL, $links);
|
||||
}
|
||||
|
||||
$href = $url === '@auto' ? Url::toTemplateAsset('css/templates', 'css') : Url::to($url);
|
||||
|
||||
$attr = [
|
||||
'href' => $href,
|
||||
'rel' => 'stylesheet'
|
||||
];
|
||||
|
||||
if (is_string($options) === true) {
|
||||
$attr['media'] = $options;
|
||||
}
|
||||
|
||||
if (is_array($options) === true) {
|
||||
$attr = array_merge($options, $attr);
|
||||
}
|
||||
|
||||
return '<link ' . attr($attr) . '>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple object and variable dumper
|
||||
* to help with debugging.
|
||||
*
|
||||
* @param mixed $variable
|
||||
* @param boolean $echo
|
||||
* @return string
|
||||
*/
|
||||
function dump($variable, bool $echo = true): string
|
||||
{
|
||||
if (Server::cli() === true) {
|
||||
$output = print_r($variable, true) . PHP_EOL;
|
||||
} else {
|
||||
$output = '<pre>' . print_r($variable, true) . '</pre>';
|
||||
}
|
||||
|
||||
if ($echo === true) {
|
||||
echo $output;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Smart version of echo with an if condition as first argument
|
||||
*
|
||||
* @param mixed $condition
|
||||
* @param mixed $value The string to be echoed if the condition is true
|
||||
* @param mixed $alternative An alternative string which should be echoed when the condition is false
|
||||
*/
|
||||
function e($condition, $value, $alternative = null)
|
||||
{
|
||||
echo r($condition, $value, $alternative);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape context specific output
|
||||
*
|
||||
* @param string $string Untrusted data
|
||||
* @param string $context Location of output
|
||||
* @param boolean $strict Whether to escape an extended set of characters (HTML attributes only)
|
||||
* @return string Escaped data
|
||||
*/
|
||||
function esc($string, $context = 'html', $strict = false)
|
||||
{
|
||||
if (method_exists('Kirby\Toolkit\Escape', $context) === true) {
|
||||
return Escape::$context($string, $strict);
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shortcut for $kirby->request()->get()
|
||||
*
|
||||
* @param mixed $key The key to look for. Pass false or null to return the entire request array.
|
||||
* @param mixed $default Optional default value, which should be returned if no element has been found
|
||||
* @return mixed
|
||||
*/
|
||||
function get($key = null, $default = null)
|
||||
{
|
||||
return App::instance()->request()->get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Embeds a Github Gist
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $file
|
||||
* @return string
|
||||
*/
|
||||
function gist(string $url, string $file = null): string
|
||||
{
|
||||
return kirbytag([
|
||||
'gist' => $url,
|
||||
'file' => $file,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects to the given Urls
|
||||
* Urls can be relative or absolute.
|
||||
*
|
||||
* @param string $url
|
||||
* @param integer $code
|
||||
* @return void
|
||||
*/
|
||||
function go(string $url = null, int $code = 302)
|
||||
{
|
||||
die(Response::redirect($url, $code));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for html()
|
||||
*
|
||||
* @param string $text unencoded text
|
||||
* @param bool $keepTags
|
||||
* @return string
|
||||
*/
|
||||
function h(string $string = null, bool $keepTags = false)
|
||||
{
|
||||
return Html::encode($string, $keepTags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates safe html by encoding special characters
|
||||
*
|
||||
* @param string $text unencoded text
|
||||
* @param bool $keepTags
|
||||
* @return string
|
||||
*/
|
||||
function html(string $string = null, bool $keepTags = false)
|
||||
{
|
||||
return Html::encode($string, $keepTags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an image from any page
|
||||
* specified by the path
|
||||
*
|
||||
* Example:
|
||||
* <?= image('some/page/myimage.jpg') ?>
|
||||
*
|
||||
* @param string $path
|
||||
* @return File|null
|
||||
*/
|
||||
function image(string $path = null)
|
||||
{
|
||||
if ($path === null) {
|
||||
return page()->image();
|
||||
}
|
||||
|
||||
$uri = dirname($path);
|
||||
$filename = basename($path);
|
||||
|
||||
if ($uri === '.') {
|
||||
$uri = null;
|
||||
}
|
||||
|
||||
$page = $uri === '/' ? site() : page($uri);
|
||||
|
||||
if ($page) {
|
||||
return $page->image($filename);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a number of validators on a set of data and checks if the data is invalid
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $rules
|
||||
* @param array $messages
|
||||
* @return false|array
|
||||
*/
|
||||
function invalid(array $data = [], array $rules = [], array $messages = [])
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
foreach ($rules as $field => $validations) {
|
||||
$validationIndex = -1;
|
||||
|
||||
// See: http://php.net/manual/en/types.comparisons.php
|
||||
// only false for: null, undefined variable, '', []
|
||||
$filled = isset($data[$field]) && $data[$field] !== '' && $data[$field] !== [];
|
||||
$message = $messages[$field] ?? $field;
|
||||
|
||||
// True if there is an error message for each validation method.
|
||||
$messageArray = is_array($message);
|
||||
|
||||
foreach ($validations as $method => $options) {
|
||||
if (is_numeric($method) === true) {
|
||||
$method = $options;
|
||||
}
|
||||
|
||||
$validationIndex++;
|
||||
|
||||
if ($method === 'required') {
|
||||
if ($filled) {
|
||||
// Field is required and filled.
|
||||
continue;
|
||||
}
|
||||
} elseif ($filled) {
|
||||
if (is_array($options) === false) {
|
||||
$options = [$options];
|
||||
}
|
||||
|
||||
array_unshift($options, $data[$field] ?? null);
|
||||
|
||||
if (V::$method(...$options) === true) {
|
||||
// Field is filled and passes validation method.
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// If a field is not required and not filled, no validation should be done.
|
||||
continue;
|
||||
}
|
||||
|
||||
// If no continue was called we have a failed validation.
|
||||
if ($messageArray) {
|
||||
$errors[$field][] = $message[$validationIndex] ?? $field;
|
||||
} else {
|
||||
$errors[$field] = $message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a script tag to load a javascript file
|
||||
*
|
||||
* @param string|array $src
|
||||
* @param string|array $options
|
||||
* @return void
|
||||
*/
|
||||
function js($url, $options = null)
|
||||
{
|
||||
if (is_array($url) === true) {
|
||||
$scripts = array_map(function ($url) use ($options) {
|
||||
return js($url, $options);
|
||||
}, $url);
|
||||
|
||||
return implode(PHP_EOL, $scripts);
|
||||
}
|
||||
|
||||
$src = $url === '@auto' ? Url::toTemplateAsset('js/templates', 'js') : Url::to($url);
|
||||
$attr = [
|
||||
'src' => $src,
|
||||
];
|
||||
|
||||
if (is_bool($options) === true) {
|
||||
$attr['async'] = $options;
|
||||
}
|
||||
|
||||
if (is_array($options) === true) {
|
||||
$attr = array_merge($options, $attr);
|
||||
}
|
||||
|
||||
return '<script ' . attr($attr) . '></script>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Kirby object in any situation
|
||||
*
|
||||
* @return App
|
||||
*/
|
||||
function kirby(): App
|
||||
{
|
||||
return App::instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes it possible to use any defined Kirbytag as standalone function
|
||||
*
|
||||
* @param string|array $type
|
||||
* @param string $value
|
||||
* @param array $attr
|
||||
* @return string
|
||||
*/
|
||||
function kirbytag($type, string $value = null, array $attr = []): string
|
||||
{
|
||||
if (is_array($type) === true) {
|
||||
return App::instance()->kirbytag(key($type), current($type), $type);
|
||||
}
|
||||
|
||||
return App::instance()->kirbytag($type, $value, $attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses KirbyTags in the given string. Shortcut
|
||||
* for `$kirby->kirbytags($text, $data)`
|
||||
*
|
||||
* @param string $text
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
function kirbytags(string $text = null, array $data = []): string
|
||||
{
|
||||
return App::instance()->kirbytags($text, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses KirbyTags and Markdown in the
|
||||
* given string. Shortcut for `$kirby->kirbytext()`
|
||||
*
|
||||
* @param string $text
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
function kirbytext(string $text = null, array $data = []): string
|
||||
{
|
||||
return App::instance()->kirbytext($text, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* A super simple class autoloader
|
||||
*
|
||||
* @param array $classmap
|
||||
* @param string $base
|
||||
* @return void
|
||||
*/
|
||||
function load(array $classmap, string $base = null)
|
||||
{
|
||||
spl_autoload_register(function ($class) use ($classmap, $base) {
|
||||
$class = strtolower($class);
|
||||
|
||||
if (!isset($classmap[$class])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($base) {
|
||||
include $base . '/' . $classmap[$class];
|
||||
} else {
|
||||
include $classmap[$class];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses markdown in the given string. Shortcut for
|
||||
* `$kirby->markdown($text)`
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
function markdown(string $text = null): string
|
||||
{
|
||||
return App::instance()->markdown($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for `$kirby->option($key, $default)`
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
function option(string $key, $default = null)
|
||||
{
|
||||
return App::instance()->option($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a single page or multiple pages by
|
||||
* id or the current page when no id is specified
|
||||
*
|
||||
* @param string|array ...$id
|
||||
* @return Page|null
|
||||
*/
|
||||
function page(...$id)
|
||||
{
|
||||
if (empty($id) === true) {
|
||||
return App::instance()->site()->page();
|
||||
}
|
||||
|
||||
return App::instance()->site()->find(...$id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to build page collections
|
||||
*
|
||||
* @param string|array ...$id
|
||||
* @return Pages
|
||||
*/
|
||||
function pages(...$id)
|
||||
{
|
||||
return App::instance()->site()->find(...$id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single param from the URL
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $fallback
|
||||
* @return string|null
|
||||
*/
|
||||
function param(string $key, string $fallback = null): ?string
|
||||
{
|
||||
return App::instance()->request()->url()->params()->$key ?? $fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all params from the current Url
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function params(): array
|
||||
{
|
||||
return App::instance()->request()->url()->params()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Smart version of return with an if condition as first argument
|
||||
*
|
||||
* @param mixed $condition
|
||||
* @param mixed $value The string to be returned if the condition is true
|
||||
* @param mixed $alternative An alternative string which should be returned when the condition is false
|
||||
* @return mixed
|
||||
*/
|
||||
function r($condition, $value, $alternative = null)
|
||||
{
|
||||
return $condition ? $value : $alternative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the minutes of the given date
|
||||
* by the defined step
|
||||
*
|
||||
* @param string $date
|
||||
* @param integer $step
|
||||
* @return string|null
|
||||
*/
|
||||
function timestamp(string $date = null, int $step = null): ?string
|
||||
{
|
||||
if (V::date($date) === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$date = strtotime($date);
|
||||
|
||||
if ($step === null) {
|
||||
return $date;
|
||||
}
|
||||
|
||||
$hours = date('H', $date);
|
||||
$minutes = date('i', $date);
|
||||
$minutes = floor($minutes / $step) * $step;
|
||||
$minutes = str_pad($minutes, 2, 0, STR_PAD_LEFT);
|
||||
$date = date('Y-m-d', $date) . ' ' . $hours . ':' . $minutes;
|
||||
|
||||
return strtotime($date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currrent site object
|
||||
*
|
||||
* @return Site
|
||||
*/
|
||||
function site()
|
||||
{
|
||||
return App::instance()->site();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the size/length of numbers, strings, arrays and countable objects
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return int
|
||||
*/
|
||||
function size($value): int
|
||||
{
|
||||
if (is_numeric($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
return Str::length(trim($value));
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
return count($value);
|
||||
}
|
||||
|
||||
if (is_object($value)) {
|
||||
if (is_a($value, 'Countable') === true) {
|
||||
return count($value);
|
||||
}
|
||||
|
||||
if (is_a($value, 'Kirby\Toolkit\Collection') === true) {
|
||||
return $value->count();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhances the given string with
|
||||
* smartypants. Shortcut for `$kirby->smartypants($text)`
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
function smartypants(string $text = null): string
|
||||
{
|
||||
return App::instance()->smartypants($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Embeds a snippet from the snippet folder
|
||||
*
|
||||
* @param string $name
|
||||
* @param array|object $data
|
||||
* @param boolean $return
|
||||
* @return string
|
||||
*/
|
||||
function snippet(string $name, $data = [], bool $return = false)
|
||||
{
|
||||
if (is_object($data) === true) {
|
||||
$data = ['item' => $data];
|
||||
}
|
||||
|
||||
$snippet = App::instance()->snippet($name, $data);
|
||||
|
||||
if ($return === true) {
|
||||
return $snippet;
|
||||
}
|
||||
|
||||
echo $snippet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes an SVG file by absolute or
|
||||
* relative file path.
|
||||
*
|
||||
* @param string $file
|
||||
* @return string
|
||||
*/
|
||||
function svg(string $file)
|
||||
{
|
||||
$root = App::instance()->root();
|
||||
$file = $root . '/' . $file;
|
||||
|
||||
if (file_exists($file) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
include F::realpath($file, $root);
|
||||
$svg = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
return $svg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns translate string for key from translation file
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param string|null $fallback
|
||||
* @return mixed
|
||||
*/
|
||||
function t($key, string $fallback = null)
|
||||
{
|
||||
return I18n::translate($key, $fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a count
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param int $count
|
||||
* @return mixed
|
||||
*/
|
||||
function tc($key, int $count)
|
||||
{
|
||||
return I18n::translateCount($key, $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Twitter link
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $text
|
||||
* @param string $title
|
||||
* @param string $class
|
||||
* @return string
|
||||
*/
|
||||
function twitter(string $username, string $text = null, string $title = null, string $class = null): string
|
||||
{
|
||||
return kirbytag([
|
||||
'twitter' => $username,
|
||||
'text' => $text,
|
||||
'title' => $title,
|
||||
'class' => $class
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for url()
|
||||
*
|
||||
* @param string $path
|
||||
* @param array|null $options
|
||||
* @return string
|
||||
*/
|
||||
function u(string $path = null, $options = null): string
|
||||
{
|
||||
return Url::to($path, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an absolute URL for a given path
|
||||
*
|
||||
* @param string $path
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
function url(string $path = null, $options = null): string
|
||||
{
|
||||
return Url::to($path, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a video embed via iframe for Youtube or Vimeo
|
||||
* videos. The embed Urls are automatically detected from
|
||||
* the given Url.
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @param array $attr
|
||||
* @return string
|
||||
*/
|
||||
function video(string $url, array $options = [], array $attr = []): string
|
||||
{
|
||||
return Html::video($url, $options, $attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Embeds a Vimeo video by URL in an iframe
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @param array $attr
|
||||
* @return string
|
||||
*/
|
||||
function vimeo(string $url, array $options = [], array $attr = []): string
|
||||
{
|
||||
return Html::video($url, $options, $attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* The widont function makes sure that there are no
|
||||
* typographical widows at the end of a paragraph –
|
||||
* that's a single word in the last line
|
||||
*
|
||||
* @param string|null $string
|
||||
* @return string
|
||||
*/
|
||||
function widont(string $string = null): string
|
||||
{
|
||||
return Str::widont($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Embeds a Youtube video by URL in an iframe
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @param array $attr
|
||||
* @return string
|
||||
*/
|
||||
function youtube(string $url, array $options = [], array $attr = []): string
|
||||
{
|
||||
return Html::video($url, $options, $attr);
|
||||
}
|
389
kirby/config/methods.php
Executable file
389
kirby/config/methods.php
Executable file
@@ -0,0 +1,389 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Field;
|
||||
use Kirby\Cms\File;
|
||||
use Kirby\Cms\Files;
|
||||
use Kirby\Cms\Html;
|
||||
use Kirby\Cms\Structure;
|
||||
use Kirby\Cms\Page;
|
||||
use Kirby\Cms\Url;
|
||||
use Kirby\Data\Json;
|
||||
use Kirby\Data\Yaml;
|
||||
use Kirby\Toolkit\Str;
|
||||
use Kirby\Toolkit\V;
|
||||
use Kirby\Toolkit\Xml;
|
||||
|
||||
/**
|
||||
* Field method setup
|
||||
*/
|
||||
return function (App $app) {
|
||||
return [
|
||||
|
||||
// states
|
||||
|
||||
/**
|
||||
* Converts the field value into a proper boolean and inverts it
|
||||
*
|
||||
* @param Field $field
|
||||
* @return boolean
|
||||
*/
|
||||
'isFalse' => function (Field $field): bool {
|
||||
return $field->toBool() === false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the field value into a proper boolean
|
||||
*
|
||||
* @param Field $field
|
||||
* @return boolean
|
||||
*/
|
||||
'isTrue' => function (Field $field): bool {
|
||||
return $field->toBool() === true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Validates the field content with the given validator and parameters
|
||||
*
|
||||
* @param string $validator
|
||||
* @param mixed[] ...$arguments A list of optional validator arguments
|
||||
* @return boolean
|
||||
*/
|
||||
'isValid' => function (Field $field, string $validator, ...$arguments): bool {
|
||||
return V::$validator($field->value, ...$arguments);
|
||||
},
|
||||
|
||||
// converters
|
||||
|
||||
/**
|
||||
* Parses the field value with the given method
|
||||
*
|
||||
* @param Field $field
|
||||
* @param string $method [',', 'yaml', 'json']
|
||||
* @return array
|
||||
*/
|
||||
'toData' => function (Field $field, string $method = ',') {
|
||||
switch ($method) {
|
||||
case 'yaml':
|
||||
return Yaml::decode($field->value);
|
||||
case 'json':
|
||||
return Json::decode($field->value);
|
||||
default:
|
||||
return $field->split($method);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the field value into a proper boolean
|
||||
*
|
||||
* @param Field $field
|
||||
* @param bool $default Default value if the field is empty
|
||||
* @return bool
|
||||
*/
|
||||
'toBool' => function (Field $field, $default = false): bool {
|
||||
$value = $field->isEmpty() ? $default : $field->value;
|
||||
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the field value to a timestamp or a formatted date
|
||||
*
|
||||
* @param Field $field
|
||||
* @param string $format PHP date formatting string
|
||||
* @return string|int
|
||||
*/
|
||||
'toDate' => function (Field $field, string $format = null) use ($app) {
|
||||
if (empty($field->value) === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($format === null) {
|
||||
return $field->toTimestamp();
|
||||
}
|
||||
|
||||
return $app->option('date.handler', 'date')($format, $field->toTimestamp());
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a file object from a filename in the field
|
||||
*
|
||||
* @param Field $field
|
||||
* @return File|null
|
||||
*/
|
||||
'toFile' => function (Field $field) {
|
||||
return $field->toFiles()->first();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a file collection from a yaml list of filenames in the field
|
||||
*
|
||||
* @return Files
|
||||
*/
|
||||
'toFiles' => function (Field $field) {
|
||||
return $field->parent()->files()->find(false, false, ...$field->toData('yaml'));
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the field value into a proper float
|
||||
*
|
||||
* @param Field $field
|
||||
* @param float $default Default value if the field is empty
|
||||
* @return float
|
||||
*/
|
||||
'toFloat' => function (Field $field, float $default = 0) {
|
||||
$value = $field->isEmpty() ? $default : $field->value;
|
||||
return floatval($value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the field value into a proper integer
|
||||
*
|
||||
* @param Field $field
|
||||
* @param int $default Default value if the field is empty
|
||||
* @return int
|
||||
*/
|
||||
'toInt' => function (Field $field, int $default = 0) {
|
||||
$value = $field->isEmpty() ? $default : $field->value;
|
||||
return intval($value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wraps a link tag around the field value. The field value is used as the link text
|
||||
*
|
||||
* @param Field $field
|
||||
* @param mixed $attr1 Can be an optional Url. If no Url is set, the Url of the Page, File or Site will be used. Can also be an array of link attributes
|
||||
* @param mixed $attr2 If `$attr1` is used to set the Url, you can use `$attr2` to pass an array of additional attributes.
|
||||
* @return string
|
||||
*/
|
||||
'toLink' => function (Field $field, $attr1 = null, $attr2 = null) {
|
||||
if (is_string($attr1) === true) {
|
||||
$href = $attr1;
|
||||
$attr = $attr2;
|
||||
} else {
|
||||
$href = $field->parent()->url();
|
||||
$attr = $attr1;
|
||||
}
|
||||
|
||||
if ($field->parent()->isActive()) {
|
||||
$attr['aria-current'] = 'page';
|
||||
}
|
||||
|
||||
return Html::a($href, $field->value, $attr ?? []);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a page object from a page id in the field
|
||||
*
|
||||
* @param Field $field
|
||||
* @return Page|null
|
||||
*/
|
||||
'toPage' => function (Field $field) use ($app) {
|
||||
return $field->toPages()->first();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a pages collection from a yaml list of page ids in the field
|
||||
*
|
||||
* @param string $separator Can be any other separator to split the field value by
|
||||
* @return Pages
|
||||
*/
|
||||
'toPages' => function (Field $field, string $separator = 'yaml') use ($app) {
|
||||
return $app->site()->find(false, false, ...$field->toData($separator));
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a yaml field to a Structure object
|
||||
*/
|
||||
'toStructure' => function (Field $field) {
|
||||
return new Structure(Yaml::decode($field->value), $field->parent());
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the field value to a Unix timestamp
|
||||
*/
|
||||
'toTimestamp' => function (Field $field) {
|
||||
return strtotime($field->value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Turns the field value into an absolute Url
|
||||
*/
|
||||
'toUrl' => function (Field $field) {
|
||||
return Url::to($field->value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a user email address to a user object
|
||||
*
|
||||
* @return User|null
|
||||
*/
|
||||
'toUser' => function (Field $field) use ($app) {
|
||||
return $field->toUsers()->first();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a users collection from a yaml list of user email addresses in the field
|
||||
*
|
||||
* @return Users
|
||||
*/
|
||||
'toUsers' => function (Field $field) use ($app) {
|
||||
return $app->users()->find(false, false, ...$field->toData('yaml'));
|
||||
},
|
||||
|
||||
// inspectors
|
||||
|
||||
/**
|
||||
* Returns the length of the field content
|
||||
*/
|
||||
'length' => function (Field $field) {
|
||||
return Str::length($field->value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the number of words in the text
|
||||
*/
|
||||
'words' => function (Field $field) {
|
||||
return str_word_count(strip_tags($field->value));
|
||||
},
|
||||
|
||||
// manipulators
|
||||
|
||||
/**
|
||||
* Escapes the field value to be safely used in HTML
|
||||
* templates without the risk of XSS attacks
|
||||
*
|
||||
* @param Field $field
|
||||
* @param string $context html, attr, js or css
|
||||
*/
|
||||
'escape' => function (Field $field, string $context = 'html') {
|
||||
$field->value = esc($field->value, $context);
|
||||
return $field;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates an excerpt of the field value without html
|
||||
* or any other formatting.
|
||||
*/
|
||||
'excerpt' => function (Field $field, int $chars = 0, bool $strip = true, string $rep = '…') {
|
||||
$field->value = Str::excerpt($field->kirbytext()->value(), $chars, $strip, $rep);
|
||||
return $field;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the field content to valid HTML
|
||||
*/
|
||||
'html' => function (Field $field) {
|
||||
$field->value = htmlentities($field->value, ENT_COMPAT, 'utf-8');
|
||||
return $field;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the field content from Markdown/Kirbytext to valid HTML
|
||||
*/
|
||||
'kirbytext' => function (Field $field) use ($app) {
|
||||
$field->value = $app->kirbytext($field->value, [
|
||||
'parent' => $field->parent(),
|
||||
'field' => $field
|
||||
]);
|
||||
|
||||
return $field;
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses all KirbyTags without also parsing Markdown
|
||||
*/
|
||||
'kirbytags' => function (Field $field) use ($app) {
|
||||
$field->value = $app->kirbytags($field->value, [
|
||||
'parent' => $field->parent(),
|
||||
'field' => $field
|
||||
]);
|
||||
|
||||
return $field;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the field content to lowercase
|
||||
*/
|
||||
'lower' => function (Field $field) {
|
||||
$field->value = Str::lower($field->value);
|
||||
return $field;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts markdown to valid HTML
|
||||
*/
|
||||
'markdown' => function (Field $field) use ($app) {
|
||||
$field->value = $app->markdown($field->value);
|
||||
return $field;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the field content to valid XML
|
||||
*/
|
||||
'xml' => function (Field $field) {
|
||||
$field->value = Xml::encode($field->value);
|
||||
return $field;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cuts the string after the given length and adds "…" if it is longer
|
||||
*
|
||||
* @param int $length The number of characters in the string
|
||||
* @param string $appendix An optional replacement for the missing rest
|
||||
* @return Field
|
||||
*/
|
||||
'short' => function (Field $field, int $length, string $appendix = '…') {
|
||||
$field->value = Str::short($field->value, $length, $appendix);
|
||||
return $field;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the field content to a slug
|
||||
*/
|
||||
'slug' => function (Field $field) {
|
||||
$field->value = Str::slug($field->value);
|
||||
return $field;
|
||||
},
|
||||
|
||||
/**
|
||||
* Applies SmartyPants to the field
|
||||
*/
|
||||
'smartypants' => function (Field $field) use ($app) {
|
||||
$field->value = $app->smartypants($field->value);
|
||||
return $field;
|
||||
},
|
||||
|
||||
/**
|
||||
* Splits the field content into an array
|
||||
*/
|
||||
'split' => function (Field $field, $separator = ',') {
|
||||
return Str::split((string)$field->value, $separator);
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the field content to uppercase
|
||||
*/
|
||||
'upper' => function (Field $field) {
|
||||
$field->value = Str::upper($field->value);
|
||||
return $field;
|
||||
},
|
||||
|
||||
/**
|
||||
* Avoids typographical widows in strings by replacing the last space with
|
||||
*/
|
||||
'widont' => function (Field $field) {
|
||||
$field->value = Str::widont($field->value);
|
||||
return $field;
|
||||
},
|
||||
|
||||
// aliases
|
||||
|
||||
/**
|
||||
* Parses yaml in the field content and returns an array
|
||||
*/
|
||||
'yaml' => function (Field $field): array {
|
||||
return $field->toData('yaml');
|
||||
},
|
||||
|
||||
];
|
||||
};
|
14
kirby/config/presets/files.php
Executable file
14
kirby/config/presets/files.php
Executable file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
return function (array $props) {
|
||||
$props['sections'] = [
|
||||
'files' => [
|
||||
'headline' => $props['headline'] ?? t('files'),
|
||||
'type' => 'files',
|
||||
'layout' => $props['layout'] ?? 'cards',
|
||||
'info' => '{{ file.dimensions }}'
|
||||
]
|
||||
];
|
||||
|
||||
return $props;
|
||||
};
|
72
kirby/config/presets/page.php
Executable file
72
kirby/config/presets/page.php
Executable file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
return function ($props) {
|
||||
$section = function ($defaults, $props) {
|
||||
if ($props === true) {
|
||||
$props = [];
|
||||
}
|
||||
|
||||
if (is_string($props) === true) {
|
||||
$props = [
|
||||
'headline' => $props
|
||||
];
|
||||
}
|
||||
|
||||
return array_replace_recursive($defaults, $props);
|
||||
};
|
||||
|
||||
if (empty($props['sidebar']) === false) {
|
||||
$sidebar = $props['sidebar'];
|
||||
} else {
|
||||
$sidebar = [];
|
||||
|
||||
$pages = $props['pages'] ?? [];
|
||||
$files = $props['files'] ?? [];
|
||||
|
||||
if ($pages !== false) {
|
||||
$sidebar['pages'] = $section([
|
||||
'headline' => t('pages'),
|
||||
'type' => 'pages',
|
||||
'status' => 'all',
|
||||
'layout' => 'list',
|
||||
], $pages);
|
||||
}
|
||||
|
||||
if ($files !== false) {
|
||||
$sidebar['files'] = $section([
|
||||
'headline' => t('files'),
|
||||
'type' => 'files',
|
||||
'layout' => 'list'
|
||||
], $files);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($sidebar) === true) {
|
||||
$props['fields'] = $props['fields'] ?? [];
|
||||
|
||||
unset(
|
||||
$props['files'],
|
||||
$props['pages']
|
||||
);
|
||||
} else {
|
||||
$props['columns'] = [
|
||||
[
|
||||
'width' => '2/3',
|
||||
'fields' => $props['fields'] ?? []
|
||||
],
|
||||
[
|
||||
'width' => '1/3',
|
||||
'sections' => $sidebar
|
||||
],
|
||||
];
|
||||
|
||||
unset(
|
||||
$props['fields'],
|
||||
$props['files'],
|
||||
$props['pages'],
|
||||
$props['sidebar']
|
||||
);
|
||||
}
|
||||
|
||||
return $props;
|
||||
};
|
57
kirby/config/presets/pages.php
Executable file
57
kirby/config/presets/pages.php
Executable file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
return function (array $props) {
|
||||
|
||||
// load the general templates setting for all sections
|
||||
$templates = $props['templates'] ?? null;
|
||||
|
||||
$section = function ($headline, $status, $props) use ($templates) {
|
||||
$defaults = [
|
||||
'headline' => $headline,
|
||||
'type' => 'pages',
|
||||
'layout' => 'list',
|
||||
'status' => $status
|
||||
];
|
||||
|
||||
if ($props === true) {
|
||||
$props = [];
|
||||
}
|
||||
|
||||
if (is_string($props) === true) {
|
||||
$props = [
|
||||
'headline' => $props
|
||||
];
|
||||
}
|
||||
|
||||
// inject the global templates definition
|
||||
if (empty($templates) === false) {
|
||||
$props['templates'] = $props['templates'] ?? $templates;
|
||||
}
|
||||
|
||||
return array_replace_recursive($defaults, $props);
|
||||
};
|
||||
|
||||
$sections = [];
|
||||
|
||||
$drafts = $props['drafts'] ?? [];
|
||||
$unlisted = $props['unlisted'] ?? false;
|
||||
$listed = $props['listed'] ?? [];
|
||||
|
||||
|
||||
if ($drafts !== false) {
|
||||
$sections['drafts'] = $section(t('pages.status.draft'), 'drafts', $drafts);
|
||||
}
|
||||
|
||||
if ($unlisted !== false) {
|
||||
$sections['unlisted'] = $section(t('pages.status.unlisted'), 'unlisted', $unlisted);
|
||||
}
|
||||
|
||||
if ($listed !== false) {
|
||||
$sections['listed'] = $section(t('pages.status.listed'), 'listed', $listed);
|
||||
}
|
||||
|
||||
// cleaning up
|
||||
unset($props['drafts'], $props['unlisted'], $props['listed'], $props['templates']);
|
||||
|
||||
return array_merge($props, ['sections' => $sections]);
|
||||
};
|
85
kirby/config/roots.php
Executable file
85
kirby/config/roots.php
Executable file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// kirby
|
||||
'kirby' => function (array $roots) {
|
||||
return realpath(__DIR__ . '/../');
|
||||
},
|
||||
'translations' => function (array $roots) {
|
||||
return $roots['kirby'] . '/translations';
|
||||
},
|
||||
|
||||
// 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';
|
||||
},
|
||||
'emails' => function (array $roots) {
|
||||
return $roots['site'] . '/emails';
|
||||
},
|
||||
'languages' => function (array $roots) {
|
||||
return $roots['site'] . '/languages';
|
||||
},
|
||||
'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';
|
||||
},
|
||||
];
|
200
kirby/config/routes.php
Executable file
200
kirby/config/routes.php
Executable file
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Api\Api;
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Media;
|
||||
use Kirby\Cms\Panel;
|
||||
use Kirby\Cms\PluginAssets;
|
||||
use Kirby\Cms\Response;
|
||||
use Kirby\Exception\NotFoundException;
|
||||
use Kirby\Http\Response\Redirect;
|
||||
use Kirby\Http\Router\Route;
|
||||
use Kirby\Toolkit\F;
|
||||
use Kirby\Toolkit\Str;
|
||||
use Kirby\Toolkit\View;
|
||||
|
||||
return function ($kirby) {
|
||||
$api = $kirby->option('api.slug', 'api');
|
||||
$panel = $kirby->option('panel.slug', 'panel');
|
||||
|
||||
/**
|
||||
* Before routes are running before the
|
||||
* plugin routes and cannot be overwritten by
|
||||
* plugins.
|
||||
*/
|
||||
$before = [
|
||||
[
|
||||
'pattern' => $api . '/(:all)',
|
||||
'method' => 'ALL',
|
||||
'env' => 'api',
|
||||
'action' => function ($path = null) use ($kirby) {
|
||||
if ($kirby->option('api') === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$request = $kirby->request();
|
||||
|
||||
return $kirby->api()->render($path, $this->method(), [
|
||||
'body' => $request->body()->toArray(),
|
||||
'files' => $request->files()->toArray(),
|
||||
'headers' => $request->headers(),
|
||||
'query' => $request->query()->toArray(),
|
||||
]);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'media/plugins/index.(css|js)',
|
||||
'env' => 'media',
|
||||
'action' => function (string $extension) use ($kirby) {
|
||||
return $kirby
|
||||
->response()
|
||||
->type($extension)
|
||||
->body(PluginAssets::index($extension));
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'media/plugins/(:any)/(:any)/(:all).(css|gif|js|jpg|png|svg|webp|woff2|woff)',
|
||||
'env' => 'media',
|
||||
'action' => function (string $provider, string $pluginName, string $filename, string $extension) use ($kirby) {
|
||||
if ($url = PluginAssets::resolve($provider . '/' . $pluginName, $filename . '.' . $extension)) {
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($url, 307);
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'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',
|
||||
'action' => function ($path, $hash, $filename) use ($kirby) {
|
||||
return Media::link($kirby->page($path), $hash, $filename);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'media/site/(:any)/(:any)',
|
||||
'env' => 'media',
|
||||
'action' => function ($hash, $filename) use ($kirby) {
|
||||
return Media::link($kirby->site(), $hash, $filename);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'media/users/(:any)/(:any)/(:any)',
|
||||
'env' => 'media',
|
||||
'action' => function ($id, $hash, $filename) use ($kirby) {
|
||||
return Media::link($kirby->user($id), $hash, $filename);
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
// Multi-language setup
|
||||
if ($kirby->multilang() === true) {
|
||||
|
||||
// Multi-language home
|
||||
$after[] = [
|
||||
'pattern' => '',
|
||||
'method' => 'ALL',
|
||||
'env' => 'site',
|
||||
'action' => function () use ($kirby) {
|
||||
$home = $kirby->site()->homePage();
|
||||
|
||||
if ($kirby->url() !== $home->url()) {
|
||||
if ($kirby->option('languages.detect') === true) {
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($kirby->detectedLanguage()->url());
|
||||
} else {
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($kirby->site()->url());
|
||||
}
|
||||
} else {
|
||||
return $home;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
foreach ($kirby->languages() as $language) {
|
||||
$after[] = [
|
||||
'pattern' => trim($language->pattern() . '/(:all?)', '/'),
|
||||
'method' => 'ALL',
|
||||
'env' => 'site',
|
||||
'action' => function ($path = null) use ($kirby, $language) {
|
||||
return $kirby->resolve($path, $language->code());
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
// fallback route for unprefixed default language URLs.
|
||||
$after[] = [
|
||||
'pattern' => '(:all)',
|
||||
'method' => 'ALL',
|
||||
'env' => 'site',
|
||||
'action' => function (string $path) use ($kirby) {
|
||||
if ($page = $kirby->page($path)) {
|
||||
$url = $kirby->request()->url([
|
||||
'query' => null,
|
||||
'params' => null,
|
||||
'fragment' => null
|
||||
]);
|
||||
|
||||
if ($url->toString() !== $page->url()) {
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($page->url());
|
||||
}
|
||||
|
||||
return $kirby->resolve($path, $kirby->defaultLanguage()->code());
|
||||
}
|
||||
}
|
||||
];
|
||||
} else {
|
||||
|
||||
// Single-language home
|
||||
$after[] = [
|
||||
'pattern' => '',
|
||||
'method' => 'ALL',
|
||||
'env' => 'site',
|
||||
'action' => function () use ($kirby) {
|
||||
return $kirby->site()->homePage();
|
||||
}
|
||||
];
|
||||
|
||||
// redirect the home page folder to the real homepage
|
||||
$after[] = [
|
||||
'pattern' => $kirby->option('home', 'home'),
|
||||
'method' => 'ALL',
|
||||
'env' => 'site',
|
||||
'action' => function () use ($kirby) {
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($kirby->site()->url());
|
||||
}
|
||||
];
|
||||
|
||||
// Single-language subpages
|
||||
$after[] = [
|
||||
'pattern' => '(:all)',
|
||||
'method' => 'ALL',
|
||||
'env' => 'site',
|
||||
'action' => function (string $path) use ($kirby) {
|
||||
return $kirby->resolve($path);
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'before' => $before,
|
||||
'after' => $after
|
||||
];
|
||||
};
|
66
kirby/config/sections/fields.php
Executable file
66
kirby/config/sections/fields.php
Executable file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\Form;
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
'fields' => function (array $fields = []) {
|
||||
return $fields;
|
||||
}
|
||||
],
|
||||
'computed' => [
|
||||
'form' => function () {
|
||||
$fields = $this->fields;
|
||||
$disabled = $this->model->permissions()->update() === false;
|
||||
$content = $this->model->content()->toArray();
|
||||
|
||||
if ($disabled === true) {
|
||||
foreach ($fields as $key => $props) {
|
||||
$fields[$key]['disabled'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return new Form([
|
||||
'fields' => $fields,
|
||||
'values' => $content,
|
||||
'model' => $this->model,
|
||||
'strict' => true
|
||||
]);
|
||||
},
|
||||
'fields' => function () {
|
||||
$fields = $this->form->fields()->toArray();
|
||||
|
||||
if (is_a($this->model, 'Kirby\Cms\Page') === true || is_a($this->model, 'Kirby\Cms\Site') === true) {
|
||||
// the title should never be updated directly via
|
||||
// fields section to avoid conflicts with the rename dialog
|
||||
unset($fields['title']);
|
||||
}
|
||||
|
||||
foreach ($fields as $index => $props) {
|
||||
unset($fields[$index]['value']);
|
||||
}
|
||||
|
||||
return $fields;
|
||||
},
|
||||
'errors' => function () {
|
||||
return $this->form->errors();
|
||||
},
|
||||
'data' => function () {
|
||||
$values = $this->form->values();
|
||||
|
||||
if (is_a($this->model, 'Kirby\Cms\Page') === true || is_a($this->model, 'Kirby\Cms\Site') === true) {
|
||||
// the title should never be updated directly via
|
||||
// fields section to avoid conflicts with the rename dialog
|
||||
unset($values['title']);
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
],
|
||||
'toArray' => function () {
|
||||
return [
|
||||
'errors' => $this->errors,
|
||||
'fields' => $this->fields,
|
||||
];
|
||||
}
|
||||
];
|
229
kirby/config/sections/files.php
Executable file
229
kirby/config/sections/files.php
Executable file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\File;
|
||||
use Kirby\Toolkit\A;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
return [
|
||||
'mixins' => [
|
||||
'empty',
|
||||
'headline',
|
||||
'layout',
|
||||
'min',
|
||||
'max',
|
||||
'pagination',
|
||||
'parent',
|
||||
],
|
||||
'props' => [
|
||||
/**
|
||||
* Image options to control the source and look of file previews
|
||||
*/
|
||||
'image' => function ($image = null) {
|
||||
return $image ?? [];
|
||||
},
|
||||
/**
|
||||
* Optional info text setup. Info text is shown on the right (lists) or below (cards) the filename.
|
||||
*/
|
||||
'info' => function (string $info = null) {
|
||||
return $info;
|
||||
},
|
||||
/**
|
||||
* The size option controls the size of cards. By default cards are auto-sized and the cards grid will always fill the full width. With a size you can disable auto-sizing. Available sizes: tiny, small, medium, large
|
||||
*/
|
||||
'size' => function (string $size = 'auto') {
|
||||
return $size;
|
||||
},
|
||||
/**
|
||||
* Enables/disables manual sorting
|
||||
*/
|
||||
'sortable' => function (bool $sortable = true) {
|
||||
return $sortable;
|
||||
},
|
||||
/**
|
||||
* Overwrites manual sorting and sorts by the given field and sorting direction (i.e. filename desc)
|
||||
*/
|
||||
'sortBy' => function (string $sortBy = null) {
|
||||
return $sortBy;
|
||||
},
|
||||
/**
|
||||
* Filters all files by template and also sets the template, which will be used for all uploads
|
||||
*/
|
||||
'template' => function (string $template = null) {
|
||||
return $template;
|
||||
},
|
||||
/**
|
||||
* Setup for the main text in the list or cards. By default this will display the filename.
|
||||
*/
|
||||
'text' => function (string $text = '{{ file.filename }}') {
|
||||
return $text;
|
||||
}
|
||||
],
|
||||
'computed' => [
|
||||
'accept' => function () {
|
||||
if ($this->template) {
|
||||
$file = new File([
|
||||
'filename' => 'tmp',
|
||||
'template' => $this->template
|
||||
]);
|
||||
|
||||
return $file->blueprint()->accept()['mime'] ?? '*';
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
'dragTextType' => function () {
|
||||
return (option('panel')['kirbytext'] ?? true) ? 'kirbytext' : 'markdown';
|
||||
},
|
||||
'parent' => function () {
|
||||
return $this->parentModel();
|
||||
},
|
||||
'files' => function () {
|
||||
$files = $this->parent->files()->template($this->template);
|
||||
|
||||
if ($this->sortBy) {
|
||||
$files = $files->sortBy(...Str::split($this->sortBy, ' '));
|
||||
} elseif ($this->sortable === true) {
|
||||
$files = $files->sortBy('sort', 'asc');
|
||||
}
|
||||
|
||||
// apply the default pagination
|
||||
$files = $files->paginate([
|
||||
'page' => $this->page,
|
||||
'limit' => $this->limit
|
||||
]);
|
||||
|
||||
return $files;
|
||||
},
|
||||
'data' => function () {
|
||||
$data = [];
|
||||
|
||||
if ($this->layout === 'list') {
|
||||
$thumb = [
|
||||
'width' => 100,
|
||||
'height' => 100
|
||||
];
|
||||
} else {
|
||||
$thumb = [
|
||||
'width' => 400,
|
||||
'height' => 400
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($this->files as $file) {
|
||||
$image = $file->panelImage($this->image, $thumb);
|
||||
|
||||
$data[] = [
|
||||
'dragText' => $file->dragText($this->dragTextType),
|
||||
'filename' => $file->filename(),
|
||||
'id' => $file->id(),
|
||||
'text' => $file->toString($this->text),
|
||||
'info' => $file->toString($this->info ?? false),
|
||||
'icon' => $file->panelIcon($image),
|
||||
'image' => $image,
|
||||
'link' => $file->panelUrl(true),
|
||||
'parent' => $file->parent()->panelPath(),
|
||||
'url' => $file->url(),
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
},
|
||||
'total' => function () {
|
||||
return $this->files->pagination()->total();
|
||||
},
|
||||
'errors' => function () {
|
||||
$errors = [];
|
||||
|
||||
if ($this->validateMax() === false) {
|
||||
$errors['max'] = I18n::template('error.section.files.max.' . I18n::form($this->max), [
|
||||
'max' => $this->max,
|
||||
'section' => $this->headline
|
||||
]);
|
||||
}
|
||||
|
||||
if ($this->validateMin() === false) {
|
||||
$errors['min'] = I18n::template('error.section.files.min.' . I18n::form($this->min), [
|
||||
'min' => $this->min,
|
||||
'section' => $this->headline
|
||||
]);
|
||||
}
|
||||
|
||||
if (empty($errors) === true) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
$this->name => [
|
||||
'label' => $this->headline,
|
||||
'message' => $errors,
|
||||
]
|
||||
];
|
||||
},
|
||||
'link' => function () {
|
||||
$modelLink = $this->model->panelUrl(true);
|
||||
$parentLink = $this->parent->panelUrl(true);
|
||||
|
||||
if ($modelLink !== $parentLink) {
|
||||
return $parentLink;
|
||||
}
|
||||
},
|
||||
'pagination' => function () {
|
||||
return $this->pagination();
|
||||
},
|
||||
'sortable' => function () {
|
||||
if ($this->sortable === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->sortBy !== null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
'upload' => function () {
|
||||
if ($this->isFull() === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// count all uploaded files
|
||||
$total = count($this->data);
|
||||
$max = $this->max ? $this->max - $total : null;
|
||||
|
||||
if ($this->max && $total === $this->max - 1) {
|
||||
$multiple = false;
|
||||
} else {
|
||||
$multiple = true;
|
||||
}
|
||||
|
||||
return [
|
||||
'accept' => $this->accept,
|
||||
'multiple' => $multiple,
|
||||
'max' => $max,
|
||||
'api' => $this->parent->apiUrl(true) . '/files',
|
||||
'attributes' => array_filter([
|
||||
'template' => $this->template
|
||||
])
|
||||
];
|
||||
}
|
||||
],
|
||||
'toArray' => function () {
|
||||
return [
|
||||
'data' => $this->data,
|
||||
'errors' => $this->errors,
|
||||
'options' => [
|
||||
'accept' => $this->accept,
|
||||
'empty' => $this->empty,
|
||||
'headline' => $this->headline,
|
||||
'layout' => $this->layout,
|
||||
'link' => $this->link,
|
||||
'max' => $this->max,
|
||||
'min' => $this->min,
|
||||
'size' => $this->size,
|
||||
'sortable' => $this->sortable,
|
||||
'upload' => $this->upload
|
||||
],
|
||||
'pagination' => $this->pagination
|
||||
];
|
||||
}
|
||||
];
|
36
kirby/config/sections/info.php
Executable file
36
kirby/config/sections/info.php
Executable file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Toolkit\I18n;
|
||||
|
||||
return [
|
||||
'mixins' => [
|
||||
'headline'
|
||||
],
|
||||
'props' => [
|
||||
'text' => function ($text = null) {
|
||||
return I18n::translate($text, $text);
|
||||
},
|
||||
'theme' => function (string $theme = null) {
|
||||
return $theme;
|
||||
}
|
||||
],
|
||||
'computed' => [
|
||||
'text' => function () {
|
||||
if ($this->text) {
|
||||
$text = $this->model()->toString($this->text);
|
||||
$text = $this->kirby()->kirbytext($text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
},
|
||||
],
|
||||
'toArray' => function () {
|
||||
return [
|
||||
'options' => [
|
||||
'headline' => $this->headline,
|
||||
'text' => $this->text,
|
||||
'theme' => $this->theme
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
28
kirby/config/sections/mixins/empty.php
Executable file
28
kirby/config/sections/mixins/empty.php
Executable file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Sets the text for the empty state box
|
||||
*/
|
||||
'empty' => function (string $empty = null) {
|
||||
return I18n::translate($empty);
|
||||
}
|
||||
],
|
||||
'methods' => [
|
||||
'isFull' => function () {
|
||||
if ($this->max) {
|
||||
return $this->total >= $this->max;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
'validateMax' => function () {
|
||||
if ($this->max && $this->max < $this->total) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
]
|
||||
];
|
19
kirby/config/sections/mixins/headline.php
Executable file
19
kirby/config/sections/mixins/headline.php
Executable file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Toolkit\I18n;
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* The headline for the section. This can be a simple string or a template with additional info from the parent page.
|
||||
*/
|
||||
'headline' => function ($headline = null) {
|
||||
return I18n::translate($headline, $headline);
|
||||
}
|
||||
],
|
||||
'computed' => [
|
||||
'headline' => function () {
|
||||
return $this->headline ?? ucfirst($this->name);
|
||||
}
|
||||
]
|
||||
];
|
12
kirby/config/sections/mixins/layout.php
Executable file
12
kirby/config/sections/mixins/layout.php
Executable file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Section layout. Available layout methods: list, cards.
|
||||
*/
|
||||
'layout' => function (string $layout = 'list') {
|
||||
return $layout === 'cards' ? 'cards' : 'list';
|
||||
}
|
||||
]
|
||||
];
|
28
kirby/config/sections/mixins/max.php
Executable file
28
kirby/config/sections/mixins/max.php
Executable file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Sets the maximum number of allowed entries in the section
|
||||
*/
|
||||
'max' => function (int $max = null) {
|
||||
return $max;
|
||||
}
|
||||
],
|
||||
'methods' => [
|
||||
'isFull' => function () {
|
||||
if ($this->max) {
|
||||
return $this->total >= $this->max;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
'validateMax' => function () {
|
||||
if ($this->max && $this->max < $this->total) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
]
|
||||
];
|
21
kirby/config/sections/mixins/min.php
Executable file
21
kirby/config/sections/mixins/min.php
Executable file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Sets the minimum number of required entries in the section
|
||||
*/
|
||||
'min' => function (int $min = null) {
|
||||
return $min;
|
||||
}
|
||||
],
|
||||
'methods' => [
|
||||
'validateMin' => function () {
|
||||
if ($this->min && $this->min > $this->total) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
]
|
||||
];
|
36
kirby/config/sections/mixins/pagination.php
Executable file
36
kirby/config/sections/mixins/pagination.php
Executable file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Toolkit\Pagination;
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Sets the number of items per page. If there are more items the pagination navigation will be shown at the bottom of the section.
|
||||
*/
|
||||
'limit' => function (int $limit = 20) {
|
||||
return $limit;
|
||||
},
|
||||
/**
|
||||
* Sets the default page for the pagination. This will overwrite default pagination.
|
||||
*/
|
||||
'page' => function (int $page = null) {
|
||||
return $page ?? get('page', 1);
|
||||
},
|
||||
],
|
||||
'methods' => [
|
||||
'pagination' => function () {
|
||||
$pagination = new Pagination([
|
||||
'limit' => $this->limit,
|
||||
'page' => $this->page,
|
||||
'total' => $this->total
|
||||
]);
|
||||
|
||||
return [
|
||||
'limit' => $pagination->limit(),
|
||||
'offset' => $pagination->offset(),
|
||||
'page' => $pagination->page(),
|
||||
'total' => $pagination->total(),
|
||||
];
|
||||
},
|
||||
]
|
||||
];
|
29
kirby/config/sections/mixins/parent.php
Executable file
29
kirby/config/sections/mixins/parent.php
Executable file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Sets the query to a parent to find items for the list
|
||||
*/
|
||||
'parent' => function (string $parent = null) {
|
||||
return $parent;
|
||||
}
|
||||
],
|
||||
'methods' => [
|
||||
'parentModel' => function () {
|
||||
$parent = $this->parent;
|
||||
|
||||
if (is_string($parent) === true) {
|
||||
$parent = $this->model->query($parent);
|
||||
}
|
||||
|
||||
if ($parent === null) {
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
return $parent;
|
||||
}
|
||||
]
|
||||
];
|
290
kirby/config/sections/pages.php
Executable file
290
kirby/config/sections/pages.php
Executable file
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Blueprint;
|
||||
use Kirby\Toolkit\A;
|
||||
use Kirby\Toolkit\F;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
return [
|
||||
'mixins' => [
|
||||
'empty',
|
||||
'headline',
|
||||
'layout',
|
||||
'min',
|
||||
'max',
|
||||
'pagination',
|
||||
'parent'
|
||||
],
|
||||
'props' => [
|
||||
/**
|
||||
* Optional array of templates that should only be allowed to add.
|
||||
*/
|
||||
'create' => function ($add = null) {
|
||||
return A::wrap($add);
|
||||
},
|
||||
/**
|
||||
* Image options to control the source and look of page previews
|
||||
*/
|
||||
'image' => function ($image = null) {
|
||||
return $image ?? [];
|
||||
},
|
||||
/**
|
||||
* Optional info text setup. Info text is shown on the right (lists) or below (cards) the page title.
|
||||
*/
|
||||
'info' => function (string $info = null) {
|
||||
return $info;
|
||||
},
|
||||
/**
|
||||
* The size option controls the size of cards. By default cards are auto-sized and the cards grid will always fill the full width. With a size you can disable auto-sizing. Available sizes: tiny, small, medium, large
|
||||
*/
|
||||
'size' => function (string $size = 'auto') {
|
||||
return $size;
|
||||
},
|
||||
/**
|
||||
* Enables/disables manual sorting
|
||||
*/
|
||||
'sortable' => function (bool $sortable = true) {
|
||||
return $sortable;
|
||||
},
|
||||
/**
|
||||
* Overwrites manual sorting and sorts by the given field and sorting direction (i.e. date desc)
|
||||
*/
|
||||
'sortBy' => function (string $sortBy = null) {
|
||||
return $sortBy;
|
||||
},
|
||||
/**
|
||||
* Filters pages by their status. Available status settings: draft, unlisted, listed, published, all.
|
||||
*/
|
||||
'status' => function (string $status = '') {
|
||||
if ($status === 'drafts') {
|
||||
$status = 'draft';
|
||||
}
|
||||
|
||||
if (in_array($status, ['all', 'draft', 'published', 'listed', 'unlisted']) === false) {
|
||||
$status = 'all';
|
||||
}
|
||||
|
||||
return $status;
|
||||
},
|
||||
/**
|
||||
* Setup for the main text in the list or cards. By default this will display the page title.
|
||||
*/
|
||||
'text' => function (string $text = '{{ page.title }}') {
|
||||
return $text;
|
||||
}
|
||||
],
|
||||
'computed' => [
|
||||
'dragTextType' => function () {
|
||||
return option('panel.kirbytext', true) ? 'kirbytext' : 'markdown';
|
||||
},
|
||||
'templates' => function () {
|
||||
return A::wrap($this->templates ?? $this->template);
|
||||
},
|
||||
'parent' => function () {
|
||||
return $this->parentModel();
|
||||
},
|
||||
'pages' => function () {
|
||||
switch ($this->status) {
|
||||
case 'draft':
|
||||
$pages = $this->parent->drafts();
|
||||
break;
|
||||
case 'listed':
|
||||
$pages = $this->parent->children()->listed();
|
||||
break;
|
||||
case 'published':
|
||||
$pages = $this->parent->children();
|
||||
break;
|
||||
case 'unlisted':
|
||||
$pages = $this->parent->children()->unlisted();
|
||||
break;
|
||||
default:
|
||||
$pages = $this->parent->childrenAndDrafts();
|
||||
}
|
||||
|
||||
// loop for the best performance
|
||||
foreach ($pages->data as $id => $page) {
|
||||
|
||||
// remove all protected pages
|
||||
if ($page->isReadable() === false) {
|
||||
unset($pages->data[$id]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter by all set templates
|
||||
if ($this->templates && in_array($page->intendedTemplate()->name(), $this->templates) === false) {
|
||||
unset($pages->data[$id]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// sort
|
||||
if ($this->sortBy) {
|
||||
$pages = $pages->sortBy(...Str::split($this->sortBy, ' '));
|
||||
}
|
||||
|
||||
// pagination
|
||||
$pages = $pages->paginate([
|
||||
'page' => $this->page,
|
||||
'limit' => $this->limit
|
||||
]);
|
||||
|
||||
return $pages;
|
||||
},
|
||||
'total' => function () {
|
||||
return $this->pages->pagination()->total();
|
||||
},
|
||||
'data' => function () {
|
||||
$data = [];
|
||||
|
||||
if ($this->layout === 'list') {
|
||||
$thumb = [
|
||||
'width' => 100,
|
||||
'height' => 100
|
||||
];
|
||||
} else {
|
||||
$thumb = [
|
||||
'width' => 400,
|
||||
'height' => 400
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($this->pages as $item) {
|
||||
$permissions = $item->permissions();
|
||||
$blueprint = $item->blueprint();
|
||||
$image = $item->panelImage($this->image, $thumb);
|
||||
|
||||
$data[] = [
|
||||
'id' => $item->id(),
|
||||
'dragText' => $item->dragText($this->dragTextType),
|
||||
'text' => $item->toString($this->text),
|
||||
'info' => $item->toString($this->info ?? false),
|
||||
'parent' => $item->parentId(),
|
||||
'icon' => $item->panelIcon($image),
|
||||
'image' => $image,
|
||||
'link' => $item->panelUrl(true),
|
||||
'status' => $item->status(),
|
||||
'permissions' => [
|
||||
'sort' => $permissions->can('sort'),
|
||||
'changeStatus' => $permissions->can('changeStatus')
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
},
|
||||
'errors' => function () {
|
||||
$errors = [];
|
||||
|
||||
if ($this->validateMax() === false) {
|
||||
$errors['max'] = I18n::template('error.section.pages.max.' . I18n::form($this->max), [
|
||||
'max' => $this->max,
|
||||
'section' => $this->headline
|
||||
]);
|
||||
}
|
||||
|
||||
if ($this->validateMin() === false) {
|
||||
$errors['min'] = I18n::template('error.section.pages.min.' . I18n::form($this->max), [
|
||||
'min' => $this->min,
|
||||
'section' => $this->headline
|
||||
]);
|
||||
}
|
||||
|
||||
if (empty($errors) === true) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
$this->name => [
|
||||
'label' => $this->headline,
|
||||
'message' => $errors,
|
||||
]
|
||||
];
|
||||
},
|
||||
'add' => function () {
|
||||
if (in_array($this->status, ['draft', 'all']) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isFull() === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
'link' => function () {
|
||||
$modelLink = $this->model->panelUrl(true);
|
||||
$parentLink = $this->parent->panelUrl(true);
|
||||
|
||||
if ($modelLink !== $parentLink) {
|
||||
return $parentLink;
|
||||
}
|
||||
},
|
||||
'pagination' => function () {
|
||||
return $this->pagination();
|
||||
},
|
||||
'sortable' => function () {
|
||||
if ($this->status !== 'listed' && $this->status !== 'all') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->sortable === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->sortBy !== null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
],
|
||||
'methods' => [
|
||||
'blueprints' => function () {
|
||||
$blueprints = [];
|
||||
$templates = empty($this->create) === false ? $this->create : $this->templates;
|
||||
|
||||
if (empty($templates) === true) {
|
||||
foreach (glob(App::instance()->root('blueprints') . '/pages/*.yml') as $blueprint) {
|
||||
$templates[] = F::name($blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
// convert every template to a usable option array
|
||||
// for the template select box
|
||||
foreach ($templates as $template) {
|
||||
try {
|
||||
$props = Blueprint::load('pages/' . $template);
|
||||
|
||||
$blueprints[] = [
|
||||
'name' => basename($props['name']),
|
||||
'title' => $props['title'],
|
||||
];
|
||||
} catch (Throwable $e) {
|
||||
$blueprints[] = [
|
||||
'name' => basename($template),
|
||||
'title' => ucfirst($template),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $blueprints;
|
||||
}
|
||||
],
|
||||
'toArray' => function () {
|
||||
return [
|
||||
'data' => $this->data,
|
||||
'errors' => $this->errors,
|
||||
'options' => [
|
||||
'add' => $this->add,
|
||||
'empty' => $this->empty,
|
||||
'headline' => $this->headline,
|
||||
'layout' => $this->layout,
|
||||
'link' => $this->link,
|
||||
'size' => $this->size,
|
||||
'sortable' => $this->sortable
|
||||
],
|
||||
'pagination' => $this->pagination,
|
||||
];
|
||||
}
|
||||
];
|
225
kirby/config/tags.php
Executable file
225
kirby/config/tags.php
Executable file
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Html;
|
||||
use Kirby\Cms\Url;
|
||||
|
||||
/**
|
||||
* Default KirbyTags definition
|
||||
*/
|
||||
return [
|
||||
|
||||
/* Date */
|
||||
'date' => [
|
||||
'attr' => [],
|
||||
'html' => function ($tag) {
|
||||
return strtolower($tag->date) === 'year' ? date('Y') : date($tag->date);
|
||||
}
|
||||
],
|
||||
|
||||
/* Email */
|
||||
'email' => [
|
||||
'attr' => [
|
||||
'class',
|
||||
'rel',
|
||||
'target',
|
||||
'text',
|
||||
'title'
|
||||
],
|
||||
'html' => function ($tag) {
|
||||
return Html::email($tag->value, $tag->text, [
|
||||
'class' => $tag->class,
|
||||
'rel' => $tag->rel,
|
||||
'target' => $tag->target,
|
||||
'title' => $tag->title,
|
||||
]);
|
||||
}
|
||||
],
|
||||
|
||||
/* File */
|
||||
'file' => [
|
||||
'attr' => [
|
||||
'class',
|
||||
'rel',
|
||||
'target',
|
||||
'text',
|
||||
'title'
|
||||
],
|
||||
'html' => function ($tag) {
|
||||
if (!$file = $tag->file($tag->value)) {
|
||||
return $tag->text;
|
||||
}
|
||||
|
||||
// use filename if the text is empty and make sure to
|
||||
// ignore markdown italic underscores in filenames
|
||||
if (empty($tag->text) === true) {
|
||||
$tag->text = str_replace('_', '\_', $file->filename());
|
||||
}
|
||||
|
||||
return Html::a($file->url(), $tag->text, [
|
||||
'class' => $tag->class,
|
||||
'download' => true,
|
||||
'rel' => $tag->rel,
|
||||
'target' => $tag->target,
|
||||
'title' => $tag->title,
|
||||
]);
|
||||
}
|
||||
],
|
||||
|
||||
/* Gist */
|
||||
'gist' => [
|
||||
'attr' => [
|
||||
'file'
|
||||
],
|
||||
'html' => function ($tag) {
|
||||
return Html::gist($tag->value, $tag->file);
|
||||
}
|
||||
],
|
||||
|
||||
/* Image */
|
||||
'image' => [
|
||||
'attr' => [
|
||||
'alt',
|
||||
'caption',
|
||||
'class',
|
||||
'height',
|
||||
'imgclass',
|
||||
'link',
|
||||
'linkclass',
|
||||
'rel',
|
||||
'target',
|
||||
'text',
|
||||
'title',
|
||||
'width'
|
||||
],
|
||||
'html' => function ($tag) {
|
||||
if ($tag->file = $tag->file($tag->value)) {
|
||||
$tag->src = $tag->file->url();
|
||||
$tag->alt = $tag->alt ?? $tag->file->alt()->or(' ')->value();
|
||||
$tag->title = $tag->title ?? $tag->file->title()->value();
|
||||
$tag->caption = $tag->caption ?? $tag->file->caption()->value();
|
||||
} else {
|
||||
$tag->src = Url::to($tag->value);
|
||||
}
|
||||
|
||||
$link = function ($img) use ($tag) {
|
||||
if (empty($tag->link) === true) {
|
||||
return $img;
|
||||
}
|
||||
|
||||
return Html::a($tag->link === 'self' ? $tag->src : $tag->link, [$img], [
|
||||
'rel' => $tag->rel,
|
||||
'class' => $tag->linkclass,
|
||||
'target' => $tag->target
|
||||
]);
|
||||
};
|
||||
|
||||
$image = Html::img($tag->src, [
|
||||
'width' => $tag->width,
|
||||
'height' => $tag->height,
|
||||
'class' => $tag->imgclass,
|
||||
'title' => $tag->title,
|
||||
'alt' => $tag->alt ?? ' '
|
||||
]);
|
||||
|
||||
if ($tag->kirby()->option('kirbytext.image.figure', true) === false) {
|
||||
return $link($image);
|
||||
}
|
||||
|
||||
return Html::figure([ $link($image) ], $tag->caption, [
|
||||
'class' => $tag->class
|
||||
]);
|
||||
}
|
||||
],
|
||||
|
||||
/* Link */
|
||||
'link' => [
|
||||
'attr' => [
|
||||
'class',
|
||||
'rel',
|
||||
'role',
|
||||
'target',
|
||||
'title',
|
||||
'text',
|
||||
],
|
||||
'html' => function ($tag) {
|
||||
return Html::a($tag->value, $tag->text, [
|
||||
'rel' => $tag->rel,
|
||||
'class' => $tag->class,
|
||||
'role' => $tag->role,
|
||||
'title' => $tag->title,
|
||||
'target' => $tag->target,
|
||||
]);
|
||||
}
|
||||
],
|
||||
|
||||
/* Tel */
|
||||
'tel' => [
|
||||
'attr' => [
|
||||
'class',
|
||||
'rel',
|
||||
'text',
|
||||
'title'
|
||||
],
|
||||
'html' => function ($tag) {
|
||||
return Html::tel($tag->value, $tag->text, [
|
||||
'class' => $tag->class,
|
||||
'rel' => $tag->rel,
|
||||
'title' => $tag->title
|
||||
]);
|
||||
}
|
||||
],
|
||||
|
||||
/* Twitter */
|
||||
'twitter' => [
|
||||
'attr' => [
|
||||
'class',
|
||||
'rel',
|
||||
'target',
|
||||
'text',
|
||||
'title'
|
||||
],
|
||||
'html' => function ($tag) {
|
||||
|
||||
// get and sanitize the username
|
||||
$username = str_replace('@', '', $tag->value);
|
||||
|
||||
// build the profile url
|
||||
$url = 'https://twitter.com/' . $username;
|
||||
|
||||
// sanitize the link text
|
||||
$text = $tag->text ?? '@' . $username;
|
||||
|
||||
// build the final link
|
||||
return Html::a($url, $text, [
|
||||
'class' => $tag->class,
|
||||
'rel' => $tag->rel,
|
||||
'target' => $tag->target,
|
||||
'title' => $tag->title,
|
||||
]);
|
||||
}
|
||||
],
|
||||
|
||||
/* Video */
|
||||
'video' => [
|
||||
'attr' => [
|
||||
'class',
|
||||
'caption',
|
||||
'height',
|
||||
'width'
|
||||
],
|
||||
'html' => function ($tag) {
|
||||
$video = Html::video(
|
||||
$tag->value,
|
||||
$tag->kirby()->option('kirbytext.video.options', [])
|
||||
);
|
||||
|
||||
return Html::figure([$video], $tag->caption, [
|
||||
'class' => $tag->class ?? $tag->kirby()->option('kirbytext.video.class', 'video'),
|
||||
'height' => $tag->height ?? $tag->kirby()->option('kirbytext.video.height'),
|
||||
'width' => $tag->width ?? $tag->kirby()->option('kirbytext.video.width'),
|
||||
]);
|
||||
}
|
||||
],
|
||||
|
||||
];
|
15
kirby/config/tests.php
Executable file
15
kirby/config/tests.php
Executable file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
$testDir = dirname(__DIR__) . '/tests';
|
||||
|
||||
if (is_dir($testDir) === true) {
|
||||
spl_autoload_register(function ($className) use ($testDir) {
|
||||
$path = str_replace('Kirby\\', '', $className);
|
||||
$path = str_replace('\\', '/', $path);
|
||||
$file = $testDir . '/' . $path . '.php';
|
||||
|
||||
if (file_exists($file)) {
|
||||
include $file;
|
||||
}
|
||||
});
|
||||
}
|
33
kirby/config/urls.php
Executable file
33
kirby/config/urls.php
Executable file
@@ -0,0 +1,33 @@
|
||||
<?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');
|
||||
}
|
||||
];
|
Reference in New Issue
Block a user