Upgrade to 4.1.0
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
"description": "The Kirby core",
|
||||
"license": "proprietary",
|
||||
"type": "kirby-cms",
|
||||
"version": "4.0.3",
|
||||
"version": "4.1.0",
|
||||
"keywords": [
|
||||
"kirby",
|
||||
"cms",
|
||||
@@ -99,6 +99,8 @@
|
||||
"analyze:composer": "composer validate --strict --no-check-version --no-check-all",
|
||||
"analyze:phpmd": "phpmd . ansi phpmd.xml.dist --exclude 'dependencies/*,tests/*,vendor/*'",
|
||||
"analyze:psalm": "psalm",
|
||||
"bench": "phpbench run --report=aggregate --ref baseline",
|
||||
"bench:baseline": "phpbench run --report=aggregate --tag baseline",
|
||||
"build": "./scripts/build",
|
||||
"ci": [
|
||||
"@fix",
|
||||
@@ -106,8 +108,8 @@
|
||||
"@test"
|
||||
],
|
||||
"fix": "php-cs-fixer fix",
|
||||
"test": "phpunit --stderr",
|
||||
"test:coverage": "XDEBUG_MODE=coverage phpunit --stderr --coverage-html=tests/coverage",
|
||||
"test": "phpunit",
|
||||
"test:coverage": "XDEBUG_MODE=coverage phpunit --coverage-html=tests/coverage",
|
||||
"zip": "composer archive --format=zip --file=dist"
|
||||
}
|
||||
}
|
||||
|
2
kirby/composer.lock
generated
2
kirby/composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "977754e23aeb4791a5c4f60c6e18ddc6",
|
||||
"content-hash": "815a3c3a27039ef652b9619518a6cc9c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "christian-riesen/base32",
|
||||
|
@@ -84,9 +84,9 @@ return [
|
||||
'kirby\cms\kirbytag' => 'Kirby\Text\KirbyTag',
|
||||
'kirby\cms\kirbytags' => 'Kirby\Text\KirbyTags',
|
||||
'kirby\cms\template' => 'Kirby\Template\Template',
|
||||
'kirby\form\options' => 'Kirby\Options\Options',
|
||||
'kirby\form\optionsapi' => 'Kirby\Options\OptionsApi',
|
||||
'kirby\form\optionsquery' => 'Kirby\Options\OptionsQuery',
|
||||
'kirby\form\options' => 'Kirby\Option\Options',
|
||||
'kirby\form\optionsapi' => 'Kirby\Option\OptionsApi',
|
||||
'kirby\form\optionsquery' => 'Kirby\Option\OptionsQuery',
|
||||
'kirby\toolkit\dir' => 'Kirby\Filesystem\Dir',
|
||||
'kirby\toolkit\f' => 'Kirby\Filesystem\F',
|
||||
'kirby\toolkit\file' => 'Kirby\Filesystem\File',
|
||||
|
@@ -10,6 +10,7 @@ use Kirby\Panel\ChangesDialog;
|
||||
use Kirby\Panel\Field;
|
||||
use Kirby\Panel\PageCreateDialog;
|
||||
use Kirby\Panel\Panel;
|
||||
use Kirby\Toolkit\Escape;
|
||||
use Kirby\Toolkit\I18n;
|
||||
use Kirby\Toolkit\Str;
|
||||
use Kirby\Uuid\Uuids;
|
||||
|
@@ -21,7 +21,7 @@ return [
|
||||
'license' => [
|
||||
'code' => $license->code($obfuscated),
|
||||
'icon' => $status->icon(),
|
||||
'info' => $status->info($license->renewal('Y-m-d')),
|
||||
'info' => $status->info($license->renewal('Y-m-d', 'date')),
|
||||
'theme' => $status->theme(),
|
||||
'type' => $license->label(),
|
||||
],
|
||||
|
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Field\FieldOptions;
|
||||
use Kirby\Toolkit\A;
|
||||
use Kirby\Toolkit\Escape;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
return [
|
||||
@@ -52,42 +54,87 @@ return [
|
||||
return Str::lower($this->default);
|
||||
},
|
||||
'options' => function (): array {
|
||||
return A::map(array_keys($this->options), fn ($key) => [
|
||||
'value' => $this->options[$key],
|
||||
'text' => is_string($key) ? $key : null
|
||||
// resolve options to support manual arrays
|
||||
// alongside api and query options
|
||||
$props = FieldOptions::polyfill($this->props);
|
||||
$options = FieldOptions::factory([
|
||||
'text' => '{{ item.value }}',
|
||||
'value' => '{{ item.key }}',
|
||||
...$props['options']
|
||||
]);
|
||||
|
||||
$options = $options->render($this->model());
|
||||
|
||||
if (empty($options) === true) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$options = match (true) {
|
||||
// simple array of values
|
||||
// or value=text (from Options class)
|
||||
is_numeric($options[0]['value']) ||
|
||||
$options[0]['value'] === $options[0]['text']
|
||||
=> A::map($options, fn ($option) => [
|
||||
'value' => $option['text']
|
||||
]),
|
||||
|
||||
// deprecated: name => value, flipping
|
||||
// TODO: start throwing in warning in v5
|
||||
$this->isColor($options[0]['text'])
|
||||
=> A::map($options, fn ($option) => [
|
||||
'value' => $option['text'],
|
||||
// ensure that any HTML in the new text is escaped
|
||||
'text' => Escape::html($option['value'])
|
||||
]),
|
||||
|
||||
default
|
||||
=> A::map($options, fn ($option) => [
|
||||
'value' => $option['value'],
|
||||
'text' => $option['text']
|
||||
]),
|
||||
};
|
||||
|
||||
return $options;
|
||||
}
|
||||
],
|
||||
'methods' => [
|
||||
'isColor' => function (string $value): bool {
|
||||
return
|
||||
$this->isHex($value) ||
|
||||
$this->isRgb($value) ||
|
||||
$this->isHsl($value);
|
||||
},
|
||||
'isHex' => function (string $value): bool {
|
||||
return preg_match('/^#([\da-f]{3,4}){1,2}$/i', $value) === 1;
|
||||
},
|
||||
'isHsl' => function (string $value): bool {
|
||||
return preg_match('/^hsla?\(\s*(\d{1,3}\.?\d*)(deg|rad|grad|turn)?(?:,|\s)+(\d{1,3})%(?:,|\s)+(\d{1,3})%(?:,|\s|\/)*(\d*(?:\.\d+)?)(%?)\s*\)?$/i', $value) === 1;
|
||||
},
|
||||
'isRgb' => function (string $value): bool {
|
||||
return preg_match('/^rgba?\(\s*(\d{1,3})(%?)(?:,|\s)+(\d{1,3})(%?)(?:,|\s)+(\d{1,3})(%?)(?:,|\s|\/)*(\d*(?:\.\d+)?)(%?)\s*\)?$/i', $value) === 1;
|
||||
},
|
||||
],
|
||||
'validations' => [
|
||||
'color' => function ($value) {
|
||||
if (empty($value) === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
$this->format === 'hex' &&
|
||||
preg_match('/^#([\da-f]{3,4}){1,2}$/i', $value) !== 1
|
||||
) {
|
||||
if ($this->format === 'hex' && $this->isHex($value) === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'validation.color',
|
||||
'data' => ['format' => 'hex']
|
||||
]);
|
||||
}
|
||||
|
||||
if (
|
||||
$this->format === 'rgb' &&
|
||||
preg_match('/^rgba?\(\s*(\d{1,3})(%?)(?:,|\s)+(\d{1,3})(%?)(?:,|\s)+(\d{1,3})(%?)(?:,|\s|\/)*(\d*(?:\.\d+)?)(%?)\s*\)?$/i', $value) !== 1
|
||||
) {
|
||||
if ($this->format === 'rgb' && $this->isRgb($value) === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'validation.color',
|
||||
'data' => ['format' => 'rgb']
|
||||
]);
|
||||
}
|
||||
|
||||
if (
|
||||
$this->format === 'hsl' &&
|
||||
preg_match('/^hsla?\(\s*(\d{1,3}\.?\d*)(deg|rad|grad|turn)?(?:,|\s)+(\d{1,3})%(?:,|\s)+(\d{1,3})%(?:,|\s|\/)*(\d*(?:\.\d+)?)(%?)\s*\)?$/i', $value) !== 1
|
||||
) {
|
||||
if ($this->format === 'hsl' && $this->isHsl($value) === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'validation.color',
|
||||
'data' => ['format' => 'hsl']
|
||||
|
@@ -173,7 +173,7 @@ return [
|
||||
},
|
||||
'form' => function (array $values = []) {
|
||||
return new Form([
|
||||
'fields' => $this->attrs['fields'],
|
||||
'fields' => $this->attrs['fields'] ?? [],
|
||||
'values' => $values,
|
||||
'model' => $this->model
|
||||
]);
|
||||
|
@@ -257,7 +257,7 @@ return function (App $app) {
|
||||
try {
|
||||
return Structure::factory(
|
||||
Data::decode($field->value, 'yaml'),
|
||||
['parent' => $field->parent()]
|
||||
['parent' => $field->parent(), 'field' => $field]
|
||||
);
|
||||
} catch (Exception) {
|
||||
$message = 'Invalid structure data for "' . $field->key() . '" field';
|
||||
|
@@ -55,7 +55,7 @@ return [
|
||||
'parent' => function () {
|
||||
return $this->parentModel();
|
||||
},
|
||||
'files' => function () {
|
||||
'models' => function () {
|
||||
if ($this->query !== null) {
|
||||
$files = $this->parent->query($this->query, Files::class) ?? new Files([]);
|
||||
} else {
|
||||
@@ -99,6 +99,9 @@ return [
|
||||
|
||||
return $files;
|
||||
},
|
||||
'files' => function () {
|
||||
return $this->models;
|
||||
},
|
||||
'data' => function () {
|
||||
$data = [];
|
||||
|
||||
@@ -106,7 +109,7 @@ return [
|
||||
// a different parent model
|
||||
$dragTextAbsolute = $this->model->is($this->parent) === false;
|
||||
|
||||
foreach ($this->files as $file) {
|
||||
foreach ($this->models as $file) {
|
||||
$panel = $file->panel();
|
||||
|
||||
$item = [
|
||||
@@ -137,7 +140,7 @@ return [
|
||||
return $data;
|
||||
},
|
||||
'total' => function () {
|
||||
return $this->files->pagination()->total();
|
||||
return $this->models->pagination()->total();
|
||||
},
|
||||
'errors' => function () {
|
||||
$errors = [];
|
||||
@@ -191,13 +194,14 @@ return [
|
||||
'multiple' => $multiple,
|
||||
'max' => $max,
|
||||
'api' => $this->parent->apiUrl(true) . '/files',
|
||||
'attributes' => array_filter([
|
||||
'attributes' => [
|
||||
// TODO: an edge issue that needs to be solved:
|
||||
// if multiple users load the same section at the same time
|
||||
// and upload a file, uploaded files have the same sort number
|
||||
// if multiple users load the same section
|
||||
// at the same time and upload a file,
|
||||
// uploaded files have the same sort number
|
||||
'sort' => $this->sortable === true ? $this->total + 1 : null,
|
||||
'template' => $template
|
||||
])
|
||||
]
|
||||
];
|
||||
}
|
||||
],
|
||||
@@ -208,7 +212,7 @@ return [
|
||||
'options' => [
|
||||
'accept' => $this->accept,
|
||||
'apiUrl' => $this->parent->apiUrl(true),
|
||||
'columns' => $this->columns,
|
||||
'columns' => $this->columnsWithTypes(),
|
||||
'empty' => $this->empty,
|
||||
'headline' => $this->headline,
|
||||
'help' => $this->help,
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Kirby\Cms\ModelWithContent;
|
||||
use Kirby\Toolkit\I18n;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
@@ -28,7 +29,7 @@ return [
|
||||
],
|
||||
'computed' => [
|
||||
'columns' => function () {
|
||||
$columns = [];
|
||||
$columns = [];
|
||||
|
||||
if ($this->layout !== 'table') {
|
||||
return [];
|
||||
@@ -94,7 +95,27 @@ return [
|
||||
},
|
||||
],
|
||||
'methods' => [
|
||||
'columnsValues' => function (array $item, $model) {
|
||||
'columnsWithTypes' => function () {
|
||||
$columns = $this->columns;
|
||||
|
||||
// add the type to the columns for the table layout
|
||||
if ($this->layout === 'table') {
|
||||
$blueprint = $this->models->first()?->blueprint();
|
||||
|
||||
if ($blueprint === null) {
|
||||
return $columns;
|
||||
}
|
||||
|
||||
foreach ($columns as $columnName => $column) {
|
||||
if ($id = $column['id'] ?? null) {
|
||||
$columns[$columnName]['type'] ??= $blueprint->field($id)['type'] ?? null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $columns;
|
||||
},
|
||||
'columnsValues' => function (array $item, ModelWithContent $model) {
|
||||
$item['title'] = [
|
||||
// override toSafeString() coming from `$item`
|
||||
// because the table cells don't use v-html
|
||||
|
@@ -82,7 +82,7 @@ return [
|
||||
|
||||
return $parent;
|
||||
},
|
||||
'pages' => function () {
|
||||
'models' => function () {
|
||||
if ($this->query !== null) {
|
||||
$pages = $this->parent->query($this->query, Pages::class) ?? new Pages([]);
|
||||
} else {
|
||||
@@ -156,13 +156,16 @@ return [
|
||||
|
||||
return $pages;
|
||||
},
|
||||
'pages' => function () {
|
||||
return $this->models;
|
||||
},
|
||||
'total' => function () {
|
||||
return $this->pages->pagination()->total();
|
||||
return $this->models->pagination()->total();
|
||||
},
|
||||
'data' => function () {
|
||||
$data = [];
|
||||
|
||||
foreach ($this->pages as $page) {
|
||||
foreach ($this->models as $page) {
|
||||
$panel = $page->panel();
|
||||
$permissions = $page->permissions();
|
||||
|
||||
@@ -284,7 +287,7 @@ return [
|
||||
'errors' => $this->errors,
|
||||
'options' => [
|
||||
'add' => $this->add,
|
||||
'columns' => $this->columns,
|
||||
'columns' => $this->columnsWithTypes(),
|
||||
'empty' => $this->empty,
|
||||
'headline' => $this->headline,
|
||||
'help' => $this->help,
|
||||
|
@@ -53,6 +53,7 @@ return [
|
||||
$value = $report['value'] ?? null;
|
||||
|
||||
$reports[] = [
|
||||
'icon' => $toString($report['icon'] ?? null),
|
||||
'info' => $toString(I18n::translate($info, $info)),
|
||||
'label' => $toString(I18n::translate($label, $label)),
|
||||
'link' => $toString(I18n::translate($link, $link)),
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
use Kirby\Cms\Html;
|
||||
use Kirby\Cms\Url;
|
||||
use Kirby\Exception\NotFoundException;
|
||||
use Kirby\Text\KirbyTag;
|
||||
use Kirby\Toolkit\A;
|
||||
use Kirby\Toolkit\Str;
|
||||
@@ -61,7 +62,7 @@ return [
|
||||
],
|
||||
'html' => function (KirbyTag $tag): string {
|
||||
if (!$file = $tag->file($tag->value)) {
|
||||
return $tag->text;
|
||||
return $tag->text ?? $tag->value;
|
||||
}
|
||||
|
||||
// use filename if the text is empty and make sure to
|
||||
@@ -197,7 +198,20 @@ return [
|
||||
Uuid::is($tag->value, 'page') === true ||
|
||||
Uuid::is($tag->value, 'file') === true
|
||||
) {
|
||||
$tag->value = Uuid::for($tag->value)->model()->url();
|
||||
$tag->value = Uuid::for($tag->value)->model()?->url();
|
||||
}
|
||||
|
||||
// if url is empty, throw exception or link to the error page
|
||||
if ($tag->value === null) {
|
||||
if ($tag->kirby()->option('debug', false) === true) {
|
||||
if (empty($tag->text) === false) {
|
||||
throw new NotFoundException('The linked page cannot be found for the link text "' . $tag->text . '"');
|
||||
} else {
|
||||
throw new NotFoundException('The linked page cannot be found');
|
||||
}
|
||||
} else {
|
||||
$tag->value = Url::to($tag->kirby()->site()->errorPageId());
|
||||
}
|
||||
}
|
||||
|
||||
return Html::a($tag->value, $tag->text, [
|
||||
|
@@ -63,7 +63,7 @@
|
||||
"email": "Email",
|
||||
"email.placeholder": "mail@exemplo.pt",
|
||||
|
||||
"enter": "Enter",
|
||||
"enter": "Insira",
|
||||
"entries": "Registos",
|
||||
"entry": "Registo",
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
"error.language.code": "Por favor, insira um código válido para o idioma",
|
||||
"error.language.duplicate": "O idioma já existe",
|
||||
"error.language.name": "Por favor, insira um nome válido para o idioma",
|
||||
"error.language.notFound": "Não foi possível encontrar o idoma",
|
||||
"error.language.notFound": "Não foi possível encontrar o idioma",
|
||||
|
||||
"error.layout.validation.block": "Há um erro no campo \"{field}\" no bloco {blockIndex} a usar o tipo de bloco \"{fieldset}\" no layout {layoutIndex}",
|
||||
"error.layout.validation.settings": "Há um erro na configuração do layout {index}",
|
||||
@@ -408,17 +408,17 @@
|
||||
"language.variable.value": "Valor",
|
||||
|
||||
"languages": "Idiomas",
|
||||
"languages.default": "Idioma padrão",
|
||||
"languages.default": "Idioma por defeito",
|
||||
"languages.empty": "Nenhum idioma ainda",
|
||||
"languages.secondary": "Idiomas secundários",
|
||||
"languages.secondary.empty": "Nenhum idioma secundário ainda",
|
||||
|
||||
"license": "Licença ",
|
||||
"license.activate": "Ativar agora",
|
||||
"license.activate": "Ative-a agora",
|
||||
"license.activate.label": "Por favor, ative a sua licença",
|
||||
"license.activate.domain": "A sua licença será ativada para <strong>{host}</strong>.",
|
||||
"license.activate.local": "Está prestes a ativar a sua licença Kirby no domínio local <strong>{host}</strong>. Se este site vai ser alojado num domínio público, por favor ative-o lá. Se o domínio {host} é o o que deseja para usar a sua licença, por favor continue.",
|
||||
"license.activated": "Ativado",
|
||||
"license.activate.local": "Está prestes a ativar a sua licença Kirby no domínio local <strong>{host}</strong>. Se este site vai ser alojado num domínio público, por favor ative-o lá. Se o domínio {host} é o que deseja para usar a sua licença, por favor continue.",
|
||||
"license.activated": "Ativada",
|
||||
"license.buy": "Compre uma licença",
|
||||
"license.code": "Código",
|
||||
"license.code.help": "Recebeu o seu código de licença por email após a compra. Por favor, copie e cole aqui.",
|
||||
@@ -459,16 +459,16 @@
|
||||
"login.code.placeholder.totp": "000000",
|
||||
"login.code.text.email": "Se o seu endereço de email está registado, o código solicitado foi enviado por email.",
|
||||
"login.code.text.totp": "Por favor, insira o código de segurança da sua aplicação de autenticação.",
|
||||
"login.email.login.body": "Olá {user.nameOrEmail},\n\nRecentemente solicitou um código de início de sessão para o Painel de {site}.\nO seguinte código de início de sessão será válido por {timeout} minutos:\n\n{code}\n\nSe não solicitou um código de início de sessão, por favor ignore este e-mail ou entre em contacto com o administrador se tiver dúvidas.\nPor motivos de segurança, por favor NÃO reencaminhe este e-mail.",
|
||||
"login.email.login.body": "Olá {user.nameOrEmail},\n\nRecentemente solicitou um código de início de sessão para o painel de {site}.\nO seguinte código de início de sessão será válido por {timeout} minutos:\n\n{code}\n\nSe não solicitou um código de início de sessão, por favor ignore este e-mail ou entre em contacto com o administrador se tiver dúvidas.\nPor motivos de segurança, por favor NÃO reencaminhe este e-mail.",
|
||||
"login.email.login.subject": "O seu código de início de sessão",
|
||||
"login.email.password-reset.body": "Olá {user.nameOrEmail},\n\nRecentemente solicitou um código de redefinição de palavra-passe para o Painel de {site}.\nO seguinte código de redefinição de palavra-passe será válido por {timeout} minutos:\n\n{code}\n\nSe não solicitou um código de redefinição de palavra-passe, por favor ignore este e-mail ou entre em contacto com o administrador se tiver dúvidas.\nPor motivos de segurança, por favor NÃO reencaminhe este e-mail.",
|
||||
"login.email.password-reset.body": "Olá {user.nameOrEmail},\n\nRecentemente solicitou um código de redefinição de palavra-passe para o painel de {site}.\nO seguinte código de redefinição de palavra-passe será válido por {timeout} minutos:\n\n{code}\n\nSe não solicitou um código de redefinição de palavra-passe, por favor ignore este e-mail ou entre em contacto com o administrador se tiver dúvidas.\nPor motivos de segurança, por favor NÃO reencaminhe este e-mail.",
|
||||
"login.email.password-reset.subject": "O seu código de redefinição de palavra-passe",
|
||||
"login.remember": "Manter sessão iniciada",
|
||||
"login.reset": "Redefinir palavra-passe",
|
||||
"login.toggleText.code.email": "Iniciar sessão com email",
|
||||
"login.toggleText.code.email-password": "Iniciar sessão com palavra-passe",
|
||||
"login.toggleText.password-reset.email": "Esqueceu-se da sua palavra-passe?",
|
||||
"login.toggleText.password-reset.email-password": "← Voltar à página de início de sessão",
|
||||
"login.toggleText.password-reset.email": "Esqueceu a sua palavra-passe?",
|
||||
"login.toggleText.password-reset.email-password": "← Voltar ao início de sessão",
|
||||
"login.totp.enable.option": "Configurar códigos de segurança",
|
||||
"login.totp.enable.intro": "As aplicações de autenticação podem gerar códigos de segurança que são usados como um segundo fator ao iniciar a sessão na sua conta.",
|
||||
"login.totp.enable.qr.label": "1. Leia este código QR",
|
||||
@@ -480,7 +480,7 @@
|
||||
"login.totp.enable.success": "Códigos de segurança ativados",
|
||||
"login.totp.disable.option": "Desativar códigos de segurança",
|
||||
"login.totp.disable.label": "Insira a sua palavra-passe para desativar códigos de segurança",
|
||||
"login.totp.disable.help": "No futuro, um segundo fator diferente, como um código de início de sessão enviado por e-mail, será solicitado quando iniciar a sessão. Poderá configurar códigos únicos novamente mais tarde.",
|
||||
"login.totp.disable.help": "No futuro, um segundo fator diferente, como um código de início de sessão enviado por e-mail, será solicitado quando iniciar a sessão. Poderá configurar códigos de segurança novamente mais tarde.",
|
||||
"login.totp.disable.admin": "<p>Isto irá desactivar os códigos de segurança para <strong>{user}</strong>.</p> <p>No futuro, um segundo fator diferente, como um código de início de sessão enviado por e-mail, será solicitado quando eles iniciarem a sessão. {user} poderá configurar códigos de segurança novamente após o próximo início de sessão.</p>",
|
||||
"login.totp.disable.success": "Códigos de segurança desativados",
|
||||
|
||||
@@ -684,7 +684,7 @@
|
||||
"upload.errors": "Erro",
|
||||
"upload.progress": "A enviar…",
|
||||
|
||||
"url": "Url",
|
||||
"url": "URL",
|
||||
"url.placeholder": "https://exemplo.pt",
|
||||
|
||||
"user": "Utilizador",
|
||||
|
@@ -135,7 +135,7 @@
|
||||
|
||||
"error.license.domain": "Domeniul pentru licență lipsește",
|
||||
"error.license.email": "Te rog introdu o adresă de e-mail validă",
|
||||
"error.license.format": "Please enter a valid license code",
|
||||
"error.license.format": "Te rog introdu un cod de licență valid",
|
||||
"error.license.verification": "Licența nu a putut fi verificată",
|
||||
|
||||
"error.login.totp.confirm.invalid": "Cod invalid",
|
||||
@@ -425,7 +425,7 @@
|
||||
"license.code.label": "Te rog introdu codul tău de licență",
|
||||
"license.status.active.info": "Include noi versiuni majore până la data de {date}",
|
||||
"license.status.active.label": "Licență validă",
|
||||
"license.status.demo.info": "This is a demo installation",
|
||||
"license.status.demo.info": "Aceasta este o instalare demo",
|
||||
"license.status.demo.label": "Demo",
|
||||
"license.status.inactive.info": "Reînnoiți licența pentru a actualiza la noile versiuni majore",
|
||||
"license.status.inactive.label": "Fără noi versiuni majore",
|
||||
|
2
kirby/panel/dist/css/style.min.css
vendored
2
kirby/panel/dist/css/style.min.css
vendored
File diff suppressed because one or more lines are too long
2
kirby/panel/dist/img/icons.svg
vendored
2
kirby/panel/dist/img/icons.svg
vendored
@@ -77,7 +77,7 @@
|
||||
<path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 10.5858L14.8284 7.75736L16.2426 9.17157L13.4142 12L16.2426 14.8284L14.8284 16.2426L12 13.4142L9.17157 16.2426L7.75736 14.8284L10.5858 12L7.75736 9.17157L9.17157 7.75736L12 10.5858Z"/>
|
||||
</symbol>
|
||||
<symbol id="icon-cancel-small" viewBox="0 0 24 24">
|
||||
<path d="M 12 10.5858 L 14.8284 7.75736 L 16.242599 9.17157 L 13.4142 12 L 16.242599 14.8284 L 14.8284 16.242599 L 12 13.4142 L 9.17157 16.242599 L 7.75736 14.8284 L 10.5858 12 L 7.75736 9.17157 L 9.17157 7.75736 Z"/>
|
||||
<path d="M11.9997 10.5865L16.9495 5.63672L18.3637 7.05093L13.4139 12.0007L18.3637 16.9504L16.9495 18.3646L11.9997 13.4149L7.04996 18.3646L5.63574 16.9504L10.5855 12.0007L5.63574 7.05093L7.04996 5.63672L11.9997 10.5865Z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-car" viewBox="0 0 24 24">
|
||||
<path d="M19 20H5V21C5 21.5523 4.55228 22 4 22H3C2.44772 22 2 21.5523 2 21V11L4.4805 5.21216C4.79566 4.47679 5.51874 4 6.31879 4H17.6812C18.4813 4 19.2043 4.47679 19.5195 5.21216L22 11V21C22 21.5523 21.5523 22 21 22H20C19.4477 22 19 21.5523 19 21V20ZM20 13H4V18H20V13ZM4.17594 11H19.8241L17.6812 6H6.31879L4.17594 11ZM6.5 17C5.67157 17 5 16.3284 5 15.5C5 14.6716 5.67157 14 6.5 14C7.32843 14 8 14.6716 8 15.5C8 16.3284 7.32843 17 6.5 17ZM17.5 17C16.6716 17 16 16.3284 16 15.5C16 14.6716 16.6716 14 17.5 14C18.3284 14 19 14.6716 19 15.5C19 16.3284 18.3284 17 17.5 17Z"/>
|
||||
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
2
kirby/panel/dist/js/Highlight.min.js
vendored
2
kirby/panel/dist/js/Highlight.min.js
vendored
File diff suppressed because one or more lines are too long
8
kirby/panel/dist/js/PlaygroundView.min.js
vendored
8
kirby/panel/dist/js/PlaygroundView.min.js
vendored
File diff suppressed because one or more lines are too long
8
kirby/panel/dist/js/index.min.js
vendored
8
kirby/panel/dist/js/index.min.js
vendored
File diff suppressed because one or more lines are too long
2
kirby/panel/dist/js/plugins.js
vendored
2
kirby/panel/dist/js/plugins.js
vendored
@@ -20,7 +20,7 @@ window.panel.plugin = function (plugin, extensions) {
|
||||
}
|
||||
|
||||
window.panel.plugins.components[`k-block-type-${name}`] = {
|
||||
extends: "k-block-type",
|
||||
extends: "k-block-type-default",
|
||||
...options
|
||||
};
|
||||
});
|
||||
|
6
kirby/panel/dist/js/vendor.min.js
vendored
6
kirby/panel/dist/js/vendor.min.js
vendored
File diff suppressed because one or more lines are too long
6
kirby/panel/dist/js/vue.min.js
vendored
6
kirby/panel/dist/js/vue.min.js
vendored
File diff suppressed because one or more lines are too long
95
kirby/panel/vite.config.mjs
Normal file
95
kirby/panel/vite.config.mjs
Normal file
@@ -0,0 +1,95 @@
|
||||
/* eslint-env node */
|
||||
import path from "path";
|
||||
|
||||
import { defineConfig, splitVendorChunkPlugin } from "vite";
|
||||
import vue from "@vitejs/plugin-vue2";
|
||||
import { viteStaticCopy } from "vite-plugin-static-copy";
|
||||
import externalGlobals from "rollup-plugin-external-globals";
|
||||
import kirby from "./scripts/vite-kirby.mjs";
|
||||
|
||||
let customServer;
|
||||
try {
|
||||
customServer = require("./vite.config.custom.js");
|
||||
} catch (err) {
|
||||
customServer = {};
|
||||
}
|
||||
|
||||
export default defineConfig(({ command }) => {
|
||||
// gather plugins depending on environment
|
||||
const plugins = [vue(), splitVendorChunkPlugin(), kirby()];
|
||||
|
||||
if (command === "build") {
|
||||
plugins.push(
|
||||
viteStaticCopy({
|
||||
targets: [
|
||||
{
|
||||
src: "node_modules/vue/dist/vue.min.js",
|
||||
dest: "js"
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (!process.env.VITEST) {
|
||||
plugins.push(
|
||||
// Externalize Vue so it's not loaded from node_modules
|
||||
// but accessed via window.Vue
|
||||
{
|
||||
...externalGlobals({ vue: "Vue" }),
|
||||
enforce: "post"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const proxy = {
|
||||
target: process.env.VUE_APP_DEV_SERVER ?? "http://sandbox.test",
|
||||
changeOrigin: true,
|
||||
secure: false
|
||||
};
|
||||
|
||||
return {
|
||||
plugins,
|
||||
define: {
|
||||
// Fix vuelidate error
|
||||
"process.env.BUILD": JSON.stringify("production")
|
||||
},
|
||||
base: "./",
|
||||
build: {
|
||||
minify: "terser",
|
||||
cssCodeSplit: false,
|
||||
rollupOptions: {
|
||||
input: "./src/index.js",
|
||||
output: {
|
||||
entryFileNames: "js/[name].min.js",
|
||||
chunkFileNames: "js/[name].min.js",
|
||||
assetFileNames: "[ext]/[name].min.[ext]"
|
||||
}
|
||||
}
|
||||
},
|
||||
optimizeDeps: {
|
||||
entries: "src/**/*.{js,vue}",
|
||||
exclude: ["vitest", "vue"]
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "src")
|
||||
}
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
"/api": proxy,
|
||||
"/env": proxy,
|
||||
"/media": proxy
|
||||
},
|
||||
open: proxy.target + "/panel",
|
||||
port: 3000,
|
||||
...customServer
|
||||
},
|
||||
test: {
|
||||
environment: "node",
|
||||
include: ["**/*.test.js"],
|
||||
setupFiles: ["vitest.setup.js"]
|
||||
}
|
||||
};
|
||||
});
|
@@ -632,10 +632,20 @@ class Api
|
||||
$uploadMaxFileSize = Str::toBytes(ini_get('upload_max_filesize'));
|
||||
|
||||
if ($postMaxSize < $uploadMaxFileSize) {
|
||||
throw new Exception(I18n::translate('upload.error.iniPostSize'));
|
||||
throw new Exception(
|
||||
I18n::translate(
|
||||
'upload.error.iniPostSize',
|
||||
'The uploaded file exceeds the post_max_size directive in php.ini'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
throw new Exception(I18n::translate('upload.error.noFiles'));
|
||||
throw new Exception(
|
||||
I18n::translate(
|
||||
'upload.error.noFiles',
|
||||
'No files were uploaded'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($files as $upload) {
|
||||
@@ -651,7 +661,8 @@ class Api
|
||||
try {
|
||||
if ($upload['error'] !== 0) {
|
||||
throw new Exception(
|
||||
$errorMessages[$upload['error']] ?? I18n::translate('upload.error.default')
|
||||
$errorMessages[$upload['error']] ??
|
||||
I18n::translate('upload.error.default', 'The file could not be uploaded')
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -655,7 +655,7 @@ class App
|
||||
* specified by the path
|
||||
*
|
||||
* Example:
|
||||
* <?= App::image('some/page/myimage.jpg') ?>
|
||||
* <?= $kirby->image('some/page/myimage.jpg') ?>
|
||||
*
|
||||
* @todo merge with App::file()
|
||||
*/
|
||||
@@ -831,9 +831,9 @@ class App
|
||||
}
|
||||
}
|
||||
|
||||
$data['kirby'] = $data['kirby'] ?? $this;
|
||||
$data['site'] = $data['site'] ?? $data['kirby']->site();
|
||||
$data['parent'] = $data['parent'] ?? $data['site']->page();
|
||||
$data['kirby'] ??= $this;
|
||||
$data['site'] ??= $data['kirby']->site();
|
||||
$data['parent'] ??= $data['site']->page();
|
||||
|
||||
return (new KirbyTag($type, $value, $attr, $data, $this->options))->render();
|
||||
}
|
||||
@@ -1464,9 +1464,7 @@ class App
|
||||
protected function setRoles(array $roles = null): static
|
||||
{
|
||||
if ($roles !== null) {
|
||||
$this->roles = Roles::factory($roles, [
|
||||
'kirby' => $this
|
||||
]);
|
||||
$this->roles = Roles::factory($roles);
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -1480,9 +1478,7 @@ class App
|
||||
protected function setSite(Site|array $site = null): static
|
||||
{
|
||||
if (is_array($site) === true) {
|
||||
$site = new Site($site + [
|
||||
'kirby' => $this
|
||||
]);
|
||||
$site = new Site($site);
|
||||
}
|
||||
|
||||
$this->site = $site;
|
||||
@@ -1497,7 +1493,6 @@ class App
|
||||
return $this->site ??= new Site([
|
||||
'errorPageId' => $this->options['error'] ?? 'error',
|
||||
'homePageId' => $this->options['home'] ?? 'home',
|
||||
'kirby' => $this,
|
||||
'url' => $this->url('index'),
|
||||
]);
|
||||
}
|
||||
|
@@ -86,9 +86,7 @@ trait AppUsers
|
||||
protected function setUsers(array $users = null): static
|
||||
{
|
||||
if ($users !== null) {
|
||||
$this->users = Users::factory($users, [
|
||||
'kirby' => $this
|
||||
]);
|
||||
$this->users = Users::factory($users);
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -128,7 +126,6 @@ trait AppUsers
|
||||
{
|
||||
return $this->users ??= Users::load(
|
||||
$this->root('accounts'),
|
||||
['kirby' => $this]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -696,6 +696,10 @@ class Blueprint
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->sections[$name] instanceof Section) {
|
||||
return $this->sections[$name]; //@codeCoverageIgnore
|
||||
}
|
||||
|
||||
// get all props
|
||||
$props = $this->sections[$name];
|
||||
|
||||
@@ -703,7 +707,7 @@ class Blueprint
|
||||
$props['model'] = $this->model();
|
||||
|
||||
// create a new section object
|
||||
return new Section($props['type'], $props);
|
||||
return $this->sections[$name] = new Section($props['type'], $props);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -713,7 +717,10 @@ class Blueprint
|
||||
{
|
||||
return A::map(
|
||||
$this->sections,
|
||||
fn ($section) => $this->section($section['name'])
|
||||
fn ($section) => match (true) {
|
||||
$section instanceof Section => $section,
|
||||
default => $this->section($section['name'])
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -30,6 +30,11 @@ use Kirby\Form\Field\LayoutField;
|
||||
*/
|
||||
class Core
|
||||
{
|
||||
/**
|
||||
* Optional override for the auto-detected index root
|
||||
*/
|
||||
public static string|null $indexRoot = null;
|
||||
|
||||
protected array $cache = [];
|
||||
protected string $root;
|
||||
|
||||
@@ -316,7 +321,7 @@ class Core
|
||||
'i18n:translations' => fn (array $roots) => $roots['i18n'] . '/translations',
|
||||
'i18n:rules' => fn (array $roots) => $roots['i18n'] . '/rules',
|
||||
|
||||
'index' => fn (array $roots) => dirname(__DIR__, 3),
|
||||
'index' => fn (array $roots) => static::$indexRoot ?? dirname(__DIR__, 3),
|
||||
'assets' => fn (array $roots) => $roots['index'] . '/assets',
|
||||
'content' => fn (array $roots) => $roots['index'] . '/content',
|
||||
'media' => fn (array $roots) => $roots['index'] . '/media',
|
||||
|
@@ -229,9 +229,25 @@ trait FileActions
|
||||
// gather content
|
||||
$content = $props['content'] ?? [];
|
||||
|
||||
// make sure that a UUID gets generated and
|
||||
// added to content right away
|
||||
if (Uuids::enabled() === true) {
|
||||
// make sure that a UUID gets generated
|
||||
// and added to content right away
|
||||
if (
|
||||
Uuids::enabled() === true &&
|
||||
empty($content['uuid']) === true
|
||||
) {
|
||||
// sets the current uuid if it is the exact same file
|
||||
if ($file->exists() === true) {
|
||||
$existing = $file->parent()->file($file->filename());
|
||||
|
||||
if (
|
||||
$file->sha1() === $upload->sha1() &&
|
||||
$file->template() === $existing->template()
|
||||
) {
|
||||
// use existing content data if it is the exact same file
|
||||
$content = $existing->content()->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
$content['uuid'] ??= Uuid::generate();
|
||||
}
|
||||
|
||||
|
@@ -90,11 +90,9 @@ class Files extends Collection
|
||||
public static function factory(array $files, Page|Site|User $parent): static
|
||||
{
|
||||
$collection = new static([], $parent);
|
||||
$kirby = $parent->kirby();
|
||||
|
||||
foreach ($files as $props) {
|
||||
$props['collection'] = $collection;
|
||||
$props['kirby'] = $kirby;
|
||||
$props['parent'] = $parent;
|
||||
|
||||
$file = File::factory($props);
|
||||
|
@@ -135,9 +135,14 @@ class Helpers
|
||||
return true;
|
||||
});
|
||||
|
||||
$result = $action();
|
||||
|
||||
restore_error_handler();
|
||||
try {
|
||||
$result = $action();
|
||||
} finally {
|
||||
// always restore the error handler, even if the
|
||||
// action or the standard error handler threw an
|
||||
// exception; this avoids modifying global state
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
return $override ?? $result;
|
||||
}
|
||||
|
@@ -49,9 +49,11 @@ class License
|
||||
/**
|
||||
* Returns the activation date if available
|
||||
*/
|
||||
public function activation(string|IntlDateFormatter|null $format = null): int|string|null
|
||||
{
|
||||
return $this->activation !== null ? Str::date(strtotime($this->activation), $format) : null;
|
||||
public function activation(
|
||||
string|IntlDateFormatter|null $format = null,
|
||||
string|null $handler = null
|
||||
): int|string|null {
|
||||
return $this->activation !== null ? Str::date(strtotime($this->activation), $format, $handler) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,9 +87,11 @@ class License
|
||||
/**
|
||||
* Returns the purchase date if available
|
||||
*/
|
||||
public function date(string|IntlDateFormatter|null $format = null): int|string|null
|
||||
{
|
||||
return $this->date !== null ? Str::date(strtotime($this->date), $format) : null;
|
||||
public function date(
|
||||
string|IntlDateFormatter|null $format = null,
|
||||
string|null $handler = null
|
||||
): int|string|null {
|
||||
return $this->date !== null ? Str::date(strtotime($this->date), $format, $handler) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,14 +359,16 @@ class License
|
||||
/**
|
||||
* Returns the renewal date
|
||||
*/
|
||||
public function renewal(string|IntlDateFormatter|null $format = null): int|string|null
|
||||
{
|
||||
public function renewal(
|
||||
string|IntlDateFormatter|null $format = null,
|
||||
string|null $handler = null
|
||||
): int|string|null {
|
||||
if ($this->activation === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$time = strtotime('+3 years', $this->activation());
|
||||
return Str::date($time, $format);
|
||||
return Str::date($time, $format, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -784,10 +784,17 @@ abstract class ModelWithContent implements Identifiable
|
||||
]);
|
||||
}
|
||||
|
||||
$arguments = [static::CLASS_ALIAS => $this, 'values' => $form->data(), 'strings' => $form->strings(), 'languageCode' => $languageCode];
|
||||
return $this->commit('update', $arguments, function ($model, $values, $strings, $languageCode) {
|
||||
return $model->save($strings, $languageCode, true);
|
||||
});
|
||||
return $this->commit(
|
||||
'update',
|
||||
[
|
||||
static::CLASS_ALIAS => $this,
|
||||
'values' => $form->data(),
|
||||
'strings' => $form->strings(),
|
||||
'languageCode' => $languageCode
|
||||
],
|
||||
fn ($model, $values, $strings, $languageCode) =>
|
||||
$model->save($strings, $languageCode, true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -282,10 +282,16 @@ trait PageActions
|
||||
return $this;
|
||||
}
|
||||
|
||||
$arguments = ['page' => $this, 'status' => 'listed', 'position' => $num];
|
||||
$page = $this->commit('changeStatus', $arguments, function ($page, $status, $position) {
|
||||
return $page->publish()->changeNum($position);
|
||||
});
|
||||
$page = $this->commit(
|
||||
'changeStatus',
|
||||
[
|
||||
'page' => $this,
|
||||
'status' => 'listed',
|
||||
'position' => $num
|
||||
],
|
||||
fn ($page, $status, $position) =>
|
||||
$page->publish()->changeNum($position)
|
||||
);
|
||||
|
||||
if ($this->blueprint()->num() === 'default') {
|
||||
$page->resortSiblingsAfterListing($num);
|
||||
@@ -303,10 +309,15 @@ trait PageActions
|
||||
return $this;
|
||||
}
|
||||
|
||||
$arguments = ['page' => $this, 'status' => 'unlisted', 'position' => null];
|
||||
$page = $this->commit('changeStatus', $arguments, function ($page) {
|
||||
return $page->publish()->changeNum(null);
|
||||
});
|
||||
$page = $this->commit(
|
||||
'changeStatus',
|
||||
[
|
||||
'page' => $this,
|
||||
'status' => 'unlisted',
|
||||
'position' => null
|
||||
],
|
||||
fn ($page) => $page->publish()->changeNum(null)
|
||||
);
|
||||
|
||||
$this->resortSiblingsAfterUnlisting();
|
||||
|
||||
@@ -355,7 +366,19 @@ trait PageActions
|
||||
string $title,
|
||||
string|null $languageCode = null
|
||||
): static {
|
||||
// if the `$languageCode` argument is not set and is not the default language
|
||||
// the `$languageCode` argument is sent as the current language
|
||||
if (
|
||||
$languageCode === null &&
|
||||
$language = $this->kirby()->language()
|
||||
) {
|
||||
if ($language->isDefault() === false) {
|
||||
$languageCode = $language->code();
|
||||
}
|
||||
}
|
||||
|
||||
$arguments = ['page' => $this, 'title' => $title, 'languageCode' => $languageCode];
|
||||
|
||||
return $this->commit('changeTitle', $arguments, function ($page, $title, $languageCode) {
|
||||
$page = $page->save(['title' => $title], $languageCode);
|
||||
|
||||
|
@@ -147,7 +147,6 @@ class Pages extends Collection
|
||||
): static {
|
||||
$model ??= App::instance()->site();
|
||||
$children = new static([], $model);
|
||||
$kirby = $model->kirby();
|
||||
|
||||
if ($model instanceof Page) {
|
||||
$parent = $model;
|
||||
@@ -158,7 +157,6 @@ class Pages extends Collection
|
||||
}
|
||||
|
||||
foreach ($pages as $props) {
|
||||
$props['kirby'] = $kirby;
|
||||
$props['parent'] = $parent;
|
||||
$props['site'] = $site;
|
||||
$props['isDraft'] = $draft ?? $props['isDraft'] ?? $props['draft'] ?? false;
|
||||
@@ -445,9 +443,10 @@ class Pages extends Collection
|
||||
$templates = [$templates];
|
||||
}
|
||||
|
||||
return $this->filter(function ($page) use ($templates) {
|
||||
return !in_array($page->intendedTemplate()->name(), $templates);
|
||||
});
|
||||
return $this->filter(
|
||||
fn ($page) =>
|
||||
!in_array($page->intendedTemplate()->name(), $templates)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -480,9 +479,10 @@ class Pages extends Collection
|
||||
$templates = [$templates];
|
||||
}
|
||||
|
||||
return $this->filter(function ($page) use ($templates) {
|
||||
return in_array($page->intendedTemplate()->name(), $templates);
|
||||
});
|
||||
return $this->filter(
|
||||
fn ($page) =>
|
||||
in_array($page->intendedTemplate()->name(), $templates)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -49,15 +49,26 @@ trait SiteActions
|
||||
*/
|
||||
public function changeTitle(
|
||||
string $title,
|
||||
string $languageCode = null
|
||||
string|null $languageCode = null
|
||||
): static {
|
||||
$site = $this;
|
||||
$title = trim($title);
|
||||
$arguments = compact('site', 'title', 'languageCode');
|
||||
// if the `$languageCode` argument is not set and is not the default language
|
||||
// the `$languageCode` argument is sent as the current language
|
||||
if (
|
||||
$languageCode === null &&
|
||||
$language = $this->kirby()->language()
|
||||
) {
|
||||
if ($language->isDefault() === false) {
|
||||
$languageCode = $language->code();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->commit('changeTitle', $arguments, function ($site, $title, $languageCode) {
|
||||
return $site->save(['title' => $title], $languageCode);
|
||||
});
|
||||
$arguments = ['site' => $this, 'title' => trim($title), 'languageCode' => $languageCode];
|
||||
|
||||
return $this->commit(
|
||||
'changeTitle',
|
||||
$arguments,
|
||||
fn ($site, $title, $languageCode) => $site->save(['title' => $title], $languageCode)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -33,13 +33,17 @@ class Structure extends Items
|
||||
array $items = null,
|
||||
array $params = []
|
||||
): static {
|
||||
// Bake-in index as ID for all items
|
||||
// TODO: remove when adding UUID supports to Structures
|
||||
if (is_array($items) === true) {
|
||||
$items = array_map(function ($item, $index) {
|
||||
if (is_array($item) === true) {
|
||||
// pass a clean content array without special `Item` keys
|
||||
$item['content'] = $item;
|
||||
|
||||
// bake-in index as ID for all items
|
||||
// TODO: remove when adding UUID supports to Structures
|
||||
$item['id'] ??= $index;
|
||||
}
|
||||
|
||||
return $item;
|
||||
}, $items, array_keys($items));
|
||||
}
|
||||
|
@@ -566,7 +566,7 @@ Database::$types['mysql'] = [
|
||||
$parts[] = 'dbname=' . $params['database'];
|
||||
}
|
||||
|
||||
$parts[] = 'charset=' . ($params['charset'] ?? 'utf8');
|
||||
$parts[] = 'charset=' . ($params['charset'] ?? 'utf8mb4');
|
||||
|
||||
return 'mysql:' . implode(';', $parts);
|
||||
}
|
||||
|
@@ -47,7 +47,8 @@ class Db
|
||||
'password' => Config::get('db.password', ''),
|
||||
'database' => Config::get('db.database', ''),
|
||||
'prefix' => Config::get('db.prefix', ''),
|
||||
'port' => Config::get('db.port', '')
|
||||
'port' => Config::get('db.port', ''),
|
||||
'charset' => Config::get('db.charset')
|
||||
];
|
||||
|
||||
return static::$connection = new Database($params);
|
||||
|
@@ -169,7 +169,10 @@ class Dir
|
||||
$result[] = $entry;
|
||||
|
||||
if ($recursive === true && is_dir($root) === true) {
|
||||
$result = array_merge($result, static::index($root, true, $ignore, $entry));
|
||||
$result = [
|
||||
...$result,
|
||||
...static::index($root, true, $ignore, $entry)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,10 +405,8 @@ class Dir
|
||||
|
||||
$parent = dirname($dir);
|
||||
|
||||
if ($recursive === true) {
|
||||
if (is_dir($parent) === false) {
|
||||
static::make($parent, true);
|
||||
}
|
||||
if ($recursive === true && is_dir($parent) === false) {
|
||||
static::make($parent, true);
|
||||
}
|
||||
|
||||
if (is_writable($parent) === false) {
|
||||
@@ -429,18 +430,19 @@ class Dir
|
||||
* @param 'date'|'intl'|'strftime'|null $handler Custom date handler or `null`
|
||||
* for the globally configured one
|
||||
*/
|
||||
public static function modified(string $dir, string $format = null, string|null $handler = null): int|string
|
||||
{
|
||||
public static function modified(
|
||||
string $dir,
|
||||
string $format = null,
|
||||
string|null $handler = null
|
||||
): int|string {
|
||||
$modified = filemtime($dir);
|
||||
$items = static::read($dir);
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (is_file($dir . '/' . $item) === true) {
|
||||
$newModified = filemtime($dir . '/' . $item);
|
||||
} else {
|
||||
$newModified = static::modified($dir . '/' . $item);
|
||||
}
|
||||
|
||||
$newModified = match (is_file($dir . '/' . $item)) {
|
||||
true => filemtime($dir . '/' . $item),
|
||||
false => static::modified($dir . '/' . $item)
|
||||
};
|
||||
$modified = ($newModified > $modified) ? $newModified : $modified;
|
||||
}
|
||||
|
||||
@@ -595,7 +597,10 @@ class Dir
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_dir($subdir) === true && static::wasModifiedAfter($subdir, $time) === true) {
|
||||
if (
|
||||
is_dir($subdir) === true &&
|
||||
static::wasModifiedAfter($subdir, $time) === true
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -800,11 +800,11 @@ class F
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
return filesize($file);
|
||||
} catch (Throwable) {
|
||||
return 0;
|
||||
if ($size = @filesize($file)) {
|
||||
return $size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -89,6 +89,7 @@ class Filename
|
||||
'blur' => $this->blur(),
|
||||
'bw' => $this->grayscale(),
|
||||
'q' => $this->quality(),
|
||||
'sharpen' => $this->sharpen(),
|
||||
];
|
||||
|
||||
$array = array_filter(
|
||||
@@ -244,6 +245,18 @@ class Filename
|
||||
return F::safeBasename($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the sharpen option value
|
||||
*/
|
||||
public function sharpen(): int|false
|
||||
{
|
||||
return match ($this->attributes['sharpen'] ?? false) {
|
||||
false => false,
|
||||
true => 50,
|
||||
default => (int)$this->attributes['sharpen']
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the converted filename as string
|
||||
*/
|
||||
|
@@ -296,7 +296,7 @@ class Form
|
||||
$kirby = App::instance(null, true);
|
||||
|
||||
// only modify the fields if we have a valid Kirby multilang instance
|
||||
if ($kirby?->multilang() === false) {
|
||||
if ($kirby?->multilang() !== true) {
|
||||
return $fields;
|
||||
}
|
||||
|
||||
|
@@ -322,10 +322,15 @@ class Environment
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
$sapi = php_sapi_name();
|
||||
if ($sapi === 'cli') {
|
||||
return true;
|
||||
}
|
||||
|
||||
$term = getenv('TERM');
|
||||
|
||||
if (
|
||||
substr(PHP_SAPI, 0, 3) === 'cgi' &&
|
||||
substr($sapi, 0, 3) === 'cgi' &&
|
||||
$term &&
|
||||
$term !== 'unknown'
|
||||
) {
|
||||
|
@@ -60,6 +60,7 @@ class Darkroom
|
||||
'quality' => 90,
|
||||
'scaleHeight' => null,
|
||||
'scaleWidth' => null,
|
||||
'sharpen' => null,
|
||||
'width' => null,
|
||||
];
|
||||
}
|
||||
@@ -93,6 +94,11 @@ class Darkroom
|
||||
unset($options['bw']);
|
||||
}
|
||||
|
||||
// normalize the sharpen option
|
||||
if ($options['sharpen'] === true) {
|
||||
$options['sharpen'] = 50;
|
||||
}
|
||||
|
||||
$options['quality'] ??= $this->settings['quality'];
|
||||
|
||||
return $options;
|
||||
|
@@ -33,6 +33,7 @@ class GdLib extends Darkroom
|
||||
$image = $this->autoOrient($image, $options);
|
||||
$image = $this->blur($image, $options);
|
||||
$image = $this->grayscale($image, $options);
|
||||
$image = $this->sharpen($image, $options);
|
||||
|
||||
$image->toFile($file, $mime, $options);
|
||||
|
||||
@@ -116,6 +117,18 @@ class GdLib extends Darkroom
|
||||
return $image->desaturate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies sharpening if activated in the options.
|
||||
*/
|
||||
protected function sharpen(SimpleImage $image, array $options): SimpleImage
|
||||
{
|
||||
if (is_int($options['sharpen']) === false) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
return $image->sharpen($options['sharpen']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns mime type based on `format` option
|
||||
*/
|
||||
|
@@ -65,14 +65,6 @@ class ImageMagick extends Darkroom
|
||||
// limit to single-threading to keep CPU usage sane
|
||||
$command .= ' -limit thread 1';
|
||||
|
||||
// add JPEG size hint to optimize CPU and memory usage
|
||||
if (F::mime($file) === 'image/jpeg') {
|
||||
// add hint only when downscaling
|
||||
if ($options['scaleWidth'] < 1 && $options['scaleHeight'] < 1) {
|
||||
$command .= ' -define ' . escapeshellarg(sprintf('jpeg:size=%dx%d', $options['width'], $options['height']));
|
||||
}
|
||||
}
|
||||
|
||||
// append input file
|
||||
return $command . ' ' . escapeshellarg($file);
|
||||
}
|
||||
@@ -100,6 +92,19 @@ class ImageMagick extends Darkroom
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies sharpening if activated in the options.
|
||||
*/
|
||||
protected function sharpen(string $file, array $options): string|null
|
||||
{
|
||||
if (is_int($options['sharpen']) === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$amount = max(1, min(100, $options['sharpen'])) / 100;
|
||||
return '-sharpen ' . escapeshellarg('0x' . $amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the correct settings for interlaced JPEGs if
|
||||
* activated via options
|
||||
@@ -133,6 +138,7 @@ class ImageMagick extends Darkroom
|
||||
$command[] = $this->resize($file, $options);
|
||||
$command[] = $this->quality($file, $options);
|
||||
$command[] = $this->blur($file, $options);
|
||||
$command[] = $this->sharpen($file, $options);
|
||||
$command[] = $this->save($file, $options);
|
||||
|
||||
// remove all null values and join the parts
|
||||
|
@@ -76,10 +76,11 @@ class Image extends File
|
||||
}
|
||||
|
||||
if (in_array($this->mime(), [
|
||||
'image/avif',
|
||||
'image/gif',
|
||||
'image/jpeg',
|
||||
'image/jp2',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/webp'
|
||||
])) {
|
||||
return $this->dimensions = Dimensions::forImage($this->root);
|
||||
|
@@ -159,7 +159,9 @@ class OptionsQuery extends OptionsProvider
|
||||
}
|
||||
|
||||
if ($result instanceof Collection === false) {
|
||||
throw new InvalidArgumentException('Invalid query result data: ' . get_class($result));
|
||||
$type = is_object($result) === true ? get_class($result) : gettype($result);
|
||||
|
||||
throw new InvalidArgumentException('Invalid query result data: ' . $type);
|
||||
}
|
||||
|
||||
// create options array
|
||||
|
@@ -103,6 +103,18 @@ abstract class Model
|
||||
return null;
|
||||
}
|
||||
|
||||
// switched off from blueprint,
|
||||
// only if not overwritten by $settings
|
||||
$blueprint = $this->model->blueprint()->image();
|
||||
|
||||
if ($blueprint === false) {
|
||||
if (empty($settings) === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$blueprint = null;
|
||||
}
|
||||
|
||||
// skip image thumbnail if option
|
||||
// is explicitly set to show the icon
|
||||
if ($settings === 'icon') {
|
||||
@@ -116,7 +128,7 @@ abstract class Model
|
||||
$settings = array_merge(
|
||||
$this->imageDefaults(),
|
||||
$settings ?? [],
|
||||
$this->model->blueprint()->image() ?? [],
|
||||
$blueprint ?? [],
|
||||
);
|
||||
|
||||
if ($image = $this->imageSource($settings['query'] ?? null)) {
|
||||
|
@@ -105,19 +105,37 @@ class PageCreateDialog
|
||||
*/
|
||||
public function coreFields(): array
|
||||
{
|
||||
$title = $this->blueprint()->create()['title']['label'] ?? 'title';
|
||||
$fields = [];
|
||||
|
||||
return [
|
||||
'title' => Field::title([
|
||||
'label' => I18n::translate($title, $title),
|
||||
$title = $this->blueprint()->create()['title'] ?? null;
|
||||
$slug = $this->blueprint()->create()['slug'] ?? null;
|
||||
|
||||
if ($title === false || $slug === false) {
|
||||
throw new InvalidArgumentException('Page create dialog: title and slug must not be false');
|
||||
}
|
||||
|
||||
// title field
|
||||
if ($title === null || is_array($title) === true) {
|
||||
$label = $title['label'] ?? 'title';
|
||||
$fields['title'] = Field::title([
|
||||
...$title ?? [],
|
||||
'label' => I18n::translate($label, $label),
|
||||
'required' => true,
|
||||
'preselect' => true
|
||||
]),
|
||||
'slug' => Field::slug([
|
||||
]);
|
||||
}
|
||||
|
||||
// slug field
|
||||
if ($slug === null) {
|
||||
$fields['slug'] = Field::slug([
|
||||
'required' => true,
|
||||
'sync' => 'title',
|
||||
'path' => $this->parent instanceof Page ? '/' . $this->parent->id() . '/' : '/'
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
return [
|
||||
...$fields,
|
||||
'parent' => Field::hidden(),
|
||||
'section' => Field::hidden(),
|
||||
'template' => Field::hidden(),
|
||||
@@ -171,10 +189,10 @@ class PageCreateDialog
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
return array_merge(
|
||||
$this->coreFields(),
|
||||
$this->customFields()
|
||||
);
|
||||
return [
|
||||
...$this->coreFields(),
|
||||
...$this->customFields()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,14 +206,29 @@ class PageCreateDialog
|
||||
|
||||
$this->template ??= $blueprints[0]['name'];
|
||||
|
||||
$status = $this->blueprint()->create()['status'] ?? 'draft';
|
||||
$status = $this->blueprint()->status()[$status]['label'] ?? I18n::translate('page.status.' . $status);
|
||||
$status = $this->blueprint()->create()['status'] ?? 'draft';
|
||||
$status = $this->blueprint()->status()[$status]['label'] ?? null;
|
||||
$status ??= I18n::translate('page.status.' . $status);
|
||||
|
||||
$fields = $this->fields();
|
||||
$visible = array_filter(
|
||||
$fields,
|
||||
fn ($field) => ($field['hidden'] ?? null) !== true
|
||||
);
|
||||
|
||||
// immediately submit the dialog if there is no editable field
|
||||
if (count($visible) === 0 && count($blueprints) < 2) {
|
||||
$input = $this->value();
|
||||
$response = $this->submit($input);
|
||||
$response['redirect'] ??= $this->parent->panel()->url(true);
|
||||
Panel::go($response['redirect']);
|
||||
}
|
||||
|
||||
return [
|
||||
'component' => 'k-page-create-dialog',
|
||||
'props' => [
|
||||
'blueprints' => $blueprints,
|
||||
'fields' => $this->fields(),
|
||||
'fields' => $fields,
|
||||
'submitButton' => I18n::template('page.create', [
|
||||
'status' => $status
|
||||
]),
|
||||
@@ -220,24 +253,56 @@ class PageCreateDialog
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates values for title and slug
|
||||
* from template strings from the blueprint
|
||||
*/
|
||||
public function resolveFieldTemplates(array $input): array
|
||||
{
|
||||
$title = $this->blueprint()->create()['title'] ?? null;
|
||||
$slug = $this->blueprint()->create()['slug'] ?? null;
|
||||
|
||||
// create temporary page object
|
||||
// to resolve the template strings
|
||||
$page = new Page([
|
||||
'slug' => 'tmp',
|
||||
'template' => $this->template,
|
||||
'parent' => $this->model(),
|
||||
'content' => $input
|
||||
]);
|
||||
|
||||
if (is_string($title)) {
|
||||
$input['title'] = $page->toSafeString($title);
|
||||
}
|
||||
|
||||
if (is_string($slug)) {
|
||||
$input['slug'] = $page->toSafeString($slug);
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares and cleans up the input data
|
||||
*/
|
||||
public function sanitize(array $input): array
|
||||
{
|
||||
$input['slug'] ??= $this->slug ?? '';
|
||||
$input['title'] ??= $this->title ?? '';
|
||||
$input['slug'] ??= $this->slug ?? '';
|
||||
|
||||
$content = [
|
||||
'title' => trim($input['title']),
|
||||
];
|
||||
$input = $this->resolveFieldTemplates($input);
|
||||
$content = ['title' => trim($input['title'])];
|
||||
|
||||
foreach ($this->customFields() as $name => $field) {
|
||||
$content[$name] = $input[$name] ?? null;
|
||||
}
|
||||
|
||||
// create temporary form to sanitize the input
|
||||
// and add default values
|
||||
$form = Form::for($this->model(), ['values' => $content]);
|
||||
|
||||
return [
|
||||
'content' => $content,
|
||||
'content' => $form->strings(true),
|
||||
'slug' => $input['slug'],
|
||||
'template' => $this->template,
|
||||
];
|
||||
@@ -301,13 +366,22 @@ class PageCreateDialog
|
||||
|
||||
public function value(): array
|
||||
{
|
||||
return [
|
||||
$value = [
|
||||
'parent' => $this->parentId,
|
||||
'section' => $this->sectionId,
|
||||
'slug' => $this->slug ?? '',
|
||||
'slug' => '',
|
||||
'template' => $this->template,
|
||||
'title' => $this->title ?? '',
|
||||
'title' => '',
|
||||
'view' => $this->viewId,
|
||||
];
|
||||
|
||||
// add default values for custom fields
|
||||
foreach ($this->customFields() as $name => $field) {
|
||||
if ($default = $field['default'] ?? null) {
|
||||
$value[$name] = $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
@@ -11,7 +11,6 @@ use Kirby\Http\Cookie;
|
||||
use Kirby\Http\Url;
|
||||
use Kirby\Toolkit\Str;
|
||||
use Kirby\Toolkit\SymmetricCrypto;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @package Kirby Session
|
||||
@@ -661,6 +660,7 @@ class Session
|
||||
// skip if we don't have the key (only the case for moved sessions)
|
||||
$hmac = Str::before($data, "\n");
|
||||
$data = trim(Str::after($data, "\n"));
|
||||
|
||||
if (
|
||||
$this->tokenKey !== null &&
|
||||
hash_equals(hash_hmac('sha256', $data, $this->tokenKey), $hmac) !== true
|
||||
@@ -675,16 +675,15 @@ class Session
|
||||
}
|
||||
|
||||
// decode the serialized data
|
||||
try {
|
||||
$data = unserialize($data);
|
||||
} catch (Throwable $e) {
|
||||
$data = @unserialize($data);
|
||||
|
||||
if ($data === false) {
|
||||
throw new LogicException([
|
||||
'key' => 'session.invalid',
|
||||
'data' => ['token' => $this->token()],
|
||||
'fallback' => 'Session "' . $this->token() . '" is invalid',
|
||||
'translate' => false,
|
||||
'httpCode' => 500,
|
||||
'previous' => $e
|
||||
'httpCode' => 500
|
||||
]);
|
||||
}
|
||||
|
||||
|
@@ -351,6 +351,31 @@ class A
|
||||
return in_array($value, $array, $strict);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join array elements as a string,
|
||||
* also supporting nested arrays
|
||||
*/
|
||||
public static function implode(
|
||||
array $array,
|
||||
string $separator = ''
|
||||
): string {
|
||||
$result = '';
|
||||
|
||||
foreach ($array as $value) {
|
||||
if (empty($result) === false) {
|
||||
$result .= $separator;
|
||||
}
|
||||
|
||||
if (is_array($value) === true) {
|
||||
$value = static::implode($value, $separator);
|
||||
}
|
||||
|
||||
$result .= $value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an array is associative or not
|
||||
*
|
||||
@@ -914,9 +939,7 @@ class A
|
||||
*
|
||||
* // with callback
|
||||
* A::update($user, [
|
||||
* 'username' => function ($username) {
|
||||
* return $username . ' j. simpson'
|
||||
* }
|
||||
* 'username' => fn ($username) => $username . ' j. simpson'
|
||||
* ]);
|
||||
* </code>
|
||||
*/
|
||||
|
@@ -1258,74 +1258,58 @@ Collection::$filters['!='] = function ($collection, $field, $test, $split = fals
|
||||
* In Filter
|
||||
*/
|
||||
Collection::$filters['in'] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return in_array($value, $test) === true;
|
||||
},
|
||||
'strict' => false
|
||||
'validator' => fn ($value, $test) => in_array($value, $test) === true,
|
||||
'strict' => false
|
||||
];
|
||||
|
||||
/**
|
||||
* Not In Filter
|
||||
*/
|
||||
Collection::$filters['not in'] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return in_array($value, $test) === false;
|
||||
},
|
||||
'validator' => fn ($value, $test) => in_array($value, $test) === false
|
||||
];
|
||||
|
||||
/**
|
||||
* Contains Filter
|
||||
*/
|
||||
Collection::$filters['*='] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return strpos($value, $test) !== false;
|
||||
},
|
||||
'strict' => false
|
||||
'validator' => fn ($value, $test) => strpos($value, $test) !== false,
|
||||
'strict' => false
|
||||
];
|
||||
|
||||
/**
|
||||
* Not Contains Filter
|
||||
*/
|
||||
Collection::$filters['!*='] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return strpos($value, $test) === false;
|
||||
},
|
||||
'validator' => fn ($value, $test) => strpos($value, $test) === false
|
||||
];
|
||||
|
||||
/**
|
||||
* More Filter
|
||||
*/
|
||||
Collection::$filters['>'] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return $value > $test;
|
||||
}
|
||||
'validator' => fn ($value, $test) => $value > $test
|
||||
];
|
||||
|
||||
/**
|
||||
* Min Filter
|
||||
*/
|
||||
Collection::$filters['>='] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return $value >= $test;
|
||||
}
|
||||
'validator' => fn ($value, $test) => $value >= $test
|
||||
];
|
||||
|
||||
/**
|
||||
* Less Filter
|
||||
*/
|
||||
Collection::$filters['<'] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return $value < $test;
|
||||
}
|
||||
'validator' => fn ($value, $test) => $value < $test
|
||||
];
|
||||
|
||||
/**
|
||||
* Max Filter
|
||||
*/
|
||||
Collection::$filters['<='] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return $value <= $test;
|
||||
}
|
||||
'validator' => fn ($value, $test) => $value <= $test
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -1340,9 +1324,7 @@ Collection::$filters['$='] = [
|
||||
* Not Ends With Filter
|
||||
*/
|
||||
Collection::$filters['!$='] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return V::endsWith($value, $test) === false;
|
||||
}
|
||||
'validator' => fn ($value, $test) => V::endsWith($value, $test) === false
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -1357,19 +1339,15 @@ Collection::$filters['^='] = [
|
||||
* Not Starts With Filter
|
||||
*/
|
||||
Collection::$filters['!^='] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return V::startsWith($value, $test) === false;
|
||||
}
|
||||
'validator' => fn ($value, $test) => V::startsWith($value, $test) === false
|
||||
];
|
||||
|
||||
/**
|
||||
* Between Filter
|
||||
*/
|
||||
Collection::$filters['between'] = Collection::$filters['..'] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return V::between($value, ...$test) === true;
|
||||
},
|
||||
'strict' => false
|
||||
'validator' => fn ($value, $test) => V::between($value, ...$test) === true,
|
||||
'strict' => false
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -1384,9 +1362,7 @@ Collection::$filters['*'] = [
|
||||
* Not Match Filter
|
||||
*/
|
||||
Collection::$filters['!*'] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return V::match($value, $test) === false;
|
||||
}
|
||||
'validator' => fn ($value, $test) => V::match($value, $test) === false
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -1421,63 +1397,49 @@ Collection::$filters['minwords'] = [
|
||||
* Date Equals Filter
|
||||
*/
|
||||
Collection::$filters['date =='] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return V::date($value, '==', $test);
|
||||
}
|
||||
'validator' => fn ($value, $test) => V::date($value, '==', $test)
|
||||
];
|
||||
|
||||
/**
|
||||
* Date Not Equals Filter
|
||||
*/
|
||||
Collection::$filters['date !='] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return V::date($value, '!=', $test);
|
||||
}
|
||||
'validator' => fn ($value, $test) => V::date($value, '!=', $test)
|
||||
];
|
||||
|
||||
/**
|
||||
* Date More Filter
|
||||
*/
|
||||
Collection::$filters['date >'] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return V::date($value, '>', $test);
|
||||
}
|
||||
'validator' => fn ($value, $test) => V::date($value, '>', $test)
|
||||
];
|
||||
|
||||
/**
|
||||
* Date Min Filter
|
||||
*/
|
||||
Collection::$filters['date >='] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return V::date($value, '>=', $test);
|
||||
}
|
||||
'validator' => fn ($value, $test) => V::date($value, '>=', $test)
|
||||
];
|
||||
|
||||
/**
|
||||
* Date Less Filter
|
||||
*/
|
||||
Collection::$filters['date <'] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return V::date($value, '<', $test);
|
||||
}
|
||||
'validator' => fn ($value, $test) => V::date($value, '<', $test)
|
||||
];
|
||||
|
||||
/**
|
||||
* Date Max Filter
|
||||
*/
|
||||
Collection::$filters['date <='] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return V::date($value, '<=', $test);
|
||||
}
|
||||
'validator' => fn ($value, $test) => V::date($value, '<=', $test)
|
||||
];
|
||||
|
||||
/**
|
||||
* Date Between Filter
|
||||
*/
|
||||
Collection::$filters['date between'] = Collection::$filters['date ..'] = [
|
||||
'validator' => function ($value, $test) {
|
||||
return
|
||||
'validator' => fn ($value, $test) =>
|
||||
V::date($value, '>=', $test[0]) &&
|
||||
V::date($value, '<=', $test[1]);
|
||||
}
|
||||
V::date($value, '<=', $test[1])
|
||||
];
|
||||
|
@@ -222,7 +222,7 @@ class Date extends DateTime
|
||||
*/
|
||||
public function microsecond(): int
|
||||
{
|
||||
return $this->format('u');
|
||||
return (int)$this->format('u');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,7 +230,7 @@ class Date extends DateTime
|
||||
*/
|
||||
public function millisecond(): int
|
||||
{
|
||||
return $this->format('v');
|
||||
return (int)$this->format('v');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -698,7 +698,7 @@ class Str
|
||||
string $string = null,
|
||||
string $needle,
|
||||
bool $caseInsensitive = false
|
||||
): int|bool {
|
||||
): int|false {
|
||||
if ($needle === '') {
|
||||
throw new InvalidArgumentException('The needle must not be empty');
|
||||
}
|
||||
|
@@ -134,16 +134,13 @@ class V
|
||||
$value = $params[$index] ?? null;
|
||||
|
||||
if (is_array($value) === true) {
|
||||
try {
|
||||
foreach ($value as $key => $item) {
|
||||
if (is_array($item) === true) {
|
||||
$value[$key] = implode('|', $item);
|
||||
}
|
||||
foreach ($value as $key => $item) {
|
||||
if (is_array($item) === true) {
|
||||
$value[$key] = A::implode($item, '|');
|
||||
}
|
||||
$value = implode(', ', $value);
|
||||
} catch (Throwable) {
|
||||
$value = '-';
|
||||
}
|
||||
|
||||
$value = implode(', ', $value);
|
||||
}
|
||||
|
||||
$arguments[$parameter->getName()] = $value;
|
||||
|
8
kirby/vendor/composer/installed.php
vendored
8
kirby/vendor/composer/installed.php
vendored
@@ -1,8 +1,8 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => 'getkirby/cms',
|
||||
'pretty_version' => '4.0.3',
|
||||
'version' => '4.0.3.0',
|
||||
'pretty_version' => '4.1.0',
|
||||
'version' => '4.1.0.0',
|
||||
'reference' => NULL,
|
||||
'type' => 'kirby-cms',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
@@ -47,8 +47,8 @@
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'getkirby/cms' => array(
|
||||
'pretty_version' => '4.0.3',
|
||||
'version' => '4.0.3.0',
|
||||
'pretty_version' => '4.1.0',
|
||||
'version' => '4.1.0.0',
|
||||
'reference' => NULL,
|
||||
'type' => 'kirby-cms',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
|
Reference in New Issue
Block a user