Upgrade to 4.0.0

This commit is contained in:
Bastian Allgeier
2023-11-28 09:33:56 +01:00
parent f96b96af76
commit 3b0b6546ca
480 changed files with 21371 additions and 13327 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,14 +2,19 @@
use Kirby\Cms\App;
use Kirby\Cms\Find;
use Kirby\Cms\PageRules;
use Kirby\Exception\Exception;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\PermissionException;
use Kirby\Panel\ChangesDialog;
use Kirby\Panel\Field;
use Kirby\Panel\PageCreateDialog;
use Kirby\Panel\Panel;
use Kirby\Toolkit\I18n;
use Kirby\Toolkit\Str;
use Kirby\Uuid\Uuids;
$fields = require __DIR__ . '/../fields/dialogs.php';
$files = require __DIR__ . '/../files/dialogs.php';
return [
@@ -155,10 +160,16 @@ return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'notice' => [
'type' => 'info',
'theme' => 'notice',
'text' => I18n::translate('page.changeTemplate.notice')
],
'template' => Field::template($blueprints, [
'required' => true
])
],
'theme' => 'notice',
'submitButton' => I18n::translate('change'),
'value' => [
'template' => $page->intendedTemplate()->name()
@@ -167,9 +178,10 @@ return [
];
},
'submit' => function (string $id) {
$request = App::instance()->request();
$page = Find::page($id);
$template = App::instance()->request()->get('template');
Find::page($id)->changeTemplate($request->get('template'));
$page->changeTemplate($template);
return [
'event' => 'page.changeTemplate',
@@ -224,17 +236,8 @@ return [
$slug = trim($request->get('slug', ''));
// basic input validation before we move on
if (Str::length($title) === 0) {
throw new InvalidArgumentException([
'key' => 'page.changeTitle.empty'
]);
}
if (Str::length($slug) === 0) {
throw new InvalidArgumentException([
'key' => 'page.slug.invalid'
]);
}
PageRules::validateTitleLength($title);
PageRules::validateSlugLength($slug);
// nothing changed
if ($page->title()->value() === $title && $page->slug() === $slug) {
@@ -277,93 +280,30 @@ return [
'page.create' => [
'pattern' => 'pages/create',
'load' => function () {
$kirby = App::instance();
$request = $kirby->request();
$request = App::instance()->request();
$dialog = new PageCreateDialog(
parentId: $request->get('parent'),
sectionId: $request->get('section'),
slug: $request->get('slug'),
template: $request->get('template'),
title: $request->get('title'),
viewId: $request->get('view'),
);
// the parent model for the new page
$parent = $request->get('parent', 'site');
// the view on which the add button is located
// this is important to find the right section
// and provide the correct templates for the new page
$view = $request->get('view', $parent);
// templates will be fetched depending on the
// section settings in the blueprint
$section = $request->get('section');
// this is the parent model
$model = Find::parent($parent);
// this is the view model
// i.e. site if the add button is on
// the dashboard
$view = Find::parent($view);
// available blueprints/templates for the new page
// are always loaded depending on the matching section
// in the view model blueprint
$blueprints = $view->blueprints($section);
// the pre-selected template
$template = $blueprints[0]['name'] ?? $blueprints[0]['value'] ?? null;
$fields = [
'parent' => Field::hidden(),
'title' => Field::title([
'required' => true,
'preselect' => true
]),
'slug' => Field::slug([
'required' => true,
'sync' => 'title',
'path' => empty($model->id()) === false ? '/' . $model->id() . '/' : '/'
]),
'template' => Field::hidden()
];
// only show template field if > 1 templates available
// or when in debug mode
if (count($blueprints) > 1 || $kirby->option('debug') === true) {
$fields['template'] = Field::template($blueprints, [
'required' => true
]);
}
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => $fields,
'submitButton' => I18n::translate('page.draft.create'),
'value' => [
'parent' => $parent,
'slug' => '',
'template' => $template,
'title' => '',
]
]
];
return $dialog->load();
},
'submit' => function () {
$request = App::instance()->request();
$title = trim($request->get('title', ''));
$dialog = new PageCreateDialog(
parentId: $request->get('parent'),
sectionId: $request->get('section'),
slug: $request->get('slug'),
template: $request->get('template'),
title: $request->get('title'),
viewId: $request->get('view'),
);
if (Str::length($title) === 0) {
throw new InvalidArgumentException([
'key' => 'page.changeTitle.empty'
]);
}
$page = Find::parent($request->get('parent', 'site'))->createChild([
'content' => ['title' => $title],
'slug' => $request->get('slug'),
'template' => $request->get('template'),
]);
return [
'event' => 'page.create',
'redirect' => $page->panel()->url(true)
];
return $dialog->submit($request->get());
}
],
@@ -529,6 +469,13 @@ return [
}
],
// page field dialogs
'page.fields' => [
'pattern' => '(pages/.*?)/fields/(:any)/(:all?)',
'load' => $fields['model']['load'],
'submit' => $fields['model']['submit']
],
// change filename
'page.file.changeName' => [
'pattern' => '(pages/.*?)/files/(:any)/changeName',
@@ -543,6 +490,13 @@ return [
'submit' => $files['changeSort']['submit'],
],
// change template
'page.file.changeTemplate' => [
'pattern' => '(pages/.*?)/files/(:any)/changeTemplate',
'load' => $files['changeTemplate']['load'],
'submit' => $files['changeTemplate']['submit'],
],
// delete
'page.file.delete' => [
'pattern' => '(pages/.*?)/files/(:any)/delete',
@@ -550,6 +504,56 @@ return [
'submit' => $files['delete']['submit'],
],
// page file field dialogs
'page.file.fields' => [
'pattern' => '(pages/.*?)/files/(:any)/fields/(:any)/(:all?)',
'load' => $fields['file']['load'],
'submit' => $fields['file']['submit'],
],
// move page
'page.move' => [
'pattern' => 'pages/(:any)/move',
'load' => function (string $id) {
$page = Find::page($id);
$parent = $page->parentModel();
if (Uuids::enabled() === false) {
$parentId = $parent?->id() ?? '/';
} else {
$parentId = $parent?->uuid()->toString() ?? 'site://';
}
return [
'component' => 'k-page-move-dialog',
'props' => [
'value' => [
'move' => $page->panel()->url(true),
'parent' => $parentId
]
]
];
},
'submit' => function (string $id) {
$kirby = App::instance();
$parentId = $kirby->request()->get('parent');
$parent = (empty($parentId) === true || $parentId === '/' || $parentId === 'site://') ? $kirby->site() : Find::page($parentId);
$oldPage = Find::page($id);
$newPage = $oldPage->move($parent);
return [
'event' => 'page.move',
'redirect' => $newPage->panel()->url(true),
'dispatch' => [
'content/move' => [
$oldPage->panel()->url(true),
$newPage->panel()->url(true)
]
],
];
}
],
// change site title
'site.changeTitle' => [
'pattern' => 'site/changeTitle',
@@ -572,14 +576,21 @@ return [
},
'submit' => function () {
$kirby = App::instance();
$kirby->site()->changeTitle($kirby->request()->get('title'));
return [
'event' => 'site.changeTitle',
];
}
],
// site field dialogs
'site.fields' => [
'pattern' => '(site)/fields/(:any)/(:all?)',
'load' => $fields['model']['load'],
'submit' => $fields['model']['submit'],
],
// change filename
'site.file.changeName' => [
'pattern' => '(site)/files/(:any)/changeName',
@@ -594,6 +605,13 @@ return [
'submit' => $files['changeSort']['submit'],
],
// change template
'site.file.changeTemplate' => [
'pattern' => '(site)/files/(:any)/changeTemplate',
'load' => $files['changeTemplate']['load'],
'submit' => $files['changeTemplate']['submit'],
],
// delete
'site.file.delete' => [
'pattern' => '(site)/files/(:any)/delete',
@@ -601,4 +619,24 @@ return [
'submit' => $files['delete']['submit'],
],
// site file field dialogs
'site.file.fields' => [
'pattern' => '(site)/files/(:any)/fields/(:any)/(:all?)',
'load' => $fields['file']['load'],
'submit' => $fields['file']['submit'],
],
// content changes
'changes' => [
'pattern' => 'changes',
'load' => function () {
$dialog = new ChangesDialog();
return $dialog->load();
},
'submit' => function () {
$dialog = new ChangesDialog();
$ids = App::instance()->request()->get('ids');
return $dialog->submit($ids);
}
],
];

View File

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

View File

@@ -1,14 +1,9 @@
<?php
use Kirby\Panel\Dropdown;
$files = require __DIR__ . '/../files/dropdowns.php';
return [
'changes' => [
'pattern' => 'changes',
'options' => fn () => Dropdown::changes()
],
'page' => [
'pattern' => 'pages/(:any)',
'options' => function (string $path) {

View File

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

View File

@@ -8,50 +8,49 @@ return [
'pages' => [
'label' => I18n::translate('pages'),
'icon' => 'page',
'query' => function (string $query = null) {
$pages = App::instance()->site()
'query' => function (string $query = null, int $limit, int $page) {
$kirby = App::instance();
$pages = $kirby->site()
->index(true)
->search($query)
->filter('isReadable', true)
->limit(10);
->filter('isListable', true)
->paginate($limit, $page);
$results = [];
foreach ($pages as $page) {
$results[] = [
return [
'results' => $pages->values(fn ($page) => [
'image' => $page->panel()->image(),
'text' => Escape::html($page->title()->value()),
'link' => $page->panel()->url(true),
'info' => Escape::html($page->id())
];
}
return $results;
'info' => Escape::html($page->id()),
'uuid' => $page->uuid()->toString(),
]),
'pagination' => $pages->pagination()->toArray()
];
}
],
'files' => [
'label' => I18n::translate('files'),
'icon' => 'image',
'query' => function (string $query = null) {
$files = App::instance()->site()
'query' => function (string $query = null, int $limit, int $page) {
$kirby = App::instance();
$files = $kirby->site()
->index(true)
->filter('isReadable', true)
->filter('isListable', true)
->files()
->filter('isListable', true)
->search($query)
->limit(10);
->paginate($limit, $page);
$results = [];
foreach ($files as $file) {
$results[] = [
return [
'results' => $files->values(fn ($file) => [
'image' => $file->panel()->image(),
'text' => Escape::html($file->filename()),
'link' => $file->panel()->url(true),
'info' => Escape::html($file->id())
];
}
return $results;
'info' => Escape::html($file->id()),
'uuid' => $file->uuid()->toString(),
]),
'pagination' => $files->pagination()->toArray()
];
}
]
];

View File

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

View File

@@ -14,28 +14,29 @@ return [
$environment = [
[
'label' => $license ? I18n::translate('license') : I18n::translate('license.register.label'),
'value' => $license ? 'Kirby 3' : I18n::translate('license.unregistered.label'),
'theme' => $license ? null : 'negative',
'dialog' => $license ? 'license' : 'registration'
'label' => $license->status()->label(),
'value' => $license->label(),
'theme' => $license->status()->theme(),
'icon' => $license->status()->icon(),
'dialog' => $license->status()->dialog()
],
[
'label' => $updateStatus?->label() ?? I18n::translate('version'),
'value' => $kirby->version(),
'link' => (
$updateStatus ?
$updateStatus->url() :
'https://github.com/getkirby/kirby/releases/tag/' . $kirby->version()
),
'theme' => $updateStatus?->theme()
'link' => $updateStatus?->url() ??
'https://github.com/getkirby/kirby/releases/tag/' . $kirby->version(),
'theme' => $updateStatus?->theme(),
'icon' => $updateStatus?->icon() ?? 'info'
],
[
'label' => 'PHP',
'value' => phpversion()
'value' => phpversion(),
'icon' => 'code'
],
[
'label' => I18n::translate('server'),
'value' => $system->serverSoftware() ?? '?'
'value' => $system->serverSoftware() ?? '?',
'icon' => 'server'
]
];

View File

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

View File

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

View File

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

View File

@@ -8,20 +8,22 @@ return [
'users' => [
'label' => I18n::translate('users'),
'icon' => 'users',
'query' => function (string $query = null) {
$users = App::instance()->users()->search($query)->limit(10);
$results = [];
'query' => function (string $query = null, int $limit, int $page) {
$kirby = App::instance();
$users = $kirby->users()
->search($query)
->paginate($limit, $page);
foreach ($users as $user) {
$results[] = [
return [
'results' => $users->values(fn ($user) => [
'image' => $user->panel()->image(),
'text' => Escape::html($user->username()),
'link' => $user->panel()->url(true),
'info' => Escape::html($user->role()->title())
];
}
return $results;
'info' => Escape::html($user->role()->title()),
'uuid' => $user->uuid()->toString(),
]),
'pagination' => $users->pagination()->toArray()
];
}
]
];

View File

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