Update to Kirby 5
This commit is contained in:
parent
5d9979fca8
commit
0fefc5e2e1
472 changed files with 30853 additions and 10301 deletions
|
@ -12,7 +12,7 @@
|
|||
],
|
||||
"require": {
|
||||
"php": "~8.3.0",
|
||||
"getkirby/cms": "^4.0",
|
||||
"getkirby/cms": "^5.0",
|
||||
"kirbyzone/sitemapper": "^1.2.1",
|
||||
"wearejust/kirby-twig": "^5.0"
|
||||
},
|
||||
|
|
51
composer.lock
generated
51
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": "d9d8444cbdb90e7d30acec264e245c6a",
|
||||
"content-hash": "b8851efcf787532ad7f58ff49d1beaba",
|
||||
"packages": [
|
||||
{
|
||||
"name": "christian-riesen/base32",
|
||||
|
@ -201,16 +201,16 @@
|
|||
},
|
||||
{
|
||||
"name": "filp/whoops",
|
||||
"version": "2.18.0",
|
||||
"version": "2.18.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filp/whoops.git",
|
||||
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e"
|
||||
"reference": "59a123a3d459c5a23055802237cb317f609867e5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e",
|
||||
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/59a123a3d459c5a23055802237cb317f609867e5",
|
||||
"reference": "59a123a3d459c5a23055802237cb317f609867e5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -260,7 +260,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/filp/whoops/issues",
|
||||
"source": "https://github.com/filp/whoops/tree/2.18.0"
|
||||
"source": "https://github.com/filp/whoops/tree/2.18.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -268,20 +268,20 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-15T12:00:00+00:00"
|
||||
"time": "2025-06-16T00:02:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "getkirby/cms",
|
||||
"version": "4.8.0",
|
||||
"version": "5.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/getkirby/kirby.git",
|
||||
"reference": "5292c17832dd34b0e5f3e98dea837a357ef037b6"
|
||||
"reference": "ddd11f9b4a23e0b5d306b146ca5d6b7a1c1909a9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/getkirby/kirby/zipball/5292c17832dd34b0e5f3e98dea837a357ef037b6",
|
||||
"reference": "5292c17832dd34b0e5f3e98dea837a357ef037b6",
|
||||
"url": "https://api.github.com/repos/getkirby/kirby/zipball/ddd11f9b4a23e0b5d306b146ca5d6b7a1c1909a9",
|
||||
"reference": "ddd11f9b4a23e0b5d306b146ca5d6b7a1c1909a9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -299,15 +299,15 @@
|
|||
"ext-mbstring": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-simplexml": "*",
|
||||
"filp/whoops": "2.18.0",
|
||||
"filp/whoops": "2.18.3",
|
||||
"getkirby/composer-installer": "^1.2.1",
|
||||
"laminas/laminas-escaper": "2.17.0",
|
||||
"michelf/php-smartypants": "1.8.1",
|
||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
|
||||
"php": "~8.2.0 || ~8.3.0 || ~8.4.0",
|
||||
"phpmailer/phpmailer": "6.10.0",
|
||||
"symfony/polyfill-intl-idn": "1.32.0",
|
||||
"symfony/polyfill-mbstring": "1.32.0",
|
||||
"symfony/yaml": "6.4.21"
|
||||
"symfony/yaml": "7.3.1"
|
||||
},
|
||||
"replace": {
|
||||
"symfony/polyfill-php72": "*"
|
||||
|
@ -319,6 +319,7 @@
|
|||
"ext-fileinfo": "Improved mime type detection for files",
|
||||
"ext-intl": "Improved i18n number formatting",
|
||||
"ext-memcached": "Support for the Memcached cache driver",
|
||||
"ext-redis": "Support for the Redis cache driver",
|
||||
"ext-sodium": "Support for the crypto class and more robust session handling",
|
||||
"ext-zip": "Support for ZIP archive file functions",
|
||||
"ext-zlib": "Sanitization and validation for svgz files"
|
||||
|
@ -371,7 +372,7 @@
|
|||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"time": "2025-06-03T09:52:03+00:00"
|
||||
"time": "2025-07-03T11:23:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "getkirby/composer-installer",
|
||||
|
@ -1157,28 +1158,28 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v6.4.21",
|
||||
"version": "v7.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "f01987f45676778b474468aa266fe2eda1f2bc7e"
|
||||
"reference": "0c3555045a46ab3cd4cc5a69d161225195230edb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/f01987f45676778b474468aa266fe2eda1f2bc7e",
|
||||
"reference": "f01987f45676778b474468aa266fe2eda1f2bc7e",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/0c3555045a46ab3cd4cc5a69d161225195230edb",
|
||||
"reference": "0c3555045a46ab3cd4cc5a69d161225195230edb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3.0",
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/console": "<5.4"
|
||||
"symfony/console": "<6.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/console": "^5.4|^6.0|^7.0"
|
||||
"symfony/console": "^6.4|^7.0"
|
||||
},
|
||||
"bin": [
|
||||
"Resources/bin/yaml-lint"
|
||||
|
@ -1209,7 +1210,7 @@
|
|||
"description": "Loads and dumps YAML files",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/yaml/tree/v6.4.21"
|
||||
"source": "https://github.com/symfony/yaml/tree/v7.3.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1225,7 +1226,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-04T09:48:44+00:00"
|
||||
"time": "2025-06-03T06:57:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
|
|
|
@ -21,6 +21,10 @@ insert_final_newline = true
|
|||
indent_size = 2
|
||||
insert_final_newline = false
|
||||
|
||||
[views/**/*.php]
|
||||
indent_size = 2
|
||||
insert_final_newline = false
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* stop at older or too recent versions
|
||||
*/
|
||||
if (
|
||||
version_compare(PHP_VERSION, '8.1.0', '>=') === false ||
|
||||
version_compare(PHP_VERSION, '8.2.0', '>=') === false ||
|
||||
version_compare(PHP_VERSION, '8.5.0', '<') === false
|
||||
) {
|
||||
die(include __DIR__ . '/views/php.php');
|
||||
|
@ -27,10 +27,9 @@ if (is_file($autoloader = dirname(__DIR__) . '/vendor/autoload.php')) {
|
|||
* @psalm-suppress MissingFile
|
||||
*/
|
||||
include $autoloader;
|
||||
} else {
|
||||
/**
|
||||
}
|
||||
/**
|
||||
* If neither one exists, don't bother searching;
|
||||
* it's a custom directory setup and the users need to
|
||||
* load the autoloader themselves
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"description": "The Kirby core",
|
||||
"license": "proprietary",
|
||||
"type": "kirby-cms",
|
||||
"version": "4.8.0",
|
||||
"version": "5.0.1",
|
||||
"keywords": [
|
||||
"kirby",
|
||||
"cms",
|
||||
|
@ -24,7 +24,7 @@
|
|||
"source": "https://github.com/getkirby/kirby"
|
||||
},
|
||||
"require": {
|
||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
|
||||
"php": "~8.2.0 || ~8.3.0 || ~8.4.0",
|
||||
"ext-SimpleXML": "*",
|
||||
"ext-ctype": "*",
|
||||
"ext-curl": "*",
|
||||
|
@ -39,14 +39,14 @@
|
|||
"christian-riesen/base32": "1.6.0",
|
||||
"claviska/simpleimage": "4.2.1",
|
||||
"composer/semver": "3.4.3",
|
||||
"filp/whoops": "2.18.0",
|
||||
"filp/whoops": "2.18.3",
|
||||
"getkirby/composer-installer": "^1.2.1",
|
||||
"laminas/laminas-escaper": "2.17.0",
|
||||
"michelf/php-smartypants": "1.8.1",
|
||||
"phpmailer/phpmailer": "6.10.0",
|
||||
"symfony/polyfill-intl-idn": "1.32.0",
|
||||
"symfony/polyfill-mbstring": "1.32.0",
|
||||
"symfony/yaml": "6.4.21"
|
||||
"symfony/yaml": "7.3.1"
|
||||
},
|
||||
"replace": {
|
||||
"symfony/polyfill-php72": "*"
|
||||
|
@ -58,6 +58,7 @@
|
|||
"ext-fileinfo": "Improved mime type detection for files",
|
||||
"ext-intl": "Improved i18n number formatting",
|
||||
"ext-memcached": "Support for the Memcached cache driver",
|
||||
"ext-redis": "Support for the Redis cache driver",
|
||||
"ext-sodium": "Support for the crypto class and more robust session handling",
|
||||
"ext-zip": "Support for ZIP archive file functions",
|
||||
"ext-zlib": "Sanitization and validation for svgz files"
|
||||
|
@ -80,7 +81,7 @@
|
|||
},
|
||||
"optimize-autoloader": true,
|
||||
"platform": {
|
||||
"php": "8.1.0"
|
||||
"php": "8.2.0"
|
||||
},
|
||||
"platform-check": false
|
||||
},
|
||||
|
|
38
kirby/composer.lock
generated
38
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": "e70567909c74a3864f445abea10a85cc",
|
||||
"content-hash": "085c69c5d57912362ce380d23eaa8fbc",
|
||||
"packages": [
|
||||
{
|
||||
"name": "christian-riesen/base32",
|
||||
|
@ -201,16 +201,16 @@
|
|||
},
|
||||
{
|
||||
"name": "filp/whoops",
|
||||
"version": "2.18.0",
|
||||
"version": "2.18.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filp/whoops.git",
|
||||
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e"
|
||||
"reference": "59a123a3d459c5a23055802237cb317f609867e5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e",
|
||||
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/59a123a3d459c5a23055802237cb317f609867e5",
|
||||
"reference": "59a123a3d459c5a23055802237cb317f609867e5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -260,7 +260,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/filp/whoops/issues",
|
||||
"source": "https://github.com/filp/whoops/tree/2.18.0"
|
||||
"source": "https://github.com/filp/whoops/tree/2.18.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -268,7 +268,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-15T12:00:00+00:00"
|
||||
"time": "2025-06-16T00:02:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "getkirby/composer-installer",
|
||||
|
@ -1017,28 +1017,28 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v6.4.21",
|
||||
"version": "v7.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "f01987f45676778b474468aa266fe2eda1f2bc7e"
|
||||
"reference": "0c3555045a46ab3cd4cc5a69d161225195230edb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/f01987f45676778b474468aa266fe2eda1f2bc7e",
|
||||
"reference": "f01987f45676778b474468aa266fe2eda1f2bc7e",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/0c3555045a46ab3cd4cc5a69d161225195230edb",
|
||||
"reference": "0c3555045a46ab3cd4cc5a69d161225195230edb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3.0",
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/console": "<5.4"
|
||||
"symfony/console": "<6.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/console": "^5.4|^6.0|^7.0"
|
||||
"symfony/console": "^6.4|^7.0"
|
||||
},
|
||||
"bin": [
|
||||
"Resources/bin/yaml-lint"
|
||||
|
@ -1069,7 +1069,7 @@
|
|||
"description": "Loads and dumps YAML files",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/yaml/tree/v6.4.21"
|
||||
"source": "https://github.com/symfony/yaml/tree/v7.3.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1085,7 +1085,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-04T09:48:44+00:00"
|
||||
"time": "2025-06-03T06:57:57+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
@ -1095,7 +1095,7 @@
|
|||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
|
||||
"php": "~8.2.0 || ~8.3.0 || ~8.4.0",
|
||||
"ext-simplexml": "*",
|
||||
"ext-ctype": "*",
|
||||
"ext-curl": "*",
|
||||
|
@ -1110,7 +1110,7 @@
|
|||
},
|
||||
"platform-dev": {},
|
||||
"platform-overrides": {
|
||||
"php": "8.1.0"
|
||||
"php": "8.2.0"
|
||||
},
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
|
|
@ -75,7 +75,6 @@ return [
|
|||
// Any of these might be removed at any point in the future
|
||||
'kirby\cms\asset' => 'Kirby\Filesystem\Asset',
|
||||
'kirby\cms\content' => 'Kirby\Content\Content',
|
||||
'kirby\cms\contenttranslation' => 'Kirby\Content\ContentTranslation',
|
||||
'kirby\cms\dir' => 'Kirby\Filesystem\Dir',
|
||||
'kirby\cms\filename' => 'Kirby\Filesystem\Filename',
|
||||
'kirby\cms\filefoundation' => 'Kirby\Filesystem\IsFile',
|
||||
|
@ -83,6 +82,9 @@ return [
|
|||
'kirby\cms\form' => 'Kirby\Form\Form',
|
||||
'kirby\cms\kirbytag' => 'Kirby\Text\KirbyTag',
|
||||
'kirby\cms\kirbytags' => 'Kirby\Text\KirbyTags',
|
||||
'kirby\cms\plugin' => 'Kirby\Plugin\Plugin',
|
||||
'kirby\cms\pluginasset' => 'Kirby\Plugin\Asset',
|
||||
'kirby\cms\pluginassets' => 'Kirby\Plugin\Assets',
|
||||
'kirby\cms\template' => 'Kirby\Template\Template',
|
||||
'kirby\form\options' => 'Kirby\Option\Options',
|
||||
'kirby\form\optionsapi' => 'Kirby\Option\OptionsApi',
|
||||
|
|
|
@ -11,17 +11,17 @@ return function () {
|
|||
$auth->type($allowImpersonation) === 'session' &&
|
||||
$auth->csrf() === false
|
||||
) {
|
||||
throw new AuthException('Unauthenticated');
|
||||
throw new AuthException(message: 'Unauthenticated');
|
||||
}
|
||||
|
||||
// get user from session or basic auth
|
||||
if ($user = $auth->user(null, $allowImpersonation)) {
|
||||
if ($user->role()->permissions()->for('access', 'panel') === false) {
|
||||
throw new AuthException(['key' => 'access.panel']);
|
||||
throw new AuthException(key: 'access.panel');
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
throw new AuthException('Unauthenticated');
|
||||
throw new AuthException(message: 'Unauthenticated');
|
||||
};
|
||||
|
|
|
@ -4,25 +4,25 @@
|
|||
* Api Routes Definitions
|
||||
*/
|
||||
return function ($kirby) {
|
||||
$routes = array_merge(
|
||||
include __DIR__ . '/routes/auth.php',
|
||||
include __DIR__ . '/routes/pages.php',
|
||||
include __DIR__ . '/routes/roles.php',
|
||||
include __DIR__ . '/routes/site.php',
|
||||
include __DIR__ . '/routes/users.php',
|
||||
include __DIR__ . '/routes/files.php',
|
||||
include __DIR__ . '/routes/lock.php',
|
||||
include __DIR__ . '/routes/system.php',
|
||||
include __DIR__ . '/routes/translations.php'
|
||||
);
|
||||
$routes = [
|
||||
...include __DIR__ . '/routes/auth.php',
|
||||
...include __DIR__ . '/routes/changes.php',
|
||||
...include __DIR__ . '/routes/pages.php',
|
||||
...include __DIR__ . '/routes/roles.php',
|
||||
...include __DIR__ . '/routes/site.php',
|
||||
...include __DIR__ . '/routes/users.php',
|
||||
...include __DIR__ . '/routes/files.php',
|
||||
...include __DIR__ . '/routes/system.php',
|
||||
...include __DIR__ . '/routes/translations.php'
|
||||
];
|
||||
|
||||
// only add the language routes if the
|
||||
// multi language setup is activated
|
||||
if ($kirby->option('languages', false) !== false) {
|
||||
$routes = array_merge(
|
||||
$routes,
|
||||
include __DIR__ . '/routes/languages.php'
|
||||
);
|
||||
$routes = [
|
||||
...$routes,
|
||||
...include __DIR__ . '/routes/languages.php'
|
||||
];
|
||||
}
|
||||
|
||||
return $routes;
|
||||
|
|
|
@ -15,7 +15,9 @@ return [
|
|||
return $this->resolve($user)->view('auth');
|
||||
}
|
||||
|
||||
throw new NotFoundException('The user cannot be found');
|
||||
throw new NotFoundException(
|
||||
message: 'The user cannot be found'
|
||||
);
|
||||
}
|
||||
],
|
||||
[
|
||||
|
@ -27,7 +29,9 @@ return [
|
|||
|
||||
// csrf token check
|
||||
if ($auth->type() === 'session' && $auth->csrf() === false) {
|
||||
throw new InvalidArgumentException('Invalid CSRF token');
|
||||
throw new InvalidArgumentException(
|
||||
message: 'Invalid CSRF token'
|
||||
);
|
||||
}
|
||||
|
||||
$user = $auth->verifyChallenge($this->requestBody('code'));
|
||||
|
@ -49,7 +53,9 @@ return [
|
|||
|
||||
// csrf token check
|
||||
if ($auth->type() === 'session' && $auth->csrf() === false) {
|
||||
throw new InvalidArgumentException('Invalid CSRF token');
|
||||
throw new InvalidArgumentException(
|
||||
message: 'Invalid CSRF token'
|
||||
);
|
||||
}
|
||||
|
||||
$email = $this->requestBody('email');
|
||||
|
@ -58,7 +64,9 @@ return [
|
|||
|
||||
if ($password) {
|
||||
if (isset($methods['password']) !== true) {
|
||||
throw new InvalidArgumentException('Login with password is not enabled');
|
||||
throw new InvalidArgumentException(
|
||||
message: 'Login with password is not enabled'
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -73,7 +81,9 @@ return [
|
|||
$mode = match (true) {
|
||||
isset($methods['code']) => 'login',
|
||||
isset($methods['password-reset']) => 'password-reset',
|
||||
default => throw new InvalidArgumentException('Login without password is not enabled')
|
||||
default => throw new InvalidArgumentException(
|
||||
message: 'Login without password is not enabled'
|
||||
)
|
||||
};
|
||||
|
||||
$status = $auth->createChallenge($email, $long, $mode);
|
||||
|
|
37
kirby/config/api/routes/changes.php
Normal file
37
kirby/config/api/routes/changes.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Api\Controller\Changes;
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Find;
|
||||
|
||||
return [
|
||||
[
|
||||
'pattern' => '(:all)/changes/discard',
|
||||
'method' => 'POST',
|
||||
'action' => function (string $path) {
|
||||
return Changes::discard(
|
||||
model: Find::parent($path),
|
||||
);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/changes/publish',
|
||||
'method' => 'POST',
|
||||
'action' => function (string $path) {
|
||||
return Changes::publish(
|
||||
model: Find::parent($path),
|
||||
input: App::instance()->request()->get()
|
||||
);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/changes/save',
|
||||
'method' => 'POST',
|
||||
'action' => function (string $path) {
|
||||
return Changes::save(
|
||||
model: Find::parent($path),
|
||||
input: App::instance()->request()->get()
|
||||
);
|
||||
}
|
||||
],
|
||||
];
|
|
@ -47,7 +47,7 @@ return [
|
|||
// move_uploaded_file() not working with unit test
|
||||
// @codeCoverageIgnoreStart
|
||||
return $this->upload(function ($source, $filename) use ($path) {
|
||||
// move the source file from the temp dir
|
||||
// move the source file to the content folder
|
||||
return $this->parent($path)->createFile([
|
||||
'content' => [
|
||||
'sort' => $this->requestBody('sort')
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* Content Lock Routes
|
||||
*/
|
||||
|
||||
use Kirby\Exception\NotFoundException;
|
||||
|
||||
return [
|
||||
[
|
||||
'pattern' => '(:all)/lock',
|
||||
'method' => 'GET',
|
||||
'action' => function (string $path) {
|
||||
return [
|
||||
'lock' => $this->parent($path)->lock()?->toArray() ?? false
|
||||
];
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/lock',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $path) {
|
||||
return $this->parent($path)->lock()?->create();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/lock',
|
||||
'method' => 'DELETE',
|
||||
'action' => function (string $path) {
|
||||
try {
|
||||
return $this->parent($path)->lock()?->remove();
|
||||
} catch (NotFoundException) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/unlock',
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $path) {
|
||||
return $this->parent($path)->lock()?->unlock();
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => '(:all)/unlock',
|
||||
'method' => 'DELETE',
|
||||
'action' => function (string $path) {
|
||||
try {
|
||||
return $this->parent($path)->lock()?->resolve();
|
||||
} catch (NotFoundException) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
],
|
||||
];
|
|
@ -31,18 +31,6 @@ return [
|
|||
];
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'system/method-test',
|
||||
'method' => 'PATCH',
|
||||
'action' => function () {
|
||||
return [
|
||||
'status' => match ($this->kirby()->request()->method()) {
|
||||
'PATCH' => 'ok',
|
||||
default => 'fail'
|
||||
}
|
||||
];
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'system/register',
|
||||
'method' => 'POST',
|
||||
|
@ -60,19 +48,27 @@ return [
|
|||
|
||||
// csrf token check
|
||||
if ($auth->type() === 'session' && $auth->csrf() === false) {
|
||||
throw new InvalidArgumentException('Invalid CSRF token');
|
||||
throw new InvalidArgumentException(
|
||||
message: 'Invalid CSRF token'
|
||||
);
|
||||
}
|
||||
|
||||
if ($system->isOk() === false) {
|
||||
throw new Exception('The server is not setup correctly');
|
||||
throw new Exception(
|
||||
message: 'The server is not setup correctly'
|
||||
);
|
||||
}
|
||||
|
||||
if ($system->isInstallable() === false) {
|
||||
throw new Exception('The Panel cannot be installed');
|
||||
throw new Exception(
|
||||
message: 'The Panel cannot be installed'
|
||||
);
|
||||
}
|
||||
|
||||
if ($system->isInstalled() === true) {
|
||||
throw new Exception('The Panel is already installed');
|
||||
throw new Exception(
|
||||
message: 'The Panel is already installed'
|
||||
);
|
||||
}
|
||||
|
||||
// create the first user
|
||||
|
|
|
@ -86,18 +86,18 @@ return [
|
|||
function ($source, $filename) use ($id) {
|
||||
$type = F::type($filename);
|
||||
if ($type !== 'image') {
|
||||
throw new Exception([
|
||||
'key' => 'file.type.invalid',
|
||||
'data' => compact('type')
|
||||
]);
|
||||
throw new Exception(
|
||||
key: 'file.type.invalid',
|
||||
data: compact('type')
|
||||
);
|
||||
}
|
||||
|
||||
$mime = F::mime($source);
|
||||
if (Str::startsWith($mime, 'image/') !== true) {
|
||||
throw new Exception([
|
||||
'key' => 'file.mime.invalid',
|
||||
'data' => compact('mime')
|
||||
]);
|
||||
throw new Exception(
|
||||
key: 'file.mime.invalid',
|
||||
data: compact('mime')
|
||||
);
|
||||
}
|
||||
|
||||
// delete the old avatar
|
||||
|
@ -184,7 +184,23 @@ return [
|
|||
],
|
||||
'method' => 'PATCH',
|
||||
'action' => function (string $id) {
|
||||
return $this->user($id)->changePassword($this->requestBody('password'));
|
||||
$user = $this->user($id);
|
||||
|
||||
// validate password of acting user unless they have logged in to reset it;
|
||||
// always validate password of acting user when changing password of other users
|
||||
if ($this->session()->get('kirby.resetPassword') !== true || $this->user()->is($user) !== true) {
|
||||
$this->user()->validatePassword($this->requestBody('currentPassword'));
|
||||
}
|
||||
|
||||
$result = $user->changePassword($this->requestBody('password'));
|
||||
|
||||
// if we changed the password of the current user…
|
||||
if ($user->isLoggedIn() === true) {
|
||||
// …don't allow additional resets (now the password is known again)
|
||||
$this->session()->remove('kirby.resetPassword');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
],
|
||||
[
|
||||
|
|
|
@ -7,6 +7,7 @@ return function () {
|
|||
'icon' => 'account',
|
||||
'label' => I18n::translate('view.account'),
|
||||
'search' => 'users',
|
||||
'buttons' => require __DIR__ . '/account/buttons.php',
|
||||
'dialogs' => require __DIR__ . '/account/dialogs.php',
|
||||
'drawers' => require __DIR__ . '/account/drawers.php',
|
||||
'dropdowns' => require __DIR__ . '/account/dropdowns.php',
|
||||
|
|
13
kirby/config/areas/account/buttons.php
Normal file
13
kirby/config/areas/account/buttons.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\User;
|
||||
use Kirby\Panel\Ui\Buttons\ViewButton;
|
||||
|
||||
return [
|
||||
'user.theme' => function (App $kirby, User $user) {
|
||||
if ($kirby->user()->is($user) === true) {
|
||||
return new ViewButton(component: 'k-theme-view-button');
|
||||
}
|
||||
}
|
||||
];
|
|
@ -5,7 +5,6 @@ use Kirby\Panel\UserTotpEnableDialog;
|
|||
$dialogs = require __DIR__ . '/../users/dialogs.php';
|
||||
|
||||
return [
|
||||
|
||||
// change email
|
||||
'account.changeEmail' => [
|
||||
'pattern' => '(account)/changeEmail',
|
||||
|
|
|
@ -7,8 +7,16 @@ return [
|
|||
'pattern' => '(account)',
|
||||
'options' => $dropdowns['user']['options']
|
||||
],
|
||||
'account.languages' => [
|
||||
'pattern' => '(account)/languages',
|
||||
'options' => $dropdowns['user.languages']['options']
|
||||
],
|
||||
'account.file' => [
|
||||
'pattern' => '(account)/files/(:any)',
|
||||
'options' => $dropdowns['user.file']['options']
|
||||
],
|
||||
'account.file.languages' => [
|
||||
'pattern' => '(account)/files/(:any)/languages',
|
||||
'options' => $files['language']
|
||||
]
|
||||
];
|
||||
|
|
|
@ -26,6 +26,9 @@ return [
|
|||
[
|
||||
'label' => I18n::translate('view.resetPassword')
|
||||
]
|
||||
],
|
||||
'props' => [
|
||||
'requirePassword' => App::instance()->session()->get('kirby.resetPassword') !== true
|
||||
]
|
||||
]
|
||||
]
|
||||
|
|
14
kirby/config/areas/files/buttons.php
Normal file
14
kirby/config/areas/files/buttons.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\File;
|
||||
use Kirby\Panel\Ui\Buttons\OpenButton;
|
||||
use Kirby\Panel\Ui\Buttons\SettingsButton;
|
||||
|
||||
return [
|
||||
'file.open' => function (File $file) {
|
||||
return new OpenButton(link: $file->previewUrl());
|
||||
},
|
||||
'file.settings' => function (File $file) {
|
||||
return new SettingsButton(model: $file);
|
||||
}
|
||||
];
|
|
@ -45,13 +45,7 @@ return [
|
|||
$oldUrl = $file->panel()->url(true);
|
||||
$newUrl = $renamed->panel()->url(true);
|
||||
$response = [
|
||||
'event' => 'file.changeName',
|
||||
'dispatch' => [
|
||||
'content/move' => [
|
||||
$oldUrl,
|
||||
$newUrl
|
||||
]
|
||||
],
|
||||
'event' => 'file.changeName'
|
||||
];
|
||||
|
||||
// check for a necessary redirect after the filename has changed
|
||||
|
@ -163,7 +157,6 @@ return [
|
|||
|
||||
return [
|
||||
'event' => 'file.delete',
|
||||
'dispatch' => ['content/remove' => [$url]],
|
||||
'redirect' => $redirect
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\Find;
|
||||
use Kirby\Panel\Ui\Buttons\LanguagesDropdown;
|
||||
|
||||
return [
|
||||
'file' => function (string $parent, string $filename) {
|
||||
return Find::file($parent, $filename)->panel()->dropdown();
|
||||
},
|
||||
'language' => function (string $parent, string $filename) {
|
||||
$file = Find::file($parent, $filename);
|
||||
return (new LanguagesDropdown($file))->options();
|
||||
}
|
||||
];
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Panel\Lab\Doc;
|
||||
use Kirby\Panel\Lab\Docs;
|
||||
|
||||
return [
|
||||
'lab.docs' => [
|
||||
'pattern' => 'lab/docs/(:any)',
|
||||
'load' => function (string $component) {
|
||||
if (Docs::installed() === false) {
|
||||
if (Docs::isInstalled() === false) {
|
||||
return [
|
||||
'component' => 'k-text-drawer',
|
||||
'props' => [
|
||||
|
@ -15,14 +16,12 @@ return [
|
|||
];
|
||||
}
|
||||
|
||||
$docs = new Docs($component);
|
||||
|
||||
return [
|
||||
'component' => 'k-lab-docs-drawer',
|
||||
'props' => [
|
||||
'icon' => 'book',
|
||||
'title' => $component,
|
||||
'docs' => $docs->toArray()
|
||||
'docs' => Doc::factory($component)->toArray()
|
||||
]
|
||||
];
|
||||
},
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Panel\Lab\Category;
|
||||
use Kirby\Panel\Lab\Doc;
|
||||
use Kirby\Panel\Lab\Docs;
|
||||
|
||||
return [
|
||||
|
@ -12,7 +13,7 @@ return [
|
|||
'component' => 'k-lab-index-view',
|
||||
'props' => [
|
||||
'categories' => Category::all(),
|
||||
'info' => Category::installed() ? null : 'The default Lab examples are not installed.',
|
||||
'info' => Category::isInstalled() ? null : 'The default Lab examples are not installed.',
|
||||
'tab' => 'examples',
|
||||
],
|
||||
];
|
||||
|
@ -21,18 +22,7 @@ return [
|
|||
'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 [
|
||||
$view = [
|
||||
'component' => 'k-lab-index-view',
|
||||
'title' => 'Docs',
|
||||
'breadcrumb' => [
|
||||
|
@ -40,8 +30,28 @@ return [
|
|||
'label' => 'Docs',
|
||||
'link' => 'lab/docs'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// if docs are not installed, show info message
|
||||
if (Docs::isInstalled() === false) {
|
||||
return [
|
||||
...$view,
|
||||
'props' => [
|
||||
'info' => 'The UI docs are not installed.',
|
||||
'tab' => 'docs',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
...$view,
|
||||
'props' => [
|
||||
'categories' => [
|
||||
['examples' => Docs::all()]
|
||||
],
|
||||
'tab' => 'docs',
|
||||
],
|
||||
'props' => $props,
|
||||
];
|
||||
}
|
||||
],
|
||||
|
@ -59,7 +69,7 @@ return [
|
|||
]
|
||||
];
|
||||
|
||||
if (Docs::installed() === false) {
|
||||
if (Docs::isInstalled() === false) {
|
||||
return [
|
||||
'component' => 'k-lab-index-view',
|
||||
'title' => $component,
|
||||
|
@ -71,16 +81,50 @@ return [
|
|||
];
|
||||
}
|
||||
|
||||
$docs = new Docs($component);
|
||||
$doc = Doc::factory($component);
|
||||
|
||||
if ($doc === null) {
|
||||
return [
|
||||
'component' => 'k-lab-index-view',
|
||||
'title' => $component,
|
||||
'breadcrumb' => $crumbs,
|
||||
'props' => [
|
||||
'info' => 'No UI docs found for ' . $component . '.',
|
||||
'tab' => 'docs',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// header buttons
|
||||
$buttons = [];
|
||||
|
||||
if ($lab = $doc->lab()) {
|
||||
$buttons[] = [
|
||||
'props' => [
|
||||
'text' => 'Lab examples',
|
||||
'icon' => 'lab',
|
||||
'link' => '/lab/' . $lab
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
$buttons[] = [
|
||||
'props' => [
|
||||
'icon' => 'github',
|
||||
'link' => $doc->source(),
|
||||
'target' => '_blank'
|
||||
]
|
||||
];
|
||||
|
||||
return [
|
||||
'component' => 'k-lab-docs-view',
|
||||
'title' => $component,
|
||||
'breadcrumb' => $crumbs,
|
||||
'props' => [
|
||||
'buttons' => $buttons,
|
||||
'component' => $component,
|
||||
'docs' => $docs->toArray(),
|
||||
'lab' => $docs->lab()
|
||||
'docs' => $doc->toArray(),
|
||||
'lab' => $lab
|
||||
]
|
||||
];
|
||||
}
|
||||
|
@ -111,16 +155,39 @@ return [
|
|||
$vue = $example->vue();
|
||||
$compiler = App::instance()->option('panel.vue.compiler', true);
|
||||
|
||||
if (Docs::installed() === true && $docs = $props['docs'] ?? null) {
|
||||
$docs = new Docs($docs);
|
||||
if ($doc = $props['docs'] ?? null) {
|
||||
$doc = Doc::factory($doc);
|
||||
}
|
||||
|
||||
$github = $docs?->github();
|
||||
$github = $doc?->source();
|
||||
|
||||
if ($source = $props['source'] ?? null) {
|
||||
$github ??= 'https://github.com/getkirby/kirby/tree/main/' . $source;
|
||||
}
|
||||
|
||||
// header buttons
|
||||
$buttons = [];
|
||||
|
||||
if ($doc) {
|
||||
$buttons[] = [
|
||||
'props' => [
|
||||
'text' => $doc->name,
|
||||
'icon' => 'book',
|
||||
'drawer' => 'lab/docs/' . $doc->name
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
if ($github) {
|
||||
$buttons[] = [
|
||||
'props' => [
|
||||
'icon' => 'github',
|
||||
'link' => $github,
|
||||
'target' => '_blank'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'component' => 'k-lab-playground-view',
|
||||
'breadcrumb' => [
|
||||
|
@ -133,8 +200,9 @@ return [
|
|||
]
|
||||
],
|
||||
'props' => [
|
||||
'buttons' => $buttons,
|
||||
'compiler' => $compiler,
|
||||
'docs' => $docs?->name(),
|
||||
'docs' => $doc?->name,
|
||||
'examples' => $vue['examples'],
|
||||
'file' => $example->module(),
|
||||
'github' => $github,
|
||||
|
|
|
@ -7,6 +7,7 @@ return function ($kirby) {
|
|||
'icon' => 'translate',
|
||||
'label' => I18n::translate('view.languages'),
|
||||
'menu' => true,
|
||||
'buttons' => require __DIR__ . '/languages/buttons.php',
|
||||
'dialogs' => require __DIR__ . '/languages/dialogs.php',
|
||||
'views' => require __DIR__ . '/languages/views.php'
|
||||
];
|
||||
|
|
21
kirby/config/areas/languages/buttons.php
Normal file
21
kirby/config/areas/languages/buttons.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\Language;
|
||||
use Kirby\Panel\Ui\Buttons\LanguageCreateButton;
|
||||
use Kirby\Panel\Ui\Buttons\LanguageDeleteButton;
|
||||
use Kirby\Panel\Ui\Buttons\LanguageSettingsButton;
|
||||
use Kirby\Panel\Ui\Buttons\OpenButton;
|
||||
|
||||
return [
|
||||
'languages.create' => fn () =>
|
||||
new LanguageCreateButton(),
|
||||
'language.open' => fn (Language $language) =>
|
||||
new OpenButton(link: $language->url()),
|
||||
'language.settings' => fn (Language $language) =>
|
||||
new LanguageSettingsButton($language),
|
||||
'language.delete' => function (Language $language) {
|
||||
if ($language->isDeletable() === true) {
|
||||
return new LanguageDeleteButton($language);
|
||||
}
|
||||
}
|
||||
];
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Find;
|
||||
use Kirby\Cms\Language;
|
||||
use Kirby\Cms\LanguageVariable;
|
||||
use Kirby\Exception\NotFoundException;
|
||||
use Kirby\Toolkit\A;
|
||||
|
@ -49,16 +50,34 @@ $translationDialogFields = [
|
|||
'label' => I18n::translate('language.variable.key'),
|
||||
'type' => 'text'
|
||||
],
|
||||
'multiple' => [
|
||||
'label' => I18n::translate('language.variable.multiple'),
|
||||
'text' => I18n::translate('language.variable.multiple.text'),
|
||||
'help' => I18n::translate('language.variable.multiple.help'),
|
||||
'type' => 'toggle'
|
||||
],
|
||||
'value' => [
|
||||
'buttons' => false,
|
||||
'counter' => false,
|
||||
'label' => I18n::translate('language.variable.value'),
|
||||
'type' => 'textarea'
|
||||
'type' => 'textarea',
|
||||
'when' => [
|
||||
'multiple' => false
|
||||
]
|
||||
],
|
||||
'entries' => [
|
||||
'field' => ['type' => 'text'],
|
||||
'label' => I18n::translate('language.variable.entries'),
|
||||
'help' => I18n::translate('language.variable.entries.help'),
|
||||
'type' => 'entries',
|
||||
'min' => 1,
|
||||
'when' => [
|
||||
'multiple' => true
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
return [
|
||||
|
||||
// create language
|
||||
'language.create' => [
|
||||
'pattern' => 'languages/create',
|
||||
|
@ -184,6 +203,9 @@ return [
|
|||
'props' => [
|
||||
'fields' => $translationDialogFields,
|
||||
'size' => 'large',
|
||||
'value' => [
|
||||
'multiple' => false,
|
||||
]
|
||||
],
|
||||
];
|
||||
},
|
||||
|
@ -192,7 +214,12 @@ return [
|
|||
$language = Find::language($languageCode);
|
||||
|
||||
$key = $request->get('key', '');
|
||||
$value = $request->get('value', '');
|
||||
$multiple = $request->get('multiple', false);
|
||||
|
||||
$value = match ($multiple) {
|
||||
true => $request->get('entries', []),
|
||||
default => $request->get('value', '')
|
||||
};
|
||||
|
||||
LanguageVariable::create($key, $value);
|
||||
|
||||
|
@ -209,9 +236,9 @@ return [
|
|||
$variable = Find::language($languageCode)->variable($translationKey, true);
|
||||
|
||||
if ($variable->exists() === false) {
|
||||
throw new NotFoundException([
|
||||
'key' => 'language.variable.notFound'
|
||||
]);
|
||||
throw new NotFoundException(
|
||||
key: 'language.variable.notFound'
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
|
@ -230,48 +257,65 @@ return [
|
|||
'language.translation.update' => [
|
||||
'pattern' => 'languages/(:any)/translations/(:any)/update',
|
||||
'load' => function (string $languageCode, string $translationKey) use ($translationDialogFields) {
|
||||
$variable = Find::language($languageCode)->variable($translationKey, true);
|
||||
$language = Find::language($languageCode);
|
||||
$variable = $language->variable($translationKey, true);
|
||||
|
||||
if ($variable->exists() === false) {
|
||||
throw new NotFoundException([
|
||||
'key' => 'language.variable.notFound'
|
||||
]);
|
||||
throw new NotFoundException(
|
||||
key: 'language.variable.notFound'
|
||||
);
|
||||
}
|
||||
|
||||
$fields = $translationDialogFields;
|
||||
|
||||
// the key field cannot be changed
|
||||
// the multiple field is hidden
|
||||
$fields['key']['disabled'] = true;
|
||||
$fields['value']['autofocus'] = true;
|
||||
$fields['multiple']['type'] = 'hidden';
|
||||
|
||||
// shows info text when variable is an array
|
||||
// TODO: 5.0: use entries field instead showing info text
|
||||
$isVariableArray = is_array($variable->value()) === true;
|
||||
// check if the variable has multiple values;
|
||||
// ensure to use the default language for this check because
|
||||
// the variable might not exist in the current language but
|
||||
// already be defined in the default language with multiple values
|
||||
$isVariableArray = Language::ensure('default')->variable($translationKey, true)->hasMultipleValues();
|
||||
|
||||
// set the correct value field
|
||||
// when value is string, set value for value field
|
||||
// when value is array, set value for entries field
|
||||
if ($isVariableArray === true) {
|
||||
$fields['value'] = [
|
||||
'label' => I18n::translate('info'),
|
||||
'type' => 'info',
|
||||
'text' => 'You are using an array variable for this key. Please modify it in the language file in /site/languages',
|
||||
$fields['entries']['autofocus'] = true;
|
||||
$value = [
|
||||
'entries' => $variable->value(),
|
||||
'key' => $variable->key(),
|
||||
'multiple' => true
|
||||
];
|
||||
} else {
|
||||
$fields['value']['autofocus'] = true;
|
||||
$value = [
|
||||
'key' => $variable->key(),
|
||||
'multiple' => false,
|
||||
'value' => $variable->value()
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'component' => 'k-form-dialog',
|
||||
'props' => [
|
||||
'cancelButton' => $isVariableArray === false,
|
||||
'fields' => $fields,
|
||||
'size' => 'large',
|
||||
'submitButton' => $isVariableArray === false,
|
||||
'value' => [
|
||||
'key' => $variable->key(),
|
||||
'value' => $variable->value()
|
||||
'value' => $value
|
||||
]
|
||||
],
|
||||
];
|
||||
},
|
||||
'submit' => function (string $languageCode, string $translationKey) {
|
||||
Find::language($languageCode)->variable($translationKey, true)->update(
|
||||
App::instance()->request()->get('value', '')
|
||||
);
|
||||
$request = App::instance()->request();
|
||||
$multiple = $request->get('multiple', false);
|
||||
$value = match ($multiple) {
|
||||
true => $request->get('entries', []),
|
||||
default => $request->get('value', '')
|
||||
};
|
||||
|
||||
Find::language($languageCode)->variable($translationKey, true)->update($value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Find;
|
||||
use Kirby\Panel\Ui\Buttons\ViewButtons;
|
||||
use Kirby\Toolkit\Escape;
|
||||
use Kirby\Toolkit\I18n;
|
||||
|
||||
|
@ -19,9 +20,9 @@ return [
|
|||
$foundation = $kirby->defaultLanguage()->translations();
|
||||
$translations = $language->translations();
|
||||
|
||||
// TODO: update following line and adapt for update and delete options
|
||||
// when new `languageVariables.*` permissions available
|
||||
$canUpdate = $kirby->user()?->role()->permissions()->for('languages', 'update') === true;
|
||||
// TODO: update following line and adapt for update and
|
||||
// delete options when `languageVariables.*` permissions available
|
||||
$canUpdate = $kirby->role()?->permissions()->for('languages', 'update') === true;
|
||||
|
||||
ksort($foundation);
|
||||
|
||||
|
@ -73,6 +74,10 @@ return [
|
|||
]
|
||||
],
|
||||
'props' => [
|
||||
'buttons' => fn () =>
|
||||
ViewButtons::view('language', model: $language)
|
||||
->defaults('open', 'settings', 'delete')
|
||||
->render(),
|
||||
'deletable' => $language->isDeletable(),
|
||||
'code' => Escape::html($language->code()),
|
||||
'default' => $language->isDefault(),
|
||||
|
@ -113,6 +118,10 @@ return [
|
|||
return [
|
||||
'component' => 'k-languages-view',
|
||||
'props' => [
|
||||
'buttons' => fn () =>
|
||||
ViewButtons::view('languages')
|
||||
->defaults('create')
|
||||
->render(),
|
||||
'languages' => $kirby->languages()->values(fn ($language) => [
|
||||
'deletable' => $language->isDeletable(),
|
||||
'default' => $language->isDefault(),
|
||||
|
|
|
@ -12,6 +12,7 @@ return function ($kirby) {
|
|||
'icon' => $blueprint->icon() ?? 'home',
|
||||
'label' => $blueprint->title() ?? I18n::translate('view.site'),
|
||||
'menu' => true,
|
||||
'buttons' => require __DIR__ . '/site/buttons.php',
|
||||
'dialogs' => require __DIR__ . '/site/dialogs.php',
|
||||
'drawers' => require __DIR__ . '/site/drawers.php',
|
||||
'dropdowns' => require __DIR__ . '/site/dropdowns.php',
|
||||
|
|
72
kirby/config/areas/site/buttons.php
Normal file
72
kirby/config/areas/site/buttons.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\ModelWithContent;
|
||||
use Kirby\Cms\Page;
|
||||
use Kirby\Cms\Site;
|
||||
use Kirby\Panel\Ui\Buttons\LanguagesDropdown;
|
||||
use Kirby\Panel\Ui\Buttons\OpenButton;
|
||||
use Kirby\Panel\Ui\Buttons\PageStatusButton;
|
||||
use Kirby\Panel\Ui\Buttons\PreviewButton;
|
||||
use Kirby\Panel\Ui\Buttons\SettingsButton;
|
||||
use Kirby\Panel\Ui\Buttons\VersionsButton;
|
||||
|
||||
return [
|
||||
'site.open' => function (Site $site, string $versionId = 'latest') {
|
||||
$versionId = $versionId === 'compare' ? 'changes' : $versionId;
|
||||
$link = $site->previewUrl($versionId);
|
||||
|
||||
if ($link !== null) {
|
||||
return new OpenButton(
|
||||
link: $link,
|
||||
);
|
||||
}
|
||||
},
|
||||
'site.preview' => function (Site $site) {
|
||||
if ($site->previewUrl() !== null) {
|
||||
return new PreviewButton(
|
||||
link: $site->panel()->url(true) . '/preview/changes',
|
||||
);
|
||||
}
|
||||
},
|
||||
'site.versions' => function (Site $site, string $versionId = 'latest') {
|
||||
return new VersionsButton(
|
||||
model: $site,
|
||||
versionId: $versionId
|
||||
);
|
||||
},
|
||||
'page.open' => function (Page $page, string $versionId = 'latest') {
|
||||
$versionId = $versionId === 'compare' ? 'changes' : $versionId;
|
||||
$link = $page->previewUrl($versionId);
|
||||
|
||||
if ($link !== null) {
|
||||
return new OpenButton(
|
||||
link: $link,
|
||||
);
|
||||
}
|
||||
},
|
||||
'page.preview' => function (Page $page) {
|
||||
if ($page->previewUrl() !== null) {
|
||||
return new PreviewButton(
|
||||
link: $page->panel()->url(true) . '/preview/changes',
|
||||
);
|
||||
}
|
||||
},
|
||||
'page.versions' => function (Page $page, string $versionId = 'latest') {
|
||||
return new VersionsButton(
|
||||
model: $page,
|
||||
versionId: $versionId
|
||||
);
|
||||
},
|
||||
'page.settings' => fn (Page $page) => new SettingsButton(model: $page),
|
||||
'page.status' => fn (Page $page) => new PageStatusButton($page),
|
||||
|
||||
// `languages` button needs to be in site area,
|
||||
// as the languages might be not loaded even in
|
||||
// multilang mode when the `languages` option is deactivated
|
||||
// (but content languages to switch between still can exist)
|
||||
'languages' => fn (ModelWithContent $model) =>
|
||||
new LanguagesDropdown($model),
|
||||
|
||||
// file buttons
|
||||
...require __DIR__ . '/../files/buttons.php'
|
||||
];
|
|
@ -28,12 +28,10 @@ return [
|
|||
$page = Find::page($id);
|
||||
|
||||
if ($page->blueprint()->num() !== 'default') {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.sort.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
throw new PermissionException(
|
||||
key: 'page.sort.permission',
|
||||
data: ['slug' => $page->slug()]
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
|
@ -150,12 +148,10 @@ return [
|
|||
$blueprints = $page->blueprints();
|
||||
|
||||
if (count($blueprints) <= 1) {
|
||||
throw new Exception([
|
||||
'key' => 'page.changeTemplate.invalid',
|
||||
'data' => [
|
||||
'slug' => $id
|
||||
]
|
||||
]);
|
||||
throw new Exception(
|
||||
key: 'page.changeTemplate.invalid',
|
||||
data: ['slug' => $id]
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
|
@ -264,20 +260,17 @@ return [
|
|||
|
||||
// the page title changed
|
||||
if ($page->title()->value() !== $title) {
|
||||
$page->changeTitle($title);
|
||||
$page = $page->changeTitle($title);
|
||||
$response['event'][] = 'page.changeTitle';
|
||||
}
|
||||
|
||||
// the slug changed
|
||||
if ($page->slug() !== $slug) {
|
||||
$newPage = $page->changeSlug($slug);
|
||||
$response['event'][] = 'page.changeSlug';
|
||||
$response['dispatch'] = [
|
||||
'content/move' => [
|
||||
$oldUrl = $page->panel()->url(true),
|
||||
$newUrl = $newPage->panel()->url(true)
|
||||
]
|
||||
];
|
||||
|
||||
$newPage = $page->changeSlug($slug);
|
||||
$oldUrl = $page->panel()->url(true);
|
||||
$newUrl = $newPage->panel()->url(true);
|
||||
|
||||
// check for a necessary redirect after the slug has changed
|
||||
if (Panel::referrer() === $oldUrl && $oldUrl !== $newUrl) {
|
||||
|
@ -372,7 +365,9 @@ return [
|
|||
$page->childrenAndDrafts()->count() > 0 &&
|
||||
$request->get('check') !== $page->title()->value()
|
||||
) {
|
||||
throw new InvalidArgumentException(['key' => 'page.delete.confirm']);
|
||||
throw new InvalidArgumentException(
|
||||
key: 'page.delete.confirm'
|
||||
);
|
||||
}
|
||||
|
||||
$page->delete(true);
|
||||
|
@ -385,7 +380,6 @@ return [
|
|||
|
||||
return [
|
||||
'event' => 'page.delete',
|
||||
'dispatch' => ['content/remove' => [$url]],
|
||||
'redirect' => $redirect
|
||||
];
|
||||
}
|
||||
|
@ -418,7 +412,6 @@ return [
|
|||
$fields['files'] = [
|
||||
'label' => I18n::translate('page.duplicate.files'),
|
||||
'type' => 'toggle',
|
||||
'required' => true,
|
||||
'width' => $toggleWidth
|
||||
];
|
||||
}
|
||||
|
@ -427,7 +420,6 @@ return [
|
|||
$fields['children'] = [
|
||||
'label' => I18n::translate('page.duplicate.pages'),
|
||||
'type' => 'toggle',
|
||||
'required' => true,
|
||||
'width' => $toggleWidth
|
||||
];
|
||||
}
|
||||
|
@ -440,11 +432,11 @@ return [
|
|||
$duplicateSlug = $page->slug() . '-' . $slugAppendix;
|
||||
$siblingKeys = $page->parentModel()->childrenAndDrafts()->pluck('uid');
|
||||
|
||||
if (in_array($duplicateSlug, $siblingKeys) === true) {
|
||||
if (in_array($duplicateSlug, $siblingKeys, true) === true) {
|
||||
$suffixCounter = 2;
|
||||
$newSlug = $duplicateSlug . $suffixCounter;
|
||||
|
||||
while (in_array($newSlug, $siblingKeys) === true) {
|
||||
while (in_array($newSlug, $siblingKeys, true) === true) {
|
||||
$newSlug = $duplicateSlug . ++$suffixCounter;
|
||||
}
|
||||
|
||||
|
@ -556,13 +548,7 @@ return [
|
|||
|
||||
return [
|
||||
'event' => 'page.move',
|
||||
'redirect' => $newPage->panel()->url(true),
|
||||
'dispatch' => [
|
||||
'content/move' => [
|
||||
$oldPage->panel()->url(true),
|
||||
$newPage->panel()->url(true)
|
||||
]
|
||||
],
|
||||
'redirect' => $newPage->panel()->url(true)
|
||||
];
|
||||
}
|
||||
],
|
||||
|
@ -643,13 +629,7 @@ return [
|
|||
'changes' => [
|
||||
'pattern' => 'changes',
|
||||
'load' => function () {
|
||||
$dialog = new ChangesDialog();
|
||||
return $dialog->load();
|
||||
return (new ChangesDialog())->load();
|
||||
},
|
||||
'submit' => function () {
|
||||
$dialog = new ChangesDialog();
|
||||
$ids = App::instance()->request()->get('ids');
|
||||
return $dialog->submit($ids);
|
||||
}
|
||||
],
|
||||
];
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Find;
|
||||
use Kirby\Panel\Ui\Buttons\LanguagesDropdown;
|
||||
|
||||
$files = require __DIR__ . '/../files/dropdowns.php';
|
||||
|
||||
|
@ -10,12 +13,34 @@ return [
|
|||
return Find::page($path)->panel()->dropdown();
|
||||
}
|
||||
],
|
||||
'page.languages' => [
|
||||
'pattern' => 'pages/(:any)/languages',
|
||||
'options' => function (string $path) {
|
||||
$page = Find::page($path);
|
||||
return (new LanguagesDropdown($page))->options();
|
||||
}
|
||||
],
|
||||
'page.file' => [
|
||||
'pattern' => '(pages/.*?)/files/(:any)',
|
||||
'options' => $files['file']
|
||||
],
|
||||
'page.file.languages' => [
|
||||
'pattern' => '(pages/.*?)/files/(:any)/languages',
|
||||
'options' => $files['language']
|
||||
],
|
||||
'site.languages' => [
|
||||
'pattern' => 'site/languages',
|
||||
'options' => function () {
|
||||
$site = App::instance()->site();
|
||||
return (new LanguagesDropdown($site))->options();
|
||||
}
|
||||
],
|
||||
'site.file' => [
|
||||
'pattern' => '(site)/files/(:any)',
|
||||
'options' => $files['file']
|
||||
],
|
||||
'site.file.languages' => [
|
||||
'pattern' => '(site)/files/(:any)/languages',
|
||||
'options' => $files['language']
|
||||
]
|
||||
];
|
||||
|
|
|
@ -1,90 +1,25 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Find;
|
||||
use Kirby\Toolkit\I18n;
|
||||
use Kirby\Panel\Controller\PageTree;
|
||||
|
||||
return [
|
||||
// @codeCoverageIgnoreStart
|
||||
// TODO: move to controller class and add unit tests
|
||||
'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;
|
||||
return (new PageTree())->children(
|
||||
parent: App::instance()->request()->get('parent'),
|
||||
moving: App::instance()->request()->get('move')
|
||||
);
|
||||
}
|
||||
],
|
||||
'tree.parents' => [
|
||||
'pattern' => 'site/tree/parents',
|
||||
'action' => function () {
|
||||
$kirby = App::instance();
|
||||
$request = $kirby->request();
|
||||
$root = $request->get('root');
|
||||
$page = $kirby->page($request->get('page'));
|
||||
$parents = $page?->parents()->flip()->values(
|
||||
fn ($parent) => $parent->uuid()?->toString() ?? $parent->id()
|
||||
) ?? [];
|
||||
|
||||
// if root is included, add the site as top-level parent
|
||||
if ($root === 'true') {
|
||||
array_unshift($parents, $kirby->site()->uuid()?->toString() ?? '/');
|
||||
}
|
||||
|
||||
return [
|
||||
'data' => $parents
|
||||
];
|
||||
return (new PageTree())->parents(
|
||||
page: App::instance()->request()->get('page'),
|
||||
includeSite: App::instance()->request()->get('root') === 'true',
|
||||
);
|
||||
}
|
||||
]
|
||||
// @codeCoverageIgnoreEnd
|
||||
];
|
||||
|
|
|
@ -1,56 +1,17 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Toolkit\Escape;
|
||||
use Kirby\Panel\Controller\Search;
|
||||
use Kirby\Toolkit\I18n;
|
||||
|
||||
return [
|
||||
'pages' => [
|
||||
'label' => I18n::translate('pages'),
|
||||
'icon' => 'page',
|
||||
'query' => function (string|null $query, int $limit, int $page) {
|
||||
$kirby = App::instance();
|
||||
$pages = $kirby->site()
|
||||
->index(true)
|
||||
->search($query)
|
||||
->filter('isListable', true)
|
||||
->paginate($limit, $page);
|
||||
|
||||
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()),
|
||||
'uuid' => $page->uuid()?->toString(),
|
||||
]),
|
||||
'pagination' => $pages->pagination()->toArray()
|
||||
];
|
||||
}
|
||||
'query' => fn (string|null $query, int $limit, int $page) => Search::pages($query, $limit, $page)
|
||||
],
|
||||
'files' => [
|
||||
'label' => I18n::translate('files'),
|
||||
'icon' => 'image',
|
||||
'query' => function (string|null $query, int $limit, int $page) {
|
||||
$kirby = App::instance();
|
||||
$files = $kirby->site()
|
||||
->index(true)
|
||||
->filter('isListable', true)
|
||||
->files()
|
||||
->filter('isListable', true)
|
||||
->search($query)
|
||||
->paginate($limit, $page);
|
||||
|
||||
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()),
|
||||
'uuid' => $file->uuid()->toString(),
|
||||
]),
|
||||
'pagination' => $files->pagination()->toArray()
|
||||
];
|
||||
}
|
||||
'query' => fn (string|null $query, int $limit, int $page) => Search::files($query, $limit, $page)
|
||||
]
|
||||
];
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Find;
|
||||
use Kirby\Exception\PermissionException;
|
||||
use Kirby\Panel\Ui\Buttons\ViewButtons;
|
||||
use Kirby\Toolkit\I18n;
|
||||
|
||||
return [
|
||||
'page' => [
|
||||
|
@ -14,6 +17,40 @@ return [
|
|||
return Find::file('pages/' . $id, $filename)->panel()->view();
|
||||
}
|
||||
],
|
||||
'page.preview' => [
|
||||
'pattern' => 'pages/(:any)/preview/(changes|latest|compare)',
|
||||
'action' => function (string $path, string $versionId) {
|
||||
$page = Find::page($path);
|
||||
$view = $page->panel()->view();
|
||||
$src = [
|
||||
'latest' => $page->previewUrl('latest'),
|
||||
'changes' => $page->previewUrl('changes'),
|
||||
];
|
||||
|
||||
if ($src['latest'] === null) {
|
||||
throw new PermissionException('The preview is not available');
|
||||
}
|
||||
|
||||
return [
|
||||
'component' => 'k-preview-view',
|
||||
'props' => [
|
||||
...$view['props'],
|
||||
'back' => $view['props']['link'],
|
||||
'buttons' => fn () =>
|
||||
ViewButtons::view('page.preview', model: $page)
|
||||
->defaults(
|
||||
'page.versions',
|
||||
'languages',
|
||||
)
|
||||
->bind(['versionId' => $versionId])
|
||||
->render(),
|
||||
'src' => $src,
|
||||
'versionId' => $versionId,
|
||||
],
|
||||
'title' => $view['props']['title'] . ' | ' . I18n::translate('preview'),
|
||||
];
|
||||
}
|
||||
],
|
||||
'site' => [
|
||||
'pattern' => 'site',
|
||||
'action' => fn () => App::instance()->site()->panel()->view()
|
||||
|
@ -24,4 +61,38 @@ return [
|
|||
return Find::file('site', $filename)->panel()->view();
|
||||
}
|
||||
],
|
||||
'site.preview' => [
|
||||
'pattern' => 'site/preview/(changes|latest|compare)',
|
||||
'action' => function (string $versionId) {
|
||||
$site = App::instance()->site();
|
||||
$view = $site->panel()->view();
|
||||
$src = [
|
||||
'latest' => $site->previewUrl('latest'),
|
||||
'changes' => $site->previewUrl('changes'),
|
||||
];
|
||||
|
||||
if ($src['latest'] === null) {
|
||||
throw new PermissionException('The preview is not available');
|
||||
}
|
||||
|
||||
return [
|
||||
'component' => 'k-preview-view',
|
||||
'props' => [
|
||||
...$view['props'],
|
||||
'back' => $view['props']['link'],
|
||||
'buttons' => fn () =>
|
||||
ViewButtons::view('site.preview', model: $site)
|
||||
->defaults(
|
||||
'site.versions',
|
||||
'languages'
|
||||
)
|
||||
->bind(['versionId' => $versionId])
|
||||
->render(),
|
||||
'src' => $src,
|
||||
'versionId' => $versionId
|
||||
],
|
||||
'title' => I18n::translate('view.site') . ' | ' . I18n::translate('preview'),
|
||||
];
|
||||
}
|
||||
],
|
||||
];
|
||||
|
|
|
@ -53,7 +53,7 @@ return [
|
|||
];
|
||||
}
|
||||
|
||||
throw new LogicException('The upgrade failed');
|
||||
throw new LogicException(message: 'The upgrade failed');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Panel\Ui\Buttons\ViewButtons;
|
||||
use Kirby\Toolkit\I18n;
|
||||
|
||||
return [
|
||||
|
@ -59,11 +60,12 @@ return [
|
|||
|
||||
return [
|
||||
'author' => empty($authors) ? '–' : $authors,
|
||||
'license' => $plugin->license() ?? '–',
|
||||
'license' => $plugin->license()->toArray(),
|
||||
'name' => [
|
||||
'text' => $plugin->name() ?? '–',
|
||||
'href' => $plugin->link(),
|
||||
],
|
||||
'status' => $plugin->license()->status()->toArray(),
|
||||
'version' => $version,
|
||||
];
|
||||
});
|
||||
|
@ -122,12 +124,14 @@ return [
|
|||
return [
|
||||
'component' => 'k-system-view',
|
||||
'props' => [
|
||||
'buttons' => fn () =>
|
||||
ViewButtons::view('system')->render(),
|
||||
'environment' => $environment,
|
||||
'exceptions' => $debugMode ? $exceptions : [],
|
||||
'info' => $system->info(),
|
||||
'plugins' => $plugins,
|
||||
'security' => $security,
|
||||
'urls' => $sensitive ?? null
|
||||
'urls' => $sensitive ?? []
|
||||
]
|
||||
];
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ return function ($kirby) {
|
|||
'label' => I18n::translate('view.users'),
|
||||
'search' => 'users',
|
||||
'menu' => true,
|
||||
'buttons' => require __DIR__ . '/users/buttons.php',
|
||||
'dialogs' => require __DIR__ . '/users/dialogs.php',
|
||||
'drawers' => require __DIR__ . '/users/drawers.php',
|
||||
'dropdowns' => require __DIR__ . '/users/dropdowns.php',
|
||||
|
|
20
kirby/config/areas/users/buttons.php
Normal file
20
kirby/config/areas/users/buttons.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\User;
|
||||
use Kirby\Panel\Ui\Buttons\SettingsButton;
|
||||
use Kirby\Panel\Ui\Buttons\ViewButton;
|
||||
use Kirby\Toolkit\I18n;
|
||||
|
||||
return [
|
||||
'users.create' => function (User $user, string|null $role = null) {
|
||||
return new ViewButton(
|
||||
dialog: 'users/create?role=' . $role,
|
||||
disabled: $user->kirby()->roles()->canBeCreated()->count() < 1,
|
||||
icon: 'add',
|
||||
text: I18n::translate('user.create'),
|
||||
);
|
||||
},
|
||||
'user.settings' => function (User $user) {
|
||||
return new SettingsButton(model: $user);
|
||||
}
|
||||
];
|
|
@ -57,7 +57,7 @@ return [
|
|||
'email' => '',
|
||||
'password' => '',
|
||||
'translation' => $kirby->panelLanguage(),
|
||||
'role' => $role ?? $roles['options'][0]['value'] ?? null
|
||||
'role' => $role ?: $roles['options'][0]['value'] ?? null
|
||||
]
|
||||
]
|
||||
];
|
||||
|
@ -231,9 +231,9 @@ return [
|
|||
|
||||
// compare passwords
|
||||
if ($password !== $passwordConfirmation) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'user.password.notSame'
|
||||
]);
|
||||
throw new InvalidArgumentException(
|
||||
key: 'user.password.notSame'
|
||||
);
|
||||
}
|
||||
|
||||
// change password if everything's fine
|
||||
|
@ -319,7 +319,6 @@ return [
|
|||
|
||||
return [
|
||||
'event' => 'user.delete',
|
||||
'dispatch' => ['content/remove' => [$url]],
|
||||
'redirect' => $redirect
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1,18 +1,29 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\Find;
|
||||
use Kirby\Panel\Ui\Buttons\LanguagesDropdown;
|
||||
|
||||
$files = require __DIR__ . '/../files/dropdowns.php';
|
||||
|
||||
return [
|
||||
'user' => [
|
||||
'pattern' => 'users/(:any)',
|
||||
'options' => fn (string $id) =>
|
||||
Find::user($id)->panel()->dropdown()
|
||||
],
|
||||
'user.languages' => [
|
||||
'pattern' => 'users/(:any)/languages',
|
||||
'options' => function (string $id) {
|
||||
return Find::user($id)->panel()->dropdown();
|
||||
$user = Find::user($id);
|
||||
return (new LanguagesDropdown($user))->options();
|
||||
}
|
||||
],
|
||||
'user.file' => [
|
||||
'pattern' => '(users/.*?)/files/(:any)',
|
||||
'options' => $files['file']
|
||||
],
|
||||
'user.file.languages' => [
|
||||
'pattern' => '(users/.*?)/files/(:any)/languages',
|
||||
'options' => $files['language']
|
||||
]
|
||||
];
|
||||
|
|
|
@ -1,29 +1,12 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Toolkit\Escape;
|
||||
use Kirby\Panel\Controller\Search;
|
||||
use Kirby\Toolkit\I18n;
|
||||
|
||||
return [
|
||||
'users' => [
|
||||
'label' => I18n::translate('users'),
|
||||
'icon' => 'users',
|
||||
'query' => function (string|null $query, int $limit, int $page) {
|
||||
$kirby = App::instance();
|
||||
$users = $kirby->users()
|
||||
->search($query)
|
||||
->paginate($limit, $page);
|
||||
|
||||
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()),
|
||||
'uuid' => $user->uuid()->toString(),
|
||||
]),
|
||||
'pagination' => $users->pagination()->toArray()
|
||||
];
|
||||
}
|
||||
'query' => fn (string|null $query, int $limit, int $page) => Search::users($query, $limit, $page)
|
||||
]
|
||||
];
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Find;
|
||||
use Kirby\Panel\Ui\Buttons\ViewButtons;
|
||||
use Kirby\Toolkit\Escape;
|
||||
|
||||
return [
|
||||
|
@ -18,7 +19,11 @@ return [
|
|||
return [
|
||||
'component' => 'k-users-view',
|
||||
'props' => [
|
||||
'canCreate' => $kirby->roles()->canBeCreated()->count() > 0,
|
||||
'buttons' => fn () =>
|
||||
ViewButtons::view('users')
|
||||
->defaults('create')
|
||||
->bind(['role' => $role])
|
||||
->render(),
|
||||
'role' => function () use ($roles, $role) {
|
||||
if ($role) {
|
||||
return $roles[$role] ?? null;
|
||||
|
|
|
@ -4,11 +4,15 @@ use Kirby\Cms\App;
|
|||
use Kirby\Cms\Collection;
|
||||
use Kirby\Cms\File;
|
||||
use Kirby\Cms\FileVersion;
|
||||
use Kirby\Cms\ModelWithContent;
|
||||
use Kirby\Cms\Page;
|
||||
use Kirby\Cms\User;
|
||||
use Kirby\Content\PlainTextStorage;
|
||||
use Kirby\Content\Storage;
|
||||
use Kirby\Data\Data;
|
||||
use Kirby\Email\PHPMailer as Emailer;
|
||||
use Kirby\Exception\NotFoundException;
|
||||
use Kirby\Filesystem\Asset;
|
||||
use Kirby\Filesystem\F;
|
||||
use Kirby\Filesystem\Filename;
|
||||
use Kirby\Http\Uri;
|
||||
|
@ -59,22 +63,20 @@ return [
|
|||
/**
|
||||
* Adapt file characteristics
|
||||
*
|
||||
* @param \Kirby\Cms\File|\Kirby\Filesystem\Asset $file The file object
|
||||
* @param array $options All thumb options (width, height, crop, blur, grayscale)
|
||||
* @return \Kirby\Cms\File|\Kirby\Cms\FileVersion|\Kirby\Filesystem\Asset
|
||||
*/
|
||||
'file::version' => function (
|
||||
App $kirby,
|
||||
$file,
|
||||
File|Asset $file,
|
||||
array $options = []
|
||||
) {
|
||||
): File|Asset|FileVersion {
|
||||
// if file is not resizable, return
|
||||
if ($file->isResizable() === false) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
// create url and root
|
||||
$mediaRoot = dirname($file->mediaRoot());
|
||||
$mediaRoot = $file->mediaDir();
|
||||
$template = $mediaRoot . '/{{ name }}{{ attributes }}.{{ extension }}';
|
||||
$thumbRoot = (new Filename($file->root(), $template, $options))->toString();
|
||||
$thumbName = basename($thumbRoot);
|
||||
|
@ -85,9 +87,10 @@ return [
|
|||
$job = $mediaRoot . '/.jobs/' . $thumbName . '.json';
|
||||
|
||||
try {
|
||||
Data::write($job, array_merge($options, [
|
||||
'filename' => $file->filename()
|
||||
]));
|
||||
Data::write(
|
||||
$job,
|
||||
[...$options, 'filename' => $file->filename()]
|
||||
);
|
||||
} catch (Throwable) {
|
||||
// if thumb doesn't exist yet and job file cannot
|
||||
// be created, return
|
||||
|
@ -99,7 +102,7 @@ return [
|
|||
'modifications' => $options,
|
||||
'original' => $file,
|
||||
'root' => $thumbRoot,
|
||||
'url' => dirname($file->mediaUrl()) . '/' . $thumbName,
|
||||
'url' => $file->mediaUrl($thumbName),
|
||||
]);
|
||||
},
|
||||
|
||||
|
@ -150,17 +153,16 @@ return [
|
|||
$params = ['fields' => Str::split($params, '|')];
|
||||
}
|
||||
|
||||
$defaults = [
|
||||
$collection = clone $collection;
|
||||
$query = trim($query ?? '');
|
||||
$options = [
|
||||
'fields' => [],
|
||||
'minlength' => 2,
|
||||
'score' => [],
|
||||
'words' => false,
|
||||
...$params
|
||||
];
|
||||
|
||||
$collection = clone $collection;
|
||||
$options = array_merge($defaults, $params);
|
||||
$query = trim($query ?? '');
|
||||
|
||||
// empty or too short search query
|
||||
if (Str::length($query) < $options['minlength']) {
|
||||
return $collection->limit(0);
|
||||
|
@ -204,10 +206,11 @@ return [
|
|||
$keys[] = 'role';
|
||||
} elseif ($item instanceof Page) {
|
||||
// apply the default score for pages
|
||||
$options['score'] = array_merge(
|
||||
['id' => 64, 'title' => 64],
|
||||
$options['score']
|
||||
);
|
||||
$options['score'] = [
|
||||
'id' => 64,
|
||||
'title' => 64,
|
||||
...$options['score']
|
||||
];
|
||||
}
|
||||
|
||||
if (empty($options['fields']) === false) {
|
||||
|
@ -309,6 +312,16 @@ return [
|
|||
return Snippet::factory($name, $data, $slots);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new storage object for the given model
|
||||
*/
|
||||
'storage' => function (
|
||||
App $kirby,
|
||||
ModelWithContent $model
|
||||
): Storage {
|
||||
return new PlainTextStorage(model: $model);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add your own template engine
|
||||
*
|
||||
|
@ -332,7 +345,6 @@ return [
|
|||
* @param string $src Root of the original file
|
||||
* @param string $dst Template string for the root to the desired destination
|
||||
* @param array $options All thumb options that should be applied: `width`, `height`, `crop`, `blur`, `grayscale`
|
||||
* @return string
|
||||
*/
|
||||
'thumb' => function (
|
||||
App $kirby,
|
||||
|
@ -401,7 +413,7 @@ return [
|
|||
// keep relative urls
|
||||
if (
|
||||
$path !== null &&
|
||||
(substr($path, 0, 2) === './' || substr($path, 0, 3) === '../')
|
||||
(str_starts_with($path, './') || str_starts_with($path, '../'))
|
||||
) {
|
||||
return $path;
|
||||
}
|
||||
|
@ -417,7 +429,9 @@ return [
|
|||
$model = Uuid::for($path)->model();
|
||||
|
||||
if ($model === null) {
|
||||
throw new NotFoundException('The model could not be found for "' . $path . '" uuid');
|
||||
throw new NotFoundException(
|
||||
message: 'The model could not be found for "' . $path . '" uuid'
|
||||
);
|
||||
}
|
||||
|
||||
$path = $model->url();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\Helpers;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Field\FieldOptions;
|
||||
use Kirby\Toolkit\A;
|
||||
|
@ -24,8 +25,10 @@ return [
|
|||
* The CSS format (hex, rgb, hsl) to display and store the value
|
||||
*/
|
||||
'format' => function (string $format = 'hex'): string {
|
||||
if (in_array($format, ['hex', 'hsl', 'rgb']) === false) {
|
||||
throw new InvalidArgumentException('Unsupported format for color field (supported: hex, rgb, hsl)');
|
||||
if (in_array($format, ['hex', 'hsl', 'rgb'], true) === false) {
|
||||
throw new InvalidArgumentException(
|
||||
message: 'Unsupported format for color field (supported: hex, rgb, hsl)'
|
||||
);
|
||||
}
|
||||
|
||||
return $format;
|
||||
|
@ -35,8 +38,10 @@ return [
|
|||
* show the `options` as toggles
|
||||
*/
|
||||
'mode' => function (string $mode = 'picker'): string {
|
||||
if (in_array($mode, ['picker', 'input', 'options']) === false) {
|
||||
throw new InvalidArgumentException('Unsupported mode for color field (supported: picker, input, options)');
|
||||
if (in_array($mode, ['picker', 'input', 'options'], true) === false) {
|
||||
throw new InvalidArgumentException(
|
||||
message: 'Unsupported mode for color field (supported: picker, input, options)'
|
||||
);
|
||||
}
|
||||
|
||||
return $mode;
|
||||
|
@ -69,30 +74,33 @@ return [
|
|||
return [];
|
||||
}
|
||||
|
||||
$options = match (true) {
|
||||
// simple array of values
|
||||
// or value=text (from Options class)
|
||||
if (
|
||||
is_numeric($options[0]['value']) ||
|
||||
$options[0]['value'] === $options[0]['text']
|
||||
=> A::map($options, fn ($option) => [
|
||||
) {
|
||||
// simple array of values
|
||||
// or value=text (from Options class)
|
||||
$options = 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) => [
|
||||
} elseif ($this->isColor($options[0]['text'])) {
|
||||
// @deprecated 4.0.0
|
||||
// TODO: Remove in Kirby 6
|
||||
|
||||
Helpers::deprecated('Color field "' . $this->name . '": the text => value notation for options has been deprecated and will be removed in Kirby 6. Please rewrite your options as value => text.');
|
||||
|
||||
$options = 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) => [
|
||||
]);
|
||||
} else {
|
||||
$options = A::map($options, fn ($option) => [
|
||||
'value' => $option['value'],
|
||||
'text' => $option['text']
|
||||
]),
|
||||
};
|
||||
]);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
@ -121,24 +129,24 @@ return [
|
|||
}
|
||||
|
||||
if ($this->format === 'hex' && $this->isHex($value) === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'validation.color',
|
||||
'data' => ['format' => 'hex']
|
||||
]);
|
||||
throw new InvalidArgumentException(
|
||||
key: 'validation.color',
|
||||
data: ['format' => 'hex']
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->format === 'rgb' && $this->isRgb($value) === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'validation.color',
|
||||
'data' => ['format' => 'rgb']
|
||||
]);
|
||||
throw new InvalidArgumentException(
|
||||
key: 'validation.color',
|
||||
data: ['format' => 'rgb']
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->format === 'hsl' && $this->isHsl($value) === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'validation.color',
|
||||
'data' => ['format' => 'hsl']
|
||||
]);
|
||||
throw new InvalidArgumentException(
|
||||
key: 'validation.color',
|
||||
data: ['format' => 'hsl']
|
||||
);
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -125,27 +125,27 @@ return [
|
|||
$format = $this->time === false ? 'd.m.Y' : 'd.m.Y H:i';
|
||||
|
||||
if ($min && $max && $value->isBetween($min, $max) === false) {
|
||||
throw new Exception([
|
||||
'key' => 'validation.date.between',
|
||||
'data' => [
|
||||
throw new Exception(
|
||||
key: 'validation.date.between',
|
||||
data: [
|
||||
'min' => $min->format($format),
|
||||
'max' => $max->format($format)
|
||||
]
|
||||
]);
|
||||
} elseif ($min && $value->isMin($min) === false) {
|
||||
throw new Exception([
|
||||
'key' => 'validation.date.after',
|
||||
'data' => [
|
||||
'date' => $min->format($format),
|
||||
]
|
||||
]);
|
||||
} elseif ($max && $value->isMax($max) === false) {
|
||||
throw new Exception([
|
||||
'key' => 'validation.date.before',
|
||||
'data' => [
|
||||
'date' => $max->format($format),
|
||||
]
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
if ($min && $value->isMin($min) === false) {
|
||||
throw new Exception(
|
||||
key: 'validation.date.after',
|
||||
data: ['date' => $min->format($format)]
|
||||
);
|
||||
}
|
||||
|
||||
if ($max && $value->isMax($max) === false) {
|
||||
throw new Exception(
|
||||
key: 'validation.date.before',
|
||||
data: ['date' => $max->format($format)]
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -48,7 +48,7 @@ return [
|
|||
'activeTypes' => function () {
|
||||
return array_filter(
|
||||
$this->availableTypes(),
|
||||
fn (string $type) => in_array($type, $this->props['options']),
|
||||
fn (string $type) => in_array($type, $this->props['options'], true),
|
||||
ARRAY_FILTER_USE_KEY
|
||||
);
|
||||
},
|
||||
|
@ -153,17 +153,17 @@ return [
|
|||
$detected = true;
|
||||
|
||||
if ($options['validate']($link) === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'validation.' . $type
|
||||
]);
|
||||
throw new InvalidArgumentException(
|
||||
key: 'validation.' . $type
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// none of the configured types has been detected
|
||||
if ($detected === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'validation.linkType'
|
||||
]);
|
||||
throw new InvalidArgumentException(
|
||||
key: 'validation.linkType'
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -7,8 +7,11 @@ return [
|
|||
* Available layouts: `list`, `cardlets`, `cards`
|
||||
*/
|
||||
'layout' => function (string $layout = 'list') {
|
||||
$layouts = ['list', 'cardlets', 'cards'];
|
||||
return in_array($layout, $layouts) ? $layout : 'list';
|
||||
return match ($layout) {
|
||||
'cards' => 'cards',
|
||||
'cardlets' => 'cardlets',
|
||||
default => 'list'
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -36,7 +36,7 @@ return [
|
|||
},
|
||||
'sanitizeOption' => function ($value) {
|
||||
$options = array_column($this->options(), 'value');
|
||||
return in_array($value, $options) === true ? $value : null;
|
||||
return in_array($value, $options) ? $value : null;
|
||||
},
|
||||
'sanitizeOptions' => function ($values) {
|
||||
$options = array_column($this->options(), 'value');
|
||||
|
|
|
@ -34,7 +34,9 @@ return [
|
|||
$parent = $this->uploadParent($uploads['parent'] ?? null);
|
||||
|
||||
if ($parent === null) {
|
||||
throw new InvalidArgumentException('"' . $uploads['parent'] . '" could not be resolved as a valid parent for the upload');
|
||||
throw new InvalidArgumentException(
|
||||
message: '"' . $uploads['parent'] . '" could not be resolved as a valid parent for the upload'
|
||||
);
|
||||
}
|
||||
|
||||
$file = new File([
|
||||
|
@ -52,7 +54,9 @@ return [
|
|||
'methods' => [
|
||||
'upload' => function (Api $api, $params, Closure $map) {
|
||||
if ($params === false) {
|
||||
throw new Exception('Uploads are disabled for this field');
|
||||
throw new Exception(
|
||||
message: 'Uploads are disabled for this field'
|
||||
);
|
||||
}
|
||||
|
||||
$parent = $this->uploadParent($params['parent'] ?? null);
|
||||
|
@ -68,7 +72,9 @@ return [
|
|||
$file = $parent->createFile($props, true);
|
||||
|
||||
if ($file instanceof File === false) {
|
||||
throw new Exception('The file could not be uploaded');
|
||||
throw new Exception(
|
||||
message: 'The file could not be uploaded'
|
||||
);
|
||||
}
|
||||
|
||||
return $map($file, $parent);
|
||||
|
|
|
@ -38,7 +38,7 @@ return [
|
|||
],
|
||||
'methods' => [
|
||||
'toNumber' => function ($value): float|null {
|
||||
if ($this->isEmpty($value) === true) {
|
||||
if ($this->isEmptyValue($value) === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -91,13 +91,13 @@ return [
|
|||
$name = array_key_first($errors);
|
||||
$error = $errors[$name];
|
||||
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'object.validation',
|
||||
'data' => [
|
||||
throw new InvalidArgumentException(
|
||||
key: 'object.validation',
|
||||
data: [
|
||||
'label' => $error['label'] ?? $name,
|
||||
'message' => implode("\n", $error['message'])
|
||||
]
|
||||
]);
|
||||
);
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -20,7 +20,8 @@ return [
|
|||
],
|
||||
'computed' => [
|
||||
'default' => function () {
|
||||
return $this->sanitizeOption($this->default);
|
||||
$default = $this->model()->toString($this->default);
|
||||
return $this->sanitizeOption($default);
|
||||
},
|
||||
'value' => function () {
|
||||
return $this->sanitizeOption($this->value) ?? '';
|
||||
|
|
|
@ -18,7 +18,7 @@ return [
|
|||
return $icon;
|
||||
},
|
||||
/**
|
||||
* Custom placeholder string for empty option.
|
||||
* Text shown when no option is selected yet
|
||||
*/
|
||||
'placeholder' => function (string|array $placeholder = '—') {
|
||||
return I18n::translate($placeholder, $placeholder);
|
||||
|
|
|
@ -149,7 +149,7 @@ return [
|
|||
|
||||
// make the first column visible on mobile
|
||||
// if no other mobile columns are defined
|
||||
if (in_array(true, array_column($columns, 'mobile')) === false) {
|
||||
if (in_array(true, array_column($columns, 'mobile'), true) === false) {
|
||||
$columns[array_key_first($columns)]['mobile'] = true;
|
||||
}
|
||||
|
||||
|
@ -166,24 +166,37 @@ return [
|
|||
continue;
|
||||
}
|
||||
|
||||
$value[] = $this->form($row)->values();
|
||||
$value[] = $this->form()->fill(input: $row, passthrough: true)->toFormValues();
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
'form' => function (array $values = []) {
|
||||
return new Form([
|
||||
'fields' => $this->attrs['fields'] ?? [],
|
||||
'values' => $values,
|
||||
'model' => $this->model
|
||||
]);
|
||||
},
|
||||
'form' => function () {
|
||||
$this->form ??= new Form(
|
||||
fields: $this->attrs['fields'] ?? [],
|
||||
model: $this->model,
|
||||
language: 'current'
|
||||
);
|
||||
|
||||
return $this->form->reset();
|
||||
}
|
||||
],
|
||||
'save' => function ($value) {
|
||||
$data = [];
|
||||
$form = $this->form();
|
||||
$defaults = $form->defaults();
|
||||
|
||||
foreach ($value as $row) {
|
||||
$row = $this->form($row)->content();
|
||||
foreach ($value as $index => $row) {
|
||||
$row = $form
|
||||
->reset()
|
||||
->fill(
|
||||
input: $defaults,
|
||||
)
|
||||
->submit(
|
||||
input: $row,
|
||||
passthrough: true
|
||||
)
|
||||
->toStoredValues();
|
||||
|
||||
// remove frontend helper id
|
||||
unset($row['_id']);
|
||||
|
@ -204,19 +217,20 @@ return [
|
|||
$values = A::wrap($value);
|
||||
|
||||
foreach ($values as $index => $value) {
|
||||
$form = $this->form($value);
|
||||
$form = $this->form();
|
||||
$form->fill(input: $value);
|
||||
|
||||
foreach ($form->fields() as $field) {
|
||||
$errors = $field->errors();
|
||||
|
||||
if (empty($errors) === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'structure.validation',
|
||||
'data' => [
|
||||
throw new InvalidArgumentException(
|
||||
key: 'structure.validation',
|
||||
data: [
|
||||
'field' => $field->label() ?? Str::ucfirst($field->name()),
|
||||
'index' => $index + 1
|
||||
]
|
||||
]);
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,14 @@ return [
|
|||
* The field value will be converted with the selected converter before the value gets saved. Available converters: `lower`, `upper`, `ucfirst`, `slug`
|
||||
*/
|
||||
'converter' => function ($value = null) {
|
||||
if ($value !== null && array_key_exists($value, $this->converters()) === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'field.converter.invalid',
|
||||
'data' => ['converter' => $value]
|
||||
]);
|
||||
if (
|
||||
$value !== null &&
|
||||
array_key_exists($value, $this->converters()) === false
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
key: 'field.converter.invalid',
|
||||
data: ['converter' => $value]
|
||||
);
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
|
|
@ -89,12 +89,11 @@ return [
|
|||
[
|
||||
'pattern' => 'files',
|
||||
'action' => function () {
|
||||
$params = array_merge($this->field()->files(), [
|
||||
return $this->field()->filepicker([
|
||||
...$this->field()->files(),
|
||||
'page' => $this->requestQuery('page'),
|
||||
'search' => $this->requestQuery('search')
|
||||
]);
|
||||
|
||||
return $this->field()->filepicker($params);
|
||||
}
|
||||
],
|
||||
[
|
||||
|
@ -104,14 +103,12 @@ return [
|
|||
$field = $this->field();
|
||||
$uploads = $field->uploads();
|
||||
|
||||
return $this->field()->upload($this, $uploads, function ($file, $parent) use ($field) {
|
||||
$absolute = $field->model()->is($parent) === false;
|
||||
|
||||
return [
|
||||
return $this->field()->upload($this, $uploads, fn ($file, $parent) => [
|
||||
'filename' => $file->filename(),
|
||||
'dragText' => $file->panel()->dragText('auto', $absolute),
|
||||
];
|
||||
});
|
||||
'dragText' => $file->panel()->dragText(
|
||||
absolute: $field->model()->is($parent) === false
|
||||
),
|
||||
]);
|
||||
}
|
||||
]
|
||||
];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Exception\Exception;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Toolkit\Date;
|
||||
use Kirby\Toolkit\I18n;
|
||||
|
||||
|
@ -97,27 +97,27 @@ return [
|
|||
$format = 'H:i:s';
|
||||
|
||||
if ($min && $max && $value->isBetween($min, $max) === false) {
|
||||
throw new Exception([
|
||||
'key' => 'validation.time.between',
|
||||
'data' => [
|
||||
throw new InvalidArgumentException(
|
||||
key: 'validation.time.between',
|
||||
data: [
|
||||
'min' => $min->format($format),
|
||||
'max' => $min->format($format)
|
||||
]
|
||||
]);
|
||||
} elseif ($min && $value->isMin($min) === false) {
|
||||
throw new Exception([
|
||||
'key' => 'validation.time.after',
|
||||
'data' => [
|
||||
'time' => $min->format($format),
|
||||
]
|
||||
]);
|
||||
} elseif ($max && $value->isMax($max) === false) {
|
||||
throw new Exception([
|
||||
'key' => 'validation.time.before',
|
||||
'data' => [
|
||||
'time' => $max->format($format),
|
||||
]
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
if ($min && $value->isMin($min) === false) {
|
||||
throw new InvalidArgumentException(
|
||||
key: 'validation.time.after',
|
||||
data: ['time' => $min->format($format)]
|
||||
);
|
||||
}
|
||||
|
||||
if ($max && $value->isMax($max) === false) {
|
||||
throw new InvalidArgumentException(
|
||||
key: 'validation.time.before',
|
||||
data: ['time' => $max->format($format)]
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -65,8 +65,13 @@ return [
|
|||
'validations' => [
|
||||
'boolean',
|
||||
'required' => function ($value) {
|
||||
if ($this->isRequired() && ($value === false || $this->isEmpty($value))) {
|
||||
throw new InvalidArgumentException(I18n::translate('field.required'));
|
||||
if (
|
||||
$this->isRequired() &&
|
||||
($value === false || $this->isEmptyValue($value))
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
message: I18n::translate('field.required')
|
||||
);
|
||||
}
|
||||
},
|
||||
]
|
||||
|
|
|
@ -79,10 +79,10 @@ return [
|
|||
$this->minlength &&
|
||||
V::minLength(strip_tags($value), $this->minlength) === false
|
||||
) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'validation.minlength',
|
||||
'data' => ['min' => $this->minlength]
|
||||
]);
|
||||
throw new InvalidArgumentException(
|
||||
key: 'validation.minlength',
|
||||
data: ['min' => $this->minlength]
|
||||
);
|
||||
}
|
||||
},
|
||||
'maxlength' => function ($value) {
|
||||
|
@ -90,10 +90,10 @@ return [
|
|||
$this->maxlength &&
|
||||
V::maxLength(strip_tags($value), $this->maxlength) === false
|
||||
) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'validation.maxlength',
|
||||
'data' => ['max' => $this->maxlength]
|
||||
]);
|
||||
throw new InvalidArgumentException(
|
||||
key: 'validation.maxlength',
|
||||
data: ['max' => $this->maxlength]
|
||||
);
|
||||
}
|
||||
},
|
||||
]
|
||||
|
|
|
@ -55,7 +55,7 @@ if (Helpers::hasOverride('collection') === false) { // @codeCoverageIgnore
|
|||
* Returns the result of a collection by name
|
||||
*
|
||||
* @return \Kirby\Toolkit\Collection|null
|
||||
* @todo 5.0 Add return type declaration
|
||||
* @todo 6.0 Add return type declaration
|
||||
*/
|
||||
function collection(string $name, array $options = [])
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Blocks;
|
||||
use Kirby\Cms\Collection;
|
||||
use Kirby\Cms\File;
|
||||
use Kirby\Cms\Files;
|
||||
use Kirby\Cms\Html;
|
||||
|
@ -80,7 +81,9 @@ return function (App $app) {
|
|||
$message .= ' on parent "' . $parent->title() . '"';
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException($message);
|
||||
throw new InvalidArgumentException(
|
||||
message: $message
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -130,6 +133,18 @@ return function (App $app) {
|
|||
return Str::date($time, $format);
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse yaml entries data and convert it to a
|
||||
* collection of field objects
|
||||
*/
|
||||
'toEntries' => function (Field $field): Collection {
|
||||
$entries = new Collection(parent: $field->parent());
|
||||
foreach ($field->yaml() as $index => $entry) {
|
||||
$entries->append(new Field($field->parent(), $index, $entry));
|
||||
}
|
||||
return $entries;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a file object from a filename in the field
|
||||
*/
|
||||
|
@ -266,7 +281,9 @@ return function (App $app) {
|
|||
$message .= ' on parent "' . $parent->id() . '"';
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException($message);
|
||||
throw new InvalidArgumentException(
|
||||
message: $message
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -40,19 +40,36 @@ return function (array $props) {
|
|||
|
||||
|
||||
if ($drafts !== false) {
|
||||
$sections['drafts'] = $section(I18n::translate('pages.status.draft'), 'drafts', $drafts);
|
||||
$sections['drafts'] = $section(
|
||||
I18n::translate('pages.status.draft'),
|
||||
'drafts',
|
||||
$drafts
|
||||
);
|
||||
}
|
||||
|
||||
if ($unlisted !== false) {
|
||||
$sections['unlisted'] = $section(I18n::translate('pages.status.unlisted'), 'unlisted', $unlisted);
|
||||
$sections['unlisted'] = $section(
|
||||
I18n::translate('pages.status.unlisted'),
|
||||
'unlisted',
|
||||
$unlisted
|
||||
);
|
||||
}
|
||||
|
||||
if ($listed !== false) {
|
||||
$sections['listed'] = $section(I18n::translate('pages.status.listed'), 'listed', $listed);
|
||||
$sections['listed'] = $section(
|
||||
I18n::translate('pages.status.listed'),
|
||||
'listed',
|
||||
$listed
|
||||
);
|
||||
}
|
||||
|
||||
// cleaning up
|
||||
unset($props['drafts'], $props['unlisted'], $props['listed'], $props['templates']);
|
||||
unset(
|
||||
$props['drafts'],
|
||||
$props['unlisted'],
|
||||
$props['listed'],
|
||||
$props['templates']
|
||||
);
|
||||
|
||||
return array_merge($props, ['sections' => $sections]);
|
||||
return [...$props, 'sections' => $sections];
|
||||
};
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\LanguageRoutes;
|
||||
use Kirby\Cms\Media;
|
||||
use Kirby\Cms\PluginAssets;
|
||||
use Kirby\Panel\Panel;
|
||||
use Kirby\Panel\Plugins;
|
||||
use Kirby\Plugin\Assets;
|
||||
use Kirby\Toolkit\Str;
|
||||
use Kirby\Uuid\Uuid;
|
||||
|
||||
|
@ -71,7 +71,7 @@ return function (App $kirby) {
|
|||
string $hash,
|
||||
string $path
|
||||
) {
|
||||
return PluginAssets::resolve(
|
||||
return Assets::resolve(
|
||||
$provider . '/' . $pluginName,
|
||||
$hash,
|
||||
$path
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Cms\Page;
|
||||
use Kirby\Cms\Site;
|
||||
use Kirby\Form\Form;
|
||||
|
||||
return [
|
||||
|
@ -12,45 +10,19 @@ return [
|
|||
],
|
||||
'computed' => [
|
||||
'form' => function () {
|
||||
$fields = $this->fields;
|
||||
$disabled = $this->model->permissions()->update() === false;
|
||||
$lang = $this->model->kirby()->languageCode();
|
||||
$content = $this->model->content($lang)->toArray();
|
||||
|
||||
if ($disabled === true) {
|
||||
foreach ($fields as $key => $props) {
|
||||
$fields[$key]['disabled'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return new Form([
|
||||
'fields' => $fields,
|
||||
'values' => $content,
|
||||
'model' => $this->model,
|
||||
'strict' => true
|
||||
]);
|
||||
return new Form(
|
||||
fields: $this->fields,
|
||||
model: $this->model,
|
||||
language: 'current'
|
||||
);
|
||||
},
|
||||
'fields' => function () {
|
||||
$fields = $this->form->fields()->toArray();
|
||||
|
||||
if (
|
||||
$this->model instanceof Page ||
|
||||
$this->model instanceof Site
|
||||
) {
|
||||
// the title should never be updated directly via
|
||||
// fields section to avoid conflicts with the rename dialog
|
||||
unset($fields['title']);
|
||||
}
|
||||
|
||||
foreach ($fields as $index => $props) {
|
||||
unset($fields[$index]['value']);
|
||||
}
|
||||
|
||||
return $fields;
|
||||
return $this->form->fields()->toProps();
|
||||
}
|
||||
],
|
||||
'methods' => [
|
||||
'errors' => function () {
|
||||
$this->form->fill($this->model->content('current')->toArray());
|
||||
return $this->form->errors();
|
||||
}
|
||||
],
|
||||
|
|
|
@ -6,6 +6,7 @@ use Kirby\Toolkit\I18n;
|
|||
|
||||
return [
|
||||
'mixins' => [
|
||||
'batch',
|
||||
'details',
|
||||
'empty',
|
||||
'headline',
|
||||
|
@ -90,14 +91,15 @@ return [
|
|||
$files = $files->flip();
|
||||
}
|
||||
|
||||
return $files;
|
||||
},
|
||||
'modelsPaginated' => function () {
|
||||
// apply the default pagination
|
||||
$files = $files->paginate([
|
||||
return $this->models()->paginate([
|
||||
'page' => $this->page,
|
||||
'limit' => $this->limit,
|
||||
'method' => 'none' // the page is manually provided
|
||||
]);
|
||||
|
||||
return $files;
|
||||
},
|
||||
'files' => function () {
|
||||
return $this->models;
|
||||
|
@ -105,15 +107,16 @@ return [
|
|||
'data' => function () {
|
||||
$data = [];
|
||||
|
||||
// the drag text needs to be absolute when the files come from
|
||||
// a different parent model
|
||||
$dragTextAbsolute = $this->model->is($this->parent) === false;
|
||||
|
||||
foreach ($this->models as $file) {
|
||||
foreach ($this->modelsPaginated() as $file) {
|
||||
$panel = $file->panel();
|
||||
$permissions = $file->permissions();
|
||||
|
||||
$item = [
|
||||
'dragText' => $panel->dragText('auto', $dragTextAbsolute),
|
||||
'dragText' => $panel->dragText(
|
||||
// the drag text needs to be absolute
|
||||
// when the files come from a different parent model
|
||||
absolute: $this->model->is($this->parent) === false
|
||||
),
|
||||
'extension' => $file->extension(),
|
||||
'filename' => $file->filename(),
|
||||
'id' => $file->id(),
|
||||
|
@ -125,6 +128,10 @@ return [
|
|||
'link' => $panel->url(true),
|
||||
'mime' => $file->mime(),
|
||||
'parent' => $file->parent()->panel()->path(),
|
||||
'permissions' => [
|
||||
'delete' => $permissions->can('delete'),
|
||||
'sort' => $permissions->can('sort'),
|
||||
],
|
||||
'template' => $file->template(),
|
||||
'text' => $file->toSafeString($this->text),
|
||||
'url' => $file->url(),
|
||||
|
@ -140,7 +147,7 @@ return [
|
|||
return $data;
|
||||
},
|
||||
'total' => function () {
|
||||
return $this->models->pagination()->total();
|
||||
return $this->models()->count();
|
||||
},
|
||||
'errors' => function () {
|
||||
$errors = [];
|
||||
|
@ -180,13 +187,7 @@ return [
|
|||
|
||||
// count all uploaded files
|
||||
$max = $this->max ? $this->max - $this->total : null;
|
||||
|
||||
if ($this->max && $this->total === $this->max - 1) {
|
||||
$multiple = false;
|
||||
} else {
|
||||
$multiple = true;
|
||||
}
|
||||
|
||||
$multiple = !$max || $max > 1;
|
||||
$template = $this->template === 'default' ? null : $this->template;
|
||||
|
||||
return [
|
||||
|
@ -220,6 +221,15 @@ return [
|
|||
|
||||
return true;
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'delete',
|
||||
'method' => 'DELETE',
|
||||
'action' => function () {
|
||||
return $this->section()->deleteSelected(
|
||||
ids: $this->requestBody('ids'),
|
||||
);
|
||||
}
|
||||
]
|
||||
];
|
||||
},
|
||||
|
@ -231,6 +241,7 @@ return [
|
|||
'options' => [
|
||||
'accept' => $this->accept,
|
||||
'apiUrl' => $this->parent->apiUrl(true) . '/sections/' . $this->name,
|
||||
'batch' => $this->batch,
|
||||
'columns' => $this->columnsWithTypes(),
|
||||
'empty' => $this->empty,
|
||||
'headline' => $this->headline,
|
||||
|
|
45
kirby/config/sections/mixins/batch.php
Normal file
45
kirby/config/sections/mixins/batch.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Exception\Exception;
|
||||
use Kirby\Exception\PermissionException;
|
||||
use Kirby\Toolkit\I18n;
|
||||
|
||||
return [
|
||||
'props' => [
|
||||
/**
|
||||
* Activates the batch delete option for the section
|
||||
*/
|
||||
'batch' => function (bool $batch = false) {
|
||||
return $batch;
|
||||
},
|
||||
],
|
||||
'methods' => [
|
||||
'deleteSelected' => function (array $ids): bool {
|
||||
if ($ids === []) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if batch deletion is allowed
|
||||
if ($this->batch() === false) {
|
||||
throw new PermissionException(
|
||||
message: 'The section does not support batch actions'
|
||||
);
|
||||
}
|
||||
|
||||
$min = $this->min();
|
||||
|
||||
// check if the section has enough items after the deletion
|
||||
if ($this->total() - count($ids) < $min) {
|
||||
throw new Exception(
|
||||
message: I18n::template('error.section.' . $this->type() . '.min.' . I18n::form($min), [
|
||||
'min' => $min,
|
||||
'section' => $this->headline()
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
$this->models()->delete($ids);
|
||||
return true;
|
||||
}
|
||||
]
|
||||
];
|
|
@ -19,7 +19,7 @@ return [
|
|||
*/
|
||||
'layout' => function (string $layout = 'list') {
|
||||
$layouts = ['list', 'cardlets', 'cards', 'table'];
|
||||
return in_array($layout, $layouts) ? $layout : 'list';
|
||||
return in_array($layout, $layouts, true) ? $layout : 'list';
|
||||
},
|
||||
/**
|
||||
* Whether the raw content file values should be used for the table column previews. Should not be used unless it eases performance issues in your setup introduced with Kirby 4.2
|
||||
|
|
|
@ -24,7 +24,9 @@ return [
|
|||
$parent = $this->model->query($query);
|
||||
|
||||
if (!$parent) {
|
||||
throw new Exception('The parent for the query "' . $query . '" cannot be found in the section "' . $this->name() . '"');
|
||||
throw new Exception(
|
||||
message: 'The parent for the query "' . $query . '" cannot be found in the section "' . $this->name() . '"'
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -33,7 +35,9 @@ return [
|
|||
$parent instanceof File === false &&
|
||||
$parent instanceof User === false
|
||||
) {
|
||||
throw new Exception('The parent for the section "' . $this->name() . '" has to be a page, site or user object');
|
||||
throw new Exception(
|
||||
message: 'The parent for the section "' . $this->name() . '" has to be a page, site or user object'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ return [
|
|||
|
||||
if (
|
||||
$this->type === 'pages' &&
|
||||
in_array($this->status, ['listed', 'published', 'all']) === false
|
||||
in_array($this->status, ['listed', 'published', 'all'], true) === false
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use Kirby\Toolkit\I18n;
|
|||
|
||||
return [
|
||||
'mixins' => [
|
||||
'batch',
|
||||
'details',
|
||||
'empty',
|
||||
'headline',
|
||||
|
@ -44,7 +45,7 @@ return [
|
|||
$status = 'draft';
|
||||
}
|
||||
|
||||
if (in_array($status, ['all', 'draft', 'published', 'listed', 'unlisted']) === false) {
|
||||
if (in_array($status, ['all', 'draft', 'published', 'listed', 'unlisted'], true) === false) {
|
||||
$status = 'all';
|
||||
}
|
||||
|
||||
|
@ -77,7 +78,9 @@ return [
|
|||
$parent instanceof Site === false &&
|
||||
$parent instanceof Page === false
|
||||
) {
|
||||
throw new InvalidArgumentException('The parent is invalid. You must choose the site or a page as parent.');
|
||||
throw new InvalidArgumentException(
|
||||
message: 'The parent is invalid. You must choose the site or a page as parent.'
|
||||
);
|
||||
}
|
||||
|
||||
return $parent;
|
||||
|
@ -111,7 +114,7 @@ return [
|
|||
// filter by all set templates
|
||||
if (
|
||||
$this->templates &&
|
||||
in_array($intendedTemplate, $this->templates) === false
|
||||
in_array($intendedTemplate, $this->templates, true) === false
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
@ -119,7 +122,7 @@ return [
|
|||
// exclude by all ignored templates
|
||||
if (
|
||||
$this->templatesIgnore &&
|
||||
in_array($intendedTemplate, $this->templatesIgnore) === true
|
||||
in_array($intendedTemplate, $this->templatesIgnore, true) === true
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
@ -147,25 +150,26 @@ return [
|
|||
$pages = $pages->flip();
|
||||
}
|
||||
|
||||
return $pages;
|
||||
},
|
||||
'modelsPaginated' => function () {
|
||||
// pagination
|
||||
$pages = $pages->paginate([
|
||||
return $this->models()->paginate([
|
||||
'page' => $this->page,
|
||||
'limit' => $this->limit,
|
||||
'method' => 'none' // the page is manually provided
|
||||
]);
|
||||
|
||||
return $pages;
|
||||
},
|
||||
'pages' => function () {
|
||||
return $this->models;
|
||||
},
|
||||
'total' => function () {
|
||||
return $this->models->pagination()->total();
|
||||
return $this->models()->count();
|
||||
},
|
||||
'data' => function () {
|
||||
$data = [];
|
||||
|
||||
foreach ($this->models as $page) {
|
||||
foreach ($this->modelsPaginated() as $page) {
|
||||
$panel = $page->panel();
|
||||
$permissions = $page->permissions();
|
||||
|
||||
|
@ -180,10 +184,11 @@ return [
|
|||
'link' => $panel->url(true),
|
||||
'parent' => $page->parentId(),
|
||||
'permissions' => [
|
||||
'sort' => $permissions->can('sort'),
|
||||
'delete' => $permissions->can('delete'),
|
||||
'changeSlug' => $permissions->can('changeSlug'),
|
||||
'changeStatus' => $permissions->can('changeStatus'),
|
||||
'changeTitle' => $permissions->can('changeTitle'),
|
||||
'sort' => $permissions->can('sort'),
|
||||
],
|
||||
'status' => $page->status(),
|
||||
'template' => $page->intendedTemplate()->name(),
|
||||
|
@ -313,12 +318,28 @@ return [
|
|||
return $blueprints;
|
||||
},
|
||||
],
|
||||
// @codeCoverageIgnoreStart
|
||||
'api' => function () {
|
||||
return [
|
||||
[
|
||||
'pattern' => 'delete',
|
||||
'method' => 'DELETE',
|
||||
'action' => function () {
|
||||
return $this->section()->deleteSelected(
|
||||
ids: $this->requestBody('ids'),
|
||||
);
|
||||
}
|
||||
]
|
||||
];
|
||||
},
|
||||
// @codeCoverageIgnoreEnd
|
||||
'toArray' => function () {
|
||||
return [
|
||||
'data' => $this->data,
|
||||
'errors' => $this->errors,
|
||||
'options' => [
|
||||
'add' => $this->add,
|
||||
'batch' => $this->batch,
|
||||
'columns' => $this->columnsWithTypes(),
|
||||
'empty' => $this->empty,
|
||||
'headline' => $this->headline,
|
||||
|
|
|
@ -216,15 +216,19 @@ return [
|
|||
// if url is empty, throw exception or link to the error page
|
||||
if ($tag->value === null) {
|
||||
if ($tag->kirby()->option('debug', false) === true) {
|
||||
$error = 'The linked page cannot be found';
|
||||
|
||||
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');
|
||||
$error .= ' for the link text "' . $tag->text . '"';
|
||||
}
|
||||
} else {
|
||||
|
||||
throw new NotFoundException(
|
||||
message: $error
|
||||
);
|
||||
}
|
||||
|
||||
$tag->value = Url::to($tag->kirby()->site()->errorPageId());
|
||||
}
|
||||
}
|
||||
|
||||
return Html::a($tag->value, $tag->text, [
|
||||
'rel' => $tag->rel,
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
|
799
kirby/i18n/translations/bs.json
Normal file
799
kirby/i18n/translations/bs.json
Normal file
|
@ -0,0 +1,799 @@
|
|||
{
|
||||
"account.changeName": "Promijeni svoje ime",
|
||||
"account.delete": "Obriši svoj račun",
|
||||
"account.delete.confirm": "Da li stvarno želite obrisati svoj račun? Odmah ćete biti odjavljeni. Vaš račun neće biti moguće oporaviti.",
|
||||
|
||||
"activate": "Aktiviraj",
|
||||
"add": "Dodaj",
|
||||
"alpha": "Alpha",
|
||||
"author": "Autor",
|
||||
"avatar": "Profilna slika",
|
||||
"back": "Natrag",
|
||||
"cancel": "Odustani",
|
||||
"change": "Promijeni",
|
||||
"close": "Zatvori",
|
||||
"changes": "Promjene",
|
||||
"confirm": "Ok",
|
||||
"collapse": "Skupi",
|
||||
"collapse.all": "Skupi sve",
|
||||
"color": "Boja",
|
||||
"coordinates": "Koordinate",
|
||||
"copy": "Kopiraj",
|
||||
"copy.all": "Kopiraj sve",
|
||||
"copy.success": "Kopirano",
|
||||
"copy.success.multiple": "{count} kopirano!",
|
||||
"copy.url": "Kopiraj URL",
|
||||
"create": "Kreiraj",
|
||||
"custom": "Prilagođeno",
|
||||
|
||||
"date": "Datum",
|
||||
"date.select": "Odaberi datum",
|
||||
|
||||
"day": "Dan",
|
||||
"days.fri": "Pet",
|
||||
"days.mon": "Pon",
|
||||
"days.sat": "Sub",
|
||||
"days.sun": "Ned",
|
||||
"days.thu": "Čet",
|
||||
"days.tue": "Uto",
|
||||
"days.wed": "Sri",
|
||||
|
||||
"debugging": "Debugiranje",
|
||||
|
||||
"delete": "Obriši",
|
||||
"delete.all": "Obriši sve",
|
||||
|
||||
"dialog.fields.empty": "Ovaj dijalog nema polja",
|
||||
"dialog.files.empty": "Nema datoteka za odabir",
|
||||
"dialog.pages.empty": "Nema stranica za odabir",
|
||||
"dialog.text.empty": "Ovaj dijalog ne definira nikakav tekst",
|
||||
"dialog.users.empty": "Nema korisnika za odabir",
|
||||
|
||||
"dimensions": "Dimenzije",
|
||||
"disable": "Onemogući",
|
||||
"disabled": "Onemogućeno",
|
||||
"discard": "Odbaci",
|
||||
|
||||
"drawer.fields.empty": "Ova ladica nema polja",
|
||||
|
||||
"domain": "Domena",
|
||||
"download": "Download",
|
||||
"duplicate": "Dupliciraj",
|
||||
|
||||
"edit": "Uredi",
|
||||
|
||||
"email": "Email",
|
||||
"email.placeholder": "mail@example.com",
|
||||
|
||||
"enter": "Enter",
|
||||
"entries": "Unosi",
|
||||
"entry": "Unos",
|
||||
|
||||
"environment": "Okruženje",
|
||||
|
||||
"error": "Greška",
|
||||
"error.access.code": "Nevažeći kod",
|
||||
"error.access.login": "Nevažeći login",
|
||||
"error.access.panel": "Pristup panelu nije dozvoljen",
|
||||
"error.access.view": "Pristup ovom dijelu panela nije dozvoljen",
|
||||
|
||||
"error.avatar.create.fail": "Profilnu sliku nije moguće spremiti",
|
||||
"error.avatar.delete.fail": "Profilna slika se ne može obrisati",
|
||||
"error.avatar.dimensions.invalid": "Nevažeće dimenzije. Promijenite visinu i širinu profilne slike ispod 3000 piksela",
|
||||
"error.avatar.mime.forbidden": "Profilna slika mora biti u JPEG ili PNG formatu",
|
||||
|
||||
"error.blueprint.notFound": "Blueprint \"{name}\" nije pronađen",
|
||||
|
||||
"error.blocks.max.plural": "Ne smijete dodati više od {max} blokova",
|
||||
"error.blocks.max.singular": "Ne smijete dodati više od jednog bloka",
|
||||
"error.blocks.min.plural": "Morate dodati najmanje {min} blokova",
|
||||
"error.blocks.min.singular": "Morate dodati najmanje jedan blok",
|
||||
"error.blocks.validation": "Postoji greška kod \"{field}\" polja, u bloku {index}, pri korištenju bloka tipa \"{fieldset}\" ",
|
||||
|
||||
"error.cache.type.invalid": "Nevažeći tip za cache \"{type}\"",
|
||||
|
||||
"error.content.lock.delete": "Ova verzija je zaključana i ne može se obrisati",
|
||||
"error.content.lock.move": "Izvorna verzija je zaključana i ne može se premjestiti",
|
||||
"error.content.lock.publish": "Ova verzija je već objavljena",
|
||||
"error.content.lock.replace": "Ova verzija je zaključana i ne može se zamijeniti",
|
||||
"error.content.lock.update": "Ova verzija je zaključana i ne može se urediti",
|
||||
|
||||
"error.entries.max.plural": "Ne smijete dodati više od {max} unosa",
|
||||
"error.entries.max.singular": "Ne smijete dodati više od jednog unosa",
|
||||
"error.entries.min.plural": "Morate dodati najmanje {min} unosa",
|
||||
"error.entries.min.singular": "Morate dodati najmanje jedan unos",
|
||||
"error.entries.supports": "\"{type}\" tip polja nije podržan za polje unosa",
|
||||
"error.entries.validation": "Postoji greška na \"{field}\" polju u redu {index}",
|
||||
|
||||
"error.email.preset.notFound": "Email preset \"{name}\" nije pronađen",
|
||||
|
||||
"error.field.converter.invalid": "Nevažeći Converter \"{converter}\"",
|
||||
"error.field.link.options": "Nevažeće opcije: {options}",
|
||||
"error.field.type.missing": "Polje \"{name}\": Tip polja \"{type}\" ne postoji",
|
||||
|
||||
"error.file.changeName.empty": "Naziv ne smije biti prazan",
|
||||
"error.file.changeName.permission": "Nemate dozvolu da promijenite naziv datoteke \"{filename}\"",
|
||||
"error.file.changeTemplate.invalid": "Predložak za datoteku \"{id}\" se ne može promijeniti u \"{template}\" (dozvoljeno: \"{blueprints}\")",
|
||||
"error.file.changeTemplate.permission": "Nemate dozvolu da promijenite predložak za datoteku \"{id}\"",
|
||||
|
||||
"error.file.delete.multiple": "Nije bilo moguće obrisati sve datoteke. Pokušajte ih obrisati pojedinačno da bi vidjeli specifičnu grešku koja sprječava brisanje. ",
|
||||
"error.file.duplicate": "Datoteka sa imenom \"{filename}\" već postoji",
|
||||
"error.file.extension.forbidden": "Ekstenzija \"{extension}\" nije dozvoljena",
|
||||
"error.file.extension.invalid": "Nevažeća ekstenzija: {extension}",
|
||||
"error.file.extension.missing": "Ekstenzija za datoteku \"{filename}\" nedostaje",
|
||||
"error.file.maxheight": "Visina slike ne smije prelaziti {height} piksela",
|
||||
"error.file.maxsize": "Datoteka je prevelika",
|
||||
"error.file.maxwidth": "Širina slike ne smije prelaziti {width} piksela",
|
||||
"error.file.mime.differs": "Uploadana datoteka mora biti istog mime tipa \"{mime}\"",
|
||||
"error.file.mime.forbidden": "Tip medija \"{mime}\" nije dozvoljen",
|
||||
"error.file.mime.invalid": "Nevažeći mime tip: {mime}",
|
||||
"error.file.mime.missing": "Tip medija za \"{filename}\" se ne može očitati",
|
||||
"error.file.minheight": "Visina slike mora biti najmanje {height} piksela",
|
||||
"error.file.minsize": "Datoteka je premala",
|
||||
"error.file.minwidth": "Širina slike mora biti najmanje {height} piksela",
|
||||
"error.file.name.unique": "Naziv datoteke mora biti jedinstven",
|
||||
"error.file.name.missing": "Naziv datoteke ne smije biti prazan",
|
||||
"error.file.notFound": "Datoteka \"{filename}\" nije pronađena",
|
||||
"error.file.orientation": "Orijentacija slike mora biti \"{orientation}\"",
|
||||
"error.file.sort.permission": "Nemate dozvolu da promijenite sortiranje od \"{filename}\"",
|
||||
"error.file.type.forbidden": "Nije dozvoljen upload {type} datoteka",
|
||||
"error.file.type.invalid": "Nevažeći tip datoteke: {type}",
|
||||
"error.file.undefined": "Datoteka nije pronađena",
|
||||
|
||||
"error.form.incomplete": "Popravi sve greške na formi...",
|
||||
"error.form.notSaved": "Forma se ne može spremiti",
|
||||
|
||||
"error.language.code": "Unesi važeći kod za jezik",
|
||||
"error.language.create.permission": "Nemate dozvolu da kreirate jezik",
|
||||
"error.language.delete.permission": "Nemate dozvolu da obrišete jezik",
|
||||
"error.language.duplicate": "Jezik već postoji",
|
||||
"error.language.name": "Unesi važeći naziv za jezik",
|
||||
"error.language.notFound": "Jezik nije pronađen",
|
||||
"error.language.update.permission": "Nemate dozvolu da ažurirate jezik",
|
||||
|
||||
"error.layout.validation.block": "Postoji greška kod \"{field}\" polja, u bloku {blockIndex}, pri korištenju bloka tipa \"{fieldset}\" u rasporedu {layoutIndex}",
|
||||
"error.layout.validation.settings": "Postoji greška u postavkama rasporeda {index}",
|
||||
|
||||
"error.license.domain": "Domena za licencu nedostaje",
|
||||
"error.license.email": "Unesite važeću email adresu",
|
||||
"error.license.format": "Unesite važeću licencu",
|
||||
"error.license.verification": "Licenca se ne može verificirati",
|
||||
|
||||
"error.login.totp.confirm.invalid": "Nevažeći kod",
|
||||
"error.login.totp.confirm.missing": "Unesi trenutni kod",
|
||||
|
||||
"error.object.validation": "Postoji greška u \"{label}\" polju:\n{message}",
|
||||
|
||||
"error.offline": "Panel je trenutno offline",
|
||||
|
||||
"error.page.changeSlug.permission": "Nemate dozvolu da promijenite URL nastavak za \"{slug}\"",
|
||||
"error.page.changeSlug.reserved": "Putanja stranica najvišeg nivoa ne smije počinjati sa \"{path}\"",
|
||||
"error.page.changeStatus.incomplete": "Stranica sadrži greške i ne može se objaviti",
|
||||
"error.page.changeStatus.permission": "Status za ovu stranicu se ne može promijeniti",
|
||||
"error.page.changeStatus.toDraft.invalid": "Stranica \"{slug}\" se ne može pretvoriti u skicu",
|
||||
"error.page.changeTemplate.invalid": "Predložak za stranicu \"{slug}\" se ne može promijeniti",
|
||||
"error.page.changeTemplate.permission": "Nemate dozvolu da promijenite predložak za \"{slug}\"",
|
||||
"error.page.changeTitle.empty": "Naslov ne može biti prazan",
|
||||
"error.page.changeTitle.permission": "Nemate dozvolu da promijenite naslov za \"{slug}\"",
|
||||
"error.page.create.permission": "Nemate dozvolu da kreirate \"{slug}\"",
|
||||
"error.page.delete": "Stranica \"{slug}\" se ne može obrisati",
|
||||
"error.page.delete.confirm": "Unesite naslov stranice da potvrdite",
|
||||
"error.page.delete.hasChildren": "Stranica sadrži podstranice i ne može se obrisati",
|
||||
"error.page.delete.multiple": "Nije bilo moguće obrisati sve stranice. Pokušajte ih obrisati pojedinačno da bi vidjeli specifičnu grešku koja sprječava brisanje. ",
|
||||
"error.page.delete.permission": "Nemate dozvolu da obrišete \"{slug}\"",
|
||||
"error.page.draft.duplicate": "Skica stranice sa URL nastavkom \"{slug}\" već postoji",
|
||||
"error.page.duplicate": "Stranica sa URL nastavkom \"{slug}\" već postoji",
|
||||
"error.page.duplicate.permission": "Nemate dozvolu da duplicirate \"{slug}\"",
|
||||
"error.page.move.ancestor": "Stranica se ne može premjestiti u samu sebe",
|
||||
"error.page.move.directory": "Direktorij stranice se ne može premjestiti",
|
||||
"error.page.move.duplicate": "Podstranica sa URL nastavkom \"{slug}\" već postoji",
|
||||
"error.page.move.noSections": "Stranica \"{parent}\" ne može biti roditelj bilo koje podstranice jer ne sadrži sekciju \"pages\" u svom blueprintu",
|
||||
"error.page.move.notFound": "Premještena stranica se ne može pronaći",
|
||||
"error.page.move.permission": "Nemate dozvolu da premjestite \"{slug}\"",
|
||||
"error.page.move.template": "Predložak \"{template}\" nije prihvaćen kao podstranica od \"{parent}\"",
|
||||
"error.page.notFound": "Stranica \"{slug}\" nije pronađena",
|
||||
"error.page.num.invalid": "Unesi važeći broj za sortiranje. Brojevi ne smiju biti negativni.",
|
||||
"error.page.slug.invalid": "Unesi važeći URL prefiks",
|
||||
"error.page.slug.maxlength": "Dužina URL nastavka mora biti manja od \"{length}\" znakova",
|
||||
"error.page.sort.permission": "Stranica \"{slug}\" se ne može sortirati",
|
||||
"error.page.status.invalid": "Odaberi važeći status stranice",
|
||||
"error.page.undefined": "Stranica nije pronađena",
|
||||
"error.page.update.permission": "Nemate dozvolu da uredite \"{slug}\"",
|
||||
|
||||
"error.section.files.max.plural": "Nije moguće dodati više od {max} datoteka u \"{section}\" sekciju",
|
||||
"error.section.files.max.singular": "Nije moguće dodati više od jedne datoteke u \"{section}\" sekciju",
|
||||
"error.section.files.min.plural": "Sekcija \"{section}\" zahtjeva najmanje {min} datoteka",
|
||||
"error.section.files.min.singular": "Sekcija \"{section}\" zahtjeva najmanje jednu datoteku",
|
||||
|
||||
"error.section.pages.max.plural": "Nije moguće dodati više od {max} stranica u \"{section}\" sekciju",
|
||||
"error.section.pages.max.singular": "Nije moguće dodati više od jedne stranice \"{section}\" sekciju",
|
||||
"error.section.pages.min.plural": "Sekcija \"{section}\" zahtjeva najmanje {min} stranica",
|
||||
"error.section.pages.min.singular": "Sekcija \"{section}\" zahtjeva najmanje jednu stranicu",
|
||||
|
||||
"error.section.notLoaded": "Sekcija \"{name}\" se nije mogla učitati",
|
||||
"error.section.type.invalid": "Tip sekcije \"{type}\" nije važeći",
|
||||
|
||||
"error.site.changeTitle.empty": "The title must not be empty",
|
||||
"error.site.changeTitle.permission": "Nemate dozvolu da promijenite naslov stranice",
|
||||
"error.site.update.permission": "Nemate dozvolu da uredite stranicu",
|
||||
|
||||
"error.structure.validation": "Postoji greška na \"{field}\" polju u redu {index}",
|
||||
|
||||
"error.template.default.notFound": "Default predložak ne postoji",
|
||||
|
||||
"error.unexpected": "Dogodila se neočekivana greška! Omogućite debug modus za više informacija: https://getkirby.com/docs/reference/system/options/debug",
|
||||
|
||||
"error.user.changeEmail.permission": "Nemate dozvolu da promijenite email adresu korisnika \"{name}\"",
|
||||
"error.user.changeLanguage.permission": "Nemate dozvolu da promijenite jezik korisnika \"{name}\"",
|
||||
"error.user.changeName.permission": "Nemate dozvolu da promijenite ime korisnika \"{name}\"",
|
||||
"error.user.changePassword.permission": "Nemate dozvolu da promijenite šifru korisnika \"{name}\"",
|
||||
"error.user.changeRole.lastAdmin": "Uloga zadnjeg administratora se ne može promijeniti",
|
||||
"error.user.changeRole.permission": "Nemate dozvolu da promijenite ulogu korisnika \"{name}\"",
|
||||
"error.user.changeRole.toAdmin": "Nemate dozvolu da promijenite ulogu nekog korisnika u administratora",
|
||||
"error.user.create.permission": "Nemate dozvolu da kreirate ovog korisnika",
|
||||
"error.user.delete": "Korisnik \"{name}\" se ne može obrisati",
|
||||
"error.user.delete.lastAdmin": "Zadnji administrator se ne može obrisati",
|
||||
"error.user.delete.lastUser": "Zadnji korisnik se ne može obrisati",
|
||||
"error.user.delete.permission": "Nemate dozvolu da obrišete korisnika \"{name}\"",
|
||||
"error.user.duplicate": "Korisnik sa email adresom \"{email}\" već postoji",
|
||||
"error.user.email.invalid": "Unesite važeću email adresu",
|
||||
"error.user.language.invalid": "Unesi važeći jezik",
|
||||
"error.user.notFound": "Korisnik \"{name}\" nije pronađen",
|
||||
"error.user.password.excessive": "Unesite važeću šifru. Šifre ne smiju biti duže od 1000 znakova",
|
||||
"error.user.password.invalid": "Unesi važeću šifru. Šifre moraju sadržavati najmanje 8 znakova.",
|
||||
"error.user.password.notSame": "Šifre se ne podudaraju",
|
||||
"error.user.password.undefined": "Korisnik nema šifru",
|
||||
"error.user.password.wrong": "Kriva šifra",
|
||||
"error.user.role.invalid": "Uloga je nevažeća",
|
||||
"error.user.undefined": "Nije moguće pronaći korisnika",
|
||||
"error.user.update.permission": "Nemate dozvolu da uredite korisnika \"{name}\"",
|
||||
|
||||
"error.validation.accepted": "Potvrdi",
|
||||
"error.validation.alpha": "Koristi samo znakove u rasponu a-z",
|
||||
"error.validation.alphanum": "Koristi samo znakove u rasponu a-z ili brojeve 0-9",
|
||||
"error.validation.anchor": "Unesite ispravan anker linka",
|
||||
"error.validation.between": "Unesi vrijednost između \"{min}\" i \"{max}\"",
|
||||
"error.validation.boolean": "Potvrdi ili otkaži",
|
||||
"error.validation.color": "Unesite važeću boju u {format} formatu",
|
||||
"error.validation.contains": "Unesi vrijednost koja sadrži \"{needle}\"",
|
||||
"error.validation.date": "Unesi važeći datum",
|
||||
"error.validation.date.after": "Unesi datum nakon {date}",
|
||||
"error.validation.date.before": "Unesi datum prije {date}",
|
||||
"error.validation.date.between": "Unesi datum između {min} i {max}",
|
||||
"error.validation.denied": "Otkaži",
|
||||
"error.validation.different": "Vrijednost ne može biti \"{other}\"",
|
||||
"error.validation.email": "Unesite važeću email adresu",
|
||||
"error.validation.endswith": "Vrijednost mora završavati sa \"{end}\"",
|
||||
"error.validation.filename": "Unesi važeći naziv datoteke",
|
||||
"error.validation.in": "Unesi jedno od slijedećeg: ({in})",
|
||||
"error.validation.integer": "Unesi važeći cijeli broj",
|
||||
"error.validation.ip": "Unesi važeću IP adresu",
|
||||
"error.validation.less": "Unesi vrijednost manju od {max}",
|
||||
"error.validation.linkType": "Tip linka nije dozvoljen",
|
||||
"error.validation.match": "Vrijednost ne odgovara očekivanom uzorku",
|
||||
"error.validation.max": "Unesi vrijednost jednaku ili manju od {max}",
|
||||
"error.validation.maxlength": "Unesi kraću vrijednost. (max. {max} znakova)",
|
||||
"error.validation.maxwords": "Unesi najviše {max} riječ(i)",
|
||||
"error.validation.min": "Unesi vrijednost jednaku ili veću od {min}",
|
||||
"error.validation.minlength": "Unesi dužu vrijednost. (min. {min} znakova)",
|
||||
"error.validation.minwords": "Unesi najmanje {min} riječ(i)",
|
||||
"error.validation.more": "Unesi veću vrijednost od {min}",
|
||||
"error.validation.notcontains": "Unesi vrijednost koja NE sadrži \"{needle}\"",
|
||||
"error.validation.notin": "Nemojte unositi bilo šta od slijedećeg: ({notIn})",
|
||||
"error.validation.option": "Odaberite važeću opciju",
|
||||
"error.validation.num": "Odaberite važeći broj",
|
||||
"error.validation.required": "Unesi nešto",
|
||||
"error.validation.same": "Unesi \"{other}\"",
|
||||
"error.validation.size": "Dužina unosa mora biti \"{size}\"",
|
||||
"error.validation.startswith": "Unos mora počinjati sa \"{start}\"",
|
||||
"error.validation.tel": "Unesite neformatirani broj telefona",
|
||||
"error.validation.time": "Unesi važeće vrijeme",
|
||||
"error.validation.time.after": "Unesite vrijeme poslije {time}",
|
||||
"error.validation.time.before": "Unesite vrijeme prije {time}",
|
||||
"error.validation.time.between": "Unesite vrijeme između {min} i {max}",
|
||||
"error.validation.uuid": "Unesite važeći UUID",
|
||||
"error.validation.url": "Unesi važeći URL",
|
||||
|
||||
"expand": "Proširi",
|
||||
"expand.all": "Proširi sve",
|
||||
|
||||
"field.invalid": "Polje je nevažeće",
|
||||
"field.required": "Polje je obavezno",
|
||||
"field.blocks.changeType": "Promijena tipa",
|
||||
"field.blocks.code.name": "Kod",
|
||||
"field.blocks.code.language": "Jezik",
|
||||
"field.blocks.code.placeholder": "Vaš kod ...",
|
||||
"field.blocks.delete.confirm": "Da li stvarno želite obrisati ovaj blok?",
|
||||
"field.blocks.delete.confirm.all": "Da li stvarno želite obrisati sve blokove?",
|
||||
"field.blocks.delete.confirm.selected": "Da li stvarno želite obrisati odabrane blokove?",
|
||||
"field.blocks.empty": "Još nema blokova",
|
||||
"field.blocks.fieldsets.empty": "Još nema skupova polja",
|
||||
"field.blocks.fieldsets.label": "Odaberite tip bloka ...",
|
||||
"field.blocks.fieldsets.paste": "Pritisnite <kbd>{{ shortcut }}</kbd> da uvezete rasporede/blokove iz vašeg međuspremnika.<small>Samo oni dozvoljeni u trenutnom polju će biti umetnuti.</small>",
|
||||
"field.blocks.gallery.name": "Galerija",
|
||||
"field.blocks.gallery.images.empty": "Još nema slika",
|
||||
"field.blocks.gallery.images.label": "Slike",
|
||||
"field.blocks.heading.level": "Nivo",
|
||||
"field.blocks.heading.name": "Naslov",
|
||||
"field.blocks.heading.text": "Tekst",
|
||||
"field.blocks.heading.placeholder": "Naslov ...",
|
||||
"field.blocks.figure.back.plain": "Obično",
|
||||
"field.blocks.figure.back.pattern.light": "Uzorak (svijetlo)",
|
||||
"field.blocks.figure.back.pattern.dark": "Uzorak (tamno)",
|
||||
"field.blocks.image.alt": "Alternativni tekst",
|
||||
"field.blocks.image.caption": "Natpis",
|
||||
"field.blocks.image.crop": "Odreži",
|
||||
"field.blocks.image.link": "Link",
|
||||
"field.blocks.image.location": "Lokacija",
|
||||
"field.blocks.image.location.internal": "Ova web stranica",
|
||||
"field.blocks.image.location.external": "Eksterni izvor",
|
||||
"field.blocks.image.name": "Slika",
|
||||
"field.blocks.image.placeholder": "Odaberi sliku",
|
||||
"field.blocks.image.ratio": "Omjer",
|
||||
"field.blocks.image.url": "URL slike",
|
||||
"field.blocks.line.name": "Linija",
|
||||
"field.blocks.list.name": "Lista",
|
||||
"field.blocks.markdown.name": "Markdown",
|
||||
"field.blocks.markdown.label": "Tekst",
|
||||
"field.blocks.markdown.placeholder": "Markdown ...",
|
||||
"field.blocks.quote.name": "Citat",
|
||||
"field.blocks.quote.text.label": "Tekst",
|
||||
"field.blocks.quote.text.placeholder": "Citat ...",
|
||||
"field.blocks.quote.citation.label": "Citiranje",
|
||||
"field.blocks.quote.citation.placeholder": "po ...",
|
||||
"field.blocks.text.name": "Tekst",
|
||||
"field.blocks.text.placeholder": "Tekst ...",
|
||||
"field.blocks.video.autoplay": "Automatski reproduciraj",
|
||||
"field.blocks.video.caption": "Natpis",
|
||||
"field.blocks.video.controls": "Kontrole",
|
||||
"field.blocks.video.location": "Lokacija",
|
||||
"field.blocks.video.loop": "Ponavljaj",
|
||||
"field.blocks.video.muted": "Utišano",
|
||||
"field.blocks.video.name": "Video",
|
||||
"field.blocks.video.placeholder": "Unesi video URL",
|
||||
"field.blocks.video.poster": "Poster",
|
||||
"field.blocks.video.preload": "Predučitavanje",
|
||||
"field.blocks.video.url.label": "Video-URL",
|
||||
"field.blocks.video.url.placeholder": "https://youtube.com/?v=",
|
||||
|
||||
"field.entries.delete.confirm.all": "Da li stvarno želite obrisati sve unose?",
|
||||
"field.entries.empty": "Još nema unosa",
|
||||
|
||||
"field.files.empty": "Niti jedna datoteka još nije odabrana",
|
||||
"field.files.empty.single": "Datoteka još nije odabrana",
|
||||
|
||||
"field.layout.change": "Promijeni raspored",
|
||||
"field.layout.delete": "Obriši raspored",
|
||||
"field.layout.delete.confirm": "Da li stvarno želite obrisati ovaj raspored?",
|
||||
"field.layout.delete.confirm.all": "Da li stvarno želite obrisati sve rasporede?",
|
||||
"field.layout.empty": "Još nema redova",
|
||||
"field.layout.select": "Odaberi raspored",
|
||||
|
||||
"field.object.empty": "Još nema informacija",
|
||||
|
||||
"field.pages.empty": "Niti jedna stranica još nije odabrana",
|
||||
"field.pages.empty.single": "Stranica još nije odabrana",
|
||||
|
||||
"field.structure.delete.confirm": "Da li stvarno želite obrisati ovaj red?",
|
||||
"field.structure.delete.confirm.all": "Da li stvarno želite obrisati sve unose?",
|
||||
"field.structure.empty": "Još nema unosa",
|
||||
|
||||
"field.users.empty": "Niti jedan korisnik još nije odabran",
|
||||
"field.users.empty.single": "Korisnik još nije odabran",
|
||||
|
||||
"fields.empty": "Još nema polja",
|
||||
|
||||
"file": "Datoteka",
|
||||
"file.blueprint": "Ovaj fajl još uvijek nema blueprint. Možete definirati postavke u <strong>/site/blueprints/files/{blueprint}.yml</strong>",
|
||||
"file.changeTemplate": "Promijeni predložak",
|
||||
"file.changeTemplate.notice": "Promjena predloška datoteke će ukloniti sadržaj za polja koja se ne podudaraju po tipu. Ako novi predložak definira određena pravila, npr. dimenzije slike, ona će se također nepovratno primijeniti. Koristite s oprezom.",
|
||||
"file.delete.confirm": "Da li stvarno želite obrisati <br><strong>{filename}</strong>?",
|
||||
"file.focus.placeholder": "Postavi fokalnu tačku",
|
||||
"file.focus.reset": "Ukloni fokalnu tačku",
|
||||
"file.focus.title": "Fokus",
|
||||
"file.sort": "Promijeni poziciju",
|
||||
|
||||
"files": "Datoteke",
|
||||
"files.delete.confirm.selected": "Da li stvarno želite da obrišete odabrane datoteke? Ova akcija se ne može poništiti.",
|
||||
"files.empty": "Još nema datoteka...",
|
||||
|
||||
"filter": "Filter",
|
||||
|
||||
"form.discard": "Odbaci promjene",
|
||||
"form.discard.confirm": "Da li stvarno želite <strong>odbaciti sve promjene</strong>?",
|
||||
"form.locked": "Ovaj sadržaj je vama onemogućen jer ga trenutno uređuje drugi korisnik",
|
||||
"form.unsaved": "Trenutne promjene još uvijek nisu spremljene",
|
||||
"form.preview": "Pregledaj izmjene",
|
||||
"form.preview.draft": "Pregledaj skicu",
|
||||
|
||||
"hide": "Sakrij",
|
||||
"hour": "Sat",
|
||||
"hue": "Nijansa",
|
||||
"import": "Uvoz",
|
||||
"info": "Info",
|
||||
"insert": "Umetni",
|
||||
"insert.after": "Umetni nakon",
|
||||
"insert.before": "Umetni prije",
|
||||
"install": "Instaliraj",
|
||||
|
||||
"installation": "Instalacija",
|
||||
"installation.completed": "Panel je instaliran",
|
||||
"installation.disabled": "Instaler za panel je onemogućen na javnim serverima po defaultu. Pokreni instaler na lokalnom okruženju ili omogući sa <code>panel.install</code> opcijom.",
|
||||
"installation.issues.accounts": "Folder <code>/site/accounts</code> ne postoji ili nema dozvolu pisanja",
|
||||
"installation.issues.content": "Folder <code>/content</code> ne postoji ili nema dozvolu pisanja",
|
||||
"installation.issues.curl": "Ekstenzija <code>CURL</code> je neophodna",
|
||||
"installation.issues.headline": "Panel se ne može instalirati",
|
||||
"installation.issues.mbstring": "Ekstenzija <code>MB String</code> je neophodna",
|
||||
"installation.issues.media": "Folder <code>/media</code> ne postoji ili nema dozvolu pisanja",
|
||||
"installation.issues.php": "Obavezno koristite <code>PHP 7+</code>",
|
||||
"installation.issues.sessions": "Folder <code>/site/sessions</code> ne postoji ili nema dozvolu pisanja",
|
||||
|
||||
"language": "Jezik",
|
||||
"language.code": "Kod",
|
||||
"language.convert": "Postavi kao zadani",
|
||||
"language.convert.confirm": "<p>Da li stvarno želite konvertirati <strong>{name}</strong> u zadani jezik? Ova akcija se ne može poništiti. </p><p>Ukoliko <strong>{name}</strong> sadrži dijelove bez prijevoda, neće postojati važeći fallback što može prouzrokovati prazne dijelove na ovoj stranici.</p>",
|
||||
"language.create": "Dodaj novi jezik",
|
||||
"language.default": "Zadani jezik",
|
||||
"language.delete.confirm": "Da li stvarno želite obrisati jezik <strong>{name}</strong> including all translations? This cannot be undone!",
|
||||
"language.deleted": "Jezik je obrisan",
|
||||
"language.direction": "Smjer čitanja",
|
||||
"language.direction.ltr": "Sa lijeva na desno",
|
||||
"language.direction.rtl": "Sa desna na lijevo",
|
||||
"language.locale": "PHP lokalizacijski string",
|
||||
"language.locale.warning": "Sistem koristi prilagođenu lokalizaciju. Promijeni postavke u jezičnoj datoteci u /site/languages",
|
||||
"language.name": "Naziv",
|
||||
"language.secondary": "Sekundarni jezik",
|
||||
"language.settings": "Postavke jezika",
|
||||
"language.updated": "Jezik je izmijenjen",
|
||||
"language.variables": "Jezične varijable",
|
||||
"language.variables.empty": "Još nema prijevoda",
|
||||
|
||||
"language.variable.delete.confirm": "Da li stvarno želite da obrišete varijablu za {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Ključ",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "Varijablu nije moguće pronaći",
|
||||
"language.variable.value": "Vrijednost",
|
||||
|
||||
"languages": "Jezici",
|
||||
"languages.default": "Zadani jezik",
|
||||
"languages.empty": "Jezici još nisu definirani",
|
||||
"languages.secondary": "Sekundarni jezici",
|
||||
"languages.secondary.empty": "Sekundarni jezici još nisu definirani",
|
||||
|
||||
"license": "Licenca",
|
||||
"license.activate": "Aktivirajte odmah",
|
||||
"license.activate.label": "Aktivirajte vašu licencu",
|
||||
"license.activate.domain": "Vaša licenca će biti aktivirana za <strong>{host}</strong>.",
|
||||
"license.activate.local": "Upravo ćete aktivirati svoju Kirby licencu za vašu lokalnu domenu {host}. Ako će ova stranica biti postavljena na javnu domenu, molimo vas da je tamo aktivirate. Ako je {host} domena za koju želite koristiti svoju licencu, molimo vas da nastavite.",
|
||||
"license.activated": "Aktivirano",
|
||||
"license.buy": "Kupite licencu",
|
||||
"license.code": "Kod",
|
||||
"license.code.help": "Primili ste vašu licencu nakon kupovine putem e-maila. Molimo da je kopirate i zalijepite ovdje.",
|
||||
"license.code.label": "Unesite vašu licencu",
|
||||
"license.status.active.info": "Uključuje nove glavne verzije do {date}",
|
||||
"license.status.active.label": "Važeća licenca",
|
||||
"license.status.demo.info": "Ovo je demo instalacija",
|
||||
"license.status.demo.label": "Demo",
|
||||
"license.status.inactive.info": "Obnovi licencu da bi se ažuriralo na nove glavne verzije",
|
||||
"license.status.inactive.label": "Nema novih glavnih verzija",
|
||||
"license.status.legacy.bubble": "Spremni da obnovite vašu licencu?",
|
||||
"license.status.legacy.info": "Vaša licenca ne pokriva ovu verziju",
|
||||
"license.status.legacy.label": "Obnovite vašu licencu",
|
||||
"license.status.missing.bubble": "Spremni da objavite vašu stranicu?",
|
||||
"license.status.missing.info": "Ne postoji važeća licenca",
|
||||
"license.status.missing.label": "Aktivirajte vašu licencu",
|
||||
"license.status.unknown.info": "Status licence je nepoznat",
|
||||
"license.status.unknown.label": "Nepoznato",
|
||||
"license.manage": "Upravljanje vašim licencama",
|
||||
"license.purchased": "Kupljeno",
|
||||
"license.success": "Hvala što podržavate Kirby",
|
||||
"license.unregistered.label": "Neregistrovano",
|
||||
|
||||
"link": "Link",
|
||||
"link.text": "Tekst linka",
|
||||
|
||||
"loading": "Učitavanje",
|
||||
|
||||
"lock.unsaved": "Izmjene nisu spremljene",
|
||||
"lock.unsaved.empty": "Ne postoje druge izmjene za spremanje",
|
||||
"lock.unsaved.files": "Nespremljene datoteke",
|
||||
"lock.unsaved.pages": "Nespremljene stranice",
|
||||
"lock.unsaved.users": "Nespremljeni računi",
|
||||
"lock.isLocked": "Izmjene od strane <strong>{email}</strong>",
|
||||
"lock.unlock": "Otključaj",
|
||||
"lock.unlock.submit": "Otključaj i prepiši preko nespremljenih promjena od <strong>{email}</strong>",
|
||||
"lock.isUnlocked": "Vaše izmjene su zamijenjene sa izmjenama drugog korisnika. Dostupne su za download, te ih možete spojiti manuelno.",
|
||||
|
||||
"login": "Prijava",
|
||||
"login.code.label.login": "Kod za prijavu",
|
||||
"login.code.label.password-reset": "Kod za resetiranje šifre",
|
||||
"login.code.placeholder.email": "000 000",
|
||||
"login.code.placeholder.totp": "000000",
|
||||
"login.code.text.email": "Ako je vaša e-mail adresa registrirana, traženi kod je poslan putem e-maila.",
|
||||
"login.code.text.totp": "Unesite jednokratni kod iz vaše autentifikacijske aplikacije.",
|
||||
"login.email.login.body": "Zdravo {user.nameOrEmail},\n\nNedavno ste zatražili kod za prijavu na Panel stranice {site}.\nSljedeći kod za prijavu važiće {timeout} minuta:\n\n{code}\n\nAko niste zatražili kod za prijavu, molimo vas da zanemarite ovaj e-mail ili kontaktirate svog administratora ako imate pitanja.\nIz sigurnosnih razloga, molimo vas da NE prosljeđujete ovaj e-mail.",
|
||||
"login.email.login.subject": "Vaš kod za prijavu",
|
||||
"login.email.password-reset.body": "Zdravo {user.nameOrEmail},\n\nNedavno ste zatražili kod za resetiranje šifre za Panel stranice {site}.\nSljedeći kod za resetiranje šifre važiće {timeout} minuta:\n\n{code}\n\nAko niste zatražili kod za resetiranje šifre, molimo vas da zanemarite ovaj e-mail ili kontaktirate svog administratora ako imate pitanja.\nIz sigurnosnih razloga, molimo vas da NE prosljeđujete ovaj e-mail.",
|
||||
"login.email.password-reset.subject": "Kod za resetiranje šifre",
|
||||
"login.remember": "Zapamti moju prijavu",
|
||||
"login.reset": "Resetiraj šifru",
|
||||
"login.toggleText.code.email": "Prijava putem emaila",
|
||||
"login.toggleText.code.email-password": "Prijava sa šifrom",
|
||||
"login.toggleText.password-reset.email": "Zaboravljena šifra?",
|
||||
"login.toggleText.password-reset.email-password": "← Nazad na login",
|
||||
"login.totp.enable.option": "Postavite jednokratne kodove",
|
||||
"login.totp.enable.intro": "Autentifikacijske aplikacije mogu generirati jednokratne kodove koji se koriste kao drugi faktor prilikom prijavljivanja u vaš račun.",
|
||||
"login.totp.enable.qr.label": "1. Skeniraj ovaj QR kod",
|
||||
"login.totp.enable.qr.help": "Skeniranje nije moguće? Dodaj ključ za postavljanje <code>{secret}</code> ručno u aplikaciju za autentifikaciju.",
|
||||
"login.totp.enable.confirm.headline": "2. Potvrdi sa generisanim kodom",
|
||||
"login.totp.enable.confirm.text": "Vaša aplikacija generira novi jednokratni kod svakih 30 sekundi. Unesite trenutni kod da završite postavljanje:",
|
||||
"login.totp.enable.confirm.label": "Trenutni kod",
|
||||
"login.totp.enable.confirm.help": "Nakon ovih postavki, tražićemo od vas jednokratni kod prilikom svakog prijavljivanja.",
|
||||
"login.totp.enable.success": "Jednokratni kodovi omogućeni",
|
||||
"login.totp.disable.option": "Onemogući jednokratne kodove",
|
||||
"login.totp.disable.label": "Unesite vaš password da bi onemogućili jednokratne kodove",
|
||||
"login.totp.disable.help": "Ubuduće, drugačiji drugi faktor poput koda za prijavu dostavljenog putem e-maila će biti zatražen prilikom slijedeće prijave. Uvijek ponovo možete postaviti jednokratne kodove kasnije.",
|
||||
"login.totp.disable.admin": "<p>Ovo će onemogućiti jednokratne kodove za <strong>{user}</strong>.</p><p>Ubuduće, drugačiji drugi faktor poput koda za prijavu dostavljenog putem e-maila će biti zatražen prilikom prijave. {user} može postaviti jednokratne kodove nakon njihove slijedeće prijave.</p>",
|
||||
"login.totp.disable.success": "Jednokratni kodovi onemogućeni",
|
||||
|
||||
"logout": "Odjava",
|
||||
|
||||
"merge": "Spoji",
|
||||
"menu": "Izbornik",
|
||||
"meridiem": "AM/PM",
|
||||
"mime": "Tip medija",
|
||||
"minutes": "Minute",
|
||||
|
||||
"month": "Mjesec",
|
||||
"months.april": "April",
|
||||
"months.august": "August",
|
||||
"months.december": "Decembar",
|
||||
"months.february": "Feburar",
|
||||
"months.january": "Januar",
|
||||
"months.july": "Juli",
|
||||
"months.june": "Juni",
|
||||
"months.march": "Mart",
|
||||
"months.may": "Maj",
|
||||
"months.november": "Novembar",
|
||||
"months.october": "Oktobar",
|
||||
"months.september": "Septembar",
|
||||
|
||||
"more": "Više",
|
||||
"move": "Pomjeri",
|
||||
"name": "Ime",
|
||||
"next": "Dalje",
|
||||
"night": "Noć",
|
||||
"no": "ne",
|
||||
"off": "off",
|
||||
"on": "on",
|
||||
"open": "Otvori",
|
||||
"open.newWindow": "Otvori u novom prozoru",
|
||||
"option": "Opcija",
|
||||
"options": "Opcije",
|
||||
"options.none": "Nema opcija",
|
||||
"options.all": "Prikaži svih {count} options",
|
||||
|
||||
"orientation": "Orijentacija",
|
||||
"orientation.landscape": "Pejzaž",
|
||||
"orientation.portrait": "Portret",
|
||||
"orientation.square": "Kvadrat",
|
||||
|
||||
"page": "Strana",
|
||||
"page.blueprint": "Ova stranica još uvijek nema blueprint. Možete definirati postavke u <strong>/site/blueprints/pages/{blueprint}.yml</strong>",
|
||||
"page.changeSlug": "Promijeni URL",
|
||||
"page.changeSlug.fromTitle": "Kreiraj iz naslova",
|
||||
"page.changeStatus": "Promijeni status",
|
||||
"page.changeStatus.position": "Odaberi poziciju",
|
||||
"page.changeStatus.select": "Odaberi novi status",
|
||||
"page.changeTemplate": "Promijeni predložak",
|
||||
"page.changeTemplate.notice": "Promjena predloška stranice će ukloniti sadržaj za polja koja se ne podudaraju po tipu. Koristite s oprezom.",
|
||||
"page.create": "Kreiraj kao {status}",
|
||||
"page.delete.confirm": "Da li stvarno želite obrisati <strong>{title}</strong>?",
|
||||
"page.delete.confirm.subpages": "<strong>Ova stranica ima podstranice</strong>. <br>Sve podstranice će također biti obrisane.",
|
||||
"page.delete.confirm.title": "Napiši naslov stranice kao potvrdu ove akcije",
|
||||
"page.duplicate.appendix": "Kopiraj",
|
||||
"page.duplicate.files": "Kopiraj datoteke",
|
||||
"page.duplicate.pages": "Kopiraj stranice",
|
||||
"page.move": "Premjesti stranicu",
|
||||
"page.sort": "Promijeni poziciju",
|
||||
"page.status": "Status",
|
||||
"page.status.draft": "Skica",
|
||||
"page.status.draft.description": "Stranica je u izradi, te je vidljiva jedino prijavljenim urednicima",
|
||||
"page.status.listed": "Javno",
|
||||
"page.status.listed.description": "Stranica je javno dostupna",
|
||||
"page.status.unlisted": "Neizlistano",
|
||||
"page.status.unlisted.description": "Stranica je dostupna putem direktnog URL-a",
|
||||
|
||||
"pages": "Stranice",
|
||||
"pages.delete.confirm.selected": "Da li stvarno želite da obrišete odabrane stranice? Ova akcija se ne može poništiti.",
|
||||
"pages.empty": "Još nema stranica...",
|
||||
"pages.status.draft": "Skica",
|
||||
"pages.status.listed": "Javno",
|
||||
"pages.status.unlisted": "Neizlistano",
|
||||
|
||||
"pagination.page": "Strana",
|
||||
|
||||
"password": "Šifra",
|
||||
"paste": "Zalijepi",
|
||||
"paste.after": "Zalijepi nakon",
|
||||
"paste.success": "{count} zalijepljeno!",
|
||||
"pixel": "Piksel",
|
||||
"plugin": "Plugin",
|
||||
"plugins": "Plugini",
|
||||
"prev": "Previous",
|
||||
"preview": "Pregled",
|
||||
|
||||
"publish": "Objavi",
|
||||
"published": "Javno",
|
||||
|
||||
"remove": "Remove",
|
||||
"rename": "Preimenuj",
|
||||
"renew": "Obnovi",
|
||||
"replace": "Zamijeni",
|
||||
"replace.with": "Zamijeni sa",
|
||||
"retry": "Pokušaj ponovo",
|
||||
"revert": "Revert",
|
||||
"revert.confirm": "Da li stvarno želite <strong>obrisati sve nespremljene promjene</strong>?",
|
||||
|
||||
"role": "Uloga",
|
||||
"role.admin.description": "Administrator ima sva prava",
|
||||
"role.admin.title": "Administrator",
|
||||
"role.all": "Sve",
|
||||
"role.empty": "Za ovu ulogu ne postoje korisnici",
|
||||
"role.description.placeholder": "Bez opisa",
|
||||
"role.nobody.description": "Ovo je pomoćna uloga bez ikakvih prava",
|
||||
"role.nobody.title": "Niko",
|
||||
|
||||
"save": "Spremi",
|
||||
"saved": "Spremljeno",
|
||||
"search": "Traži",
|
||||
"searching": "Traženje",
|
||||
"search.min": "Unesi {min} znakova za pretraživanje",
|
||||
"search.all": "Prikaži svih {count} rezultata",
|
||||
"search.results.none": "Nema rezultata",
|
||||
|
||||
"section.invalid": "Ova sekcija je nevažeća",
|
||||
"section.required": "Ova sekcija je potrebna",
|
||||
|
||||
"security": "Sigurnost",
|
||||
"select": "Odaberi",
|
||||
"server": "Server",
|
||||
"settings": "Postavke",
|
||||
"show": "Prikaži",
|
||||
"site.blueprint": "Stranica još uvijek nema blueprint. Možete definirati postavke u <strong>/site/blueprints/site.yml</strong>",
|
||||
"size": "Veličina",
|
||||
"slug": "URL nastavak",
|
||||
"sort": "Sortiranje",
|
||||
"sort.drag": "Prevuci za sortiranje ...",
|
||||
"split": "Podijeli",
|
||||
|
||||
"stats.empty": "Nema izvještaja",
|
||||
"status": "Status",
|
||||
|
||||
"system.info.copy": "Kopiraj info",
|
||||
"system.info.copied": "Sistemske info kopirane",
|
||||
"system.issues.content": "Čini se da je content folder izložen",
|
||||
"system.issues.eol.kirby": "Vaša instalirana Kirby verzija je dostigla end-of-life i neće primati daljnja sigurnosna ažuriranja",
|
||||
"system.issues.eol.plugin": "Vaša instalirana verzija plugina { plugin } je dostigla end-of-life i neće primati daljnja sigurnosna ažuriranja",
|
||||
"system.issues.eol.php": "Vaša instalirana PHP verzija { release } je dostigla end-of-life i neće primati daljnja sigurnosna ažuriranja",
|
||||
"system.issues.debug": "Debugiranje se mora isključiti u produkciji",
|
||||
"system.issues.git": "Čini se da je .git folder izložen",
|
||||
"system.issues.https": "Preporučujemo HTTPS za sve vaše stranice",
|
||||
"system.issues.kirby": "Čini se da je kirby folder izložen",
|
||||
"system.issues.local": "Stranica radi lokalno uz opuštene signurnosne provjere",
|
||||
"system.issues.site": "Čini se da je site folder izložen",
|
||||
"system.issues.vue.compiler": "Vue template compiler je omogućen",
|
||||
"system.issues.vulnerability.kirby": "Vaša instalacija je možda pogođena slijedećim sigurnosnim propustom ({ severity } stepen): { description }",
|
||||
"system.issues.vulnerability.plugin": "Vaša instalacija je možda pogođena slijedećim sigurnosnim propustom u pluginu {plugin} ({ severity } stepen): { description }",
|
||||
"system.updateStatus": "Ažuriraj status",
|
||||
"system.updateStatus.error": "Nije moguće provjeriti za ažuriranja",
|
||||
"system.updateStatus.not-vulnerable": "Nema poznatih sigurnosnih propusta",
|
||||
"system.updateStatus.security-update": "Besplatno sigurnosno ažiriranje { version } dostupno",
|
||||
"system.updateStatus.security-upgrade": "Nadogradnja { version } sa sigurnosnim popravkama dostupna",
|
||||
"system.updateStatus.unreleased": "Neobjavljena verzija",
|
||||
"system.updateStatus.up-to-date": "Ažurirano",
|
||||
"system.updateStatus.update": "Besplatno ažuriranje { version } dostupno",
|
||||
"system.updateStatus.upgrade": "Nadogradnja { version } dostupna",
|
||||
|
||||
"tel": "Telefon",
|
||||
"tel.placeholder": "+38761222333",
|
||||
"template": "Predložak",
|
||||
|
||||
"theme": "Tema",
|
||||
"theme.light": "Svjetla upaljena",
|
||||
"theme.dark": "Svjetla ugašena",
|
||||
"theme.automatic": "Uskladi sa sistemskim postavkama",
|
||||
|
||||
"title": "Naslov",
|
||||
"today": "Danas",
|
||||
|
||||
"toolbar.button.clear": "Očisti formatiranje",
|
||||
"toolbar.button.code": "Kod",
|
||||
"toolbar.button.bold": "Podebljano",
|
||||
"toolbar.button.email": "Email",
|
||||
"toolbar.button.headings": "Naslovi",
|
||||
"toolbar.button.heading.1": "Naslov 1",
|
||||
"toolbar.button.heading.2": "Naslov 2",
|
||||
"toolbar.button.heading.3": "Naslov 3",
|
||||
"toolbar.button.heading.4": "Naslov 4",
|
||||
"toolbar.button.heading.5": "Naslov 5",
|
||||
"toolbar.button.heading.6": "Naslov 6",
|
||||
"toolbar.button.italic": "Kurziv",
|
||||
"toolbar.button.file": "Datoteka",
|
||||
"toolbar.button.file.select": "Odaberi datoteku",
|
||||
"toolbar.button.file.upload": "Uploadaj datoteku",
|
||||
"toolbar.button.link": "Link",
|
||||
"toolbar.button.paragraph": "Paragraf",
|
||||
"toolbar.button.strike": "Precrtano",
|
||||
"toolbar.button.sub": "Podpis",
|
||||
"toolbar.button.sup": "Nadpis",
|
||||
"toolbar.button.ol": "Uređena list",
|
||||
"toolbar.button.underline": "Podvučeno",
|
||||
"toolbar.button.ul": "Označena lista",
|
||||
|
||||
"translation.author": "Faris Mujakić",
|
||||
"translation.direction": "ltr",
|
||||
"translation.name": "Bosanski",
|
||||
"translation.locale": "bs_BA",
|
||||
|
||||
"type": "Tip",
|
||||
|
||||
"upload": "Uploadaj",
|
||||
"upload.error.cantMove": "Uploadana datoteka se ne može premjestiti",
|
||||
"upload.error.cantWrite": "Greška prilikom pisanja datoteke na disk",
|
||||
"upload.error.default": "Datoteka se ne može uploadati",
|
||||
"upload.error.extension": "Upload zaustavljen od strane ekstenzije",
|
||||
"upload.error.formSize": "Uploadana datoteka premašuje MAX_FILE_SIZE direktivu navedenu u formi",
|
||||
"upload.error.iniPostSize": "Uploadana datoteka premašuje post_max_size direktivu u php.ini",
|
||||
"upload.error.iniSize": "Uploadana datoteka premašuje upload_max_filesize direktivu u php.ini",
|
||||
"upload.error.noFile": "Datoteka nije uploadana",
|
||||
"upload.error.noFiles": "Datoteke nisu uploadane",
|
||||
"upload.error.partial": "Datoteka je djelimično uploadana",
|
||||
"upload.error.tmpDir": "Nedostaje privremeni folder",
|
||||
"upload.errors": "Greška",
|
||||
"upload.progress": "Slanje...",
|
||||
|
||||
"url": "Url",
|
||||
"url.placeholder": "https://example.com",
|
||||
|
||||
"user": "Korisnik",
|
||||
"user.blueprint": "Možete definirati dodatne sekcije i polja forme za ovu ulogu korisnika u <strong>/site/blueprints/users/{role}.yml</strong>",
|
||||
"user.changeEmail": "Promijeni email",
|
||||
"user.changeLanguage": "Promijeni jezik",
|
||||
"user.changeName": "Preimenuj ovog korisnika",
|
||||
"user.changePassword": "Promijeni šifru",
|
||||
"user.changePassword.current": "Trenutna šifra",
|
||||
"user.changePassword.new": "Nova šifra",
|
||||
"user.changePassword.new.confirm": "Potvrdi novu šifru...",
|
||||
"user.changeRole": "Promijeni ulogu",
|
||||
"user.changeRole.select": "Odaberi novu ulogu",
|
||||
"user.create": "Dodaj novog korisnika",
|
||||
"user.delete": "Obriši ovog korisnika",
|
||||
"user.delete.confirm": "Da li stvarno želite obrisati <br><strong>{email}</strong>?",
|
||||
|
||||
"users": "Korisnici",
|
||||
|
||||
"version": "Verzija",
|
||||
"version.changes": "Promijenjena verzija",
|
||||
"version.compare": "Uporedi verzije",
|
||||
"version.current": "Trenutna verzija",
|
||||
"version.latest": "Zadnja verzija",
|
||||
"versionInformation": "Informacije o verziji",
|
||||
|
||||
"view": "Pregled",
|
||||
"view.account": "Tvoj račun",
|
||||
"view.installation": "Instalacija",
|
||||
"view.languages": "Jezici",
|
||||
"view.resetPassword": "Resetiraj šifru",
|
||||
"view.site": "Stranica",
|
||||
"view.system": "Sistem",
|
||||
"view.users": "Korisnici",
|
||||
|
||||
"welcome": "Dobrodošli",
|
||||
"year": "Godina",
|
||||
"yes": "da"
|
||||
}
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "Zatím žádné překlady",
|
||||
|
||||
"language.variable.delete.confirm": "Doopravdy chcete smazat proměnou {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Klíč",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "Proměnná nebyla nalezena",
|
||||
"language.variable.value": "Hodnota",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
|
|
@ -307,7 +307,7 @@
|
|||
"field.blocks.delete.confirm.all": "Willst du wirklich alle Blöcke löschen?",
|
||||
"field.blocks.delete.confirm.selected": "Willst du wirklich die ausgewählten Blöcke löschen?",
|
||||
"field.blocks.empty": "Keine Blöcke",
|
||||
"field.blocks.fieldsets.empty": "Keine Block Definition",
|
||||
"field.blocks.fieldsets.empty": "Keine Blockdefinitionen",
|
||||
"field.blocks.fieldsets.label": "Bitte wähle einen Blocktyp aus …",
|
||||
"field.blocks.fieldsets.paste": "Drücke <kbd>{{ shortcut }}</kbd> um Layouts/Blocks von deinem Clipboard zu importieren. <small>Nur die, die im aktuellen Feld erlaubt sind werden eingefügt.</small>",
|
||||
"field.blocks.gallery.name": "Galerie",
|
||||
|
@ -357,7 +357,7 @@
|
|||
"field.blocks.video.url.placeholder": "https://youtube.com/?v=",
|
||||
|
||||
"field.entries.delete.confirm.all": "Möchtest du wirklich alle Einträge löschen?",
|
||||
"field.entries.empty": "Es bestehen keine Einträge.",
|
||||
"field.entries.empty": "Keine Einträge",
|
||||
|
||||
"field.files.empty": "Keine Dateien ausgewählt",
|
||||
"field.files.empty.single": "Keine Dateien ausgewählt",
|
||||
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "Keine Übersetzung",
|
||||
|
||||
"language.variable.delete.confirm": "Willst du wirklich die Variable \"{key}\" entfernen?",
|
||||
"language.variable.entries": "Werte",
|
||||
"language.variable.entries.help": "Jeder Eintrag wird für die entsprechende Anzahl verwendet, z. B. werden drei Einträge in der Reihenfolge mit den Anzahlen <code>0</code>, <code>1</code>, <code>2 und mehr</code> übereinstimmen. Verwende den Platzhalter <code>{count}</code>, um die tatsächliche Anzahl einzufügen.",
|
||||
"language.variable.key": "Schlüssel",
|
||||
"language.variable.multiple": "Zählbar?",
|
||||
"language.variable.multiple.text": "Benutze unterschiedliche Übersetzungen, abhängig von der Anzahl",
|
||||
"language.variable.multiple.help": "Du kannst verschiedene Werte anlegen, die von einer Anzahl abhängen. Diese übergibst du zusammen mit der Sprachvariablen. So kannst du dynamische Übersetzungen erstellen, zum Beispiel für Singular und Plural.",
|
||||
"language.variable.notFound": "Die Variable konnte nicht gefunden werden",
|
||||
"language.variable.value": "Wert",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
"coordinates": "Coordinates",
|
||||
"copy": "Copy",
|
||||
"copy.all": "Copy all",
|
||||
"copy.success": "{count} copied!",
|
||||
"copy.success": "Copied",
|
||||
"copy.success.multiple": "{count} copied!",
|
||||
"copy.url": "Copy URL",
|
||||
"create": "Create",
|
||||
"custom": "Custom",
|
||||
|
||||
|
@ -90,6 +92,19 @@
|
|||
|
||||
"error.cache.type.invalid": "Invalid cache type \"{type}\"",
|
||||
|
||||
"error.content.lock.delete": "The version is locked and cannot be deleted",
|
||||
"error.content.lock.move": "The source version is locked and cannot be moved",
|
||||
"error.content.lock.publish": "This version is already published",
|
||||
"error.content.lock.replace": "The version is locked and cannot be replaced",
|
||||
"error.content.lock.update": "The version is locked and cannot be updated",
|
||||
|
||||
"error.entries.max.plural": "You must not add more than {max} entries",
|
||||
"error.entries.max.singular": "You must not add more than one entry",
|
||||
"error.entries.min.plural": "You must add at least {min} entries",
|
||||
"error.entries.min.singular": "You must add at least one entry",
|
||||
"error.entries.supports": "\"{type}\" field type is not supported for the entries field",
|
||||
"error.entries.validation": "There's an error on the \"{field}\" field in row {index}",
|
||||
|
||||
"error.email.preset.notFound": "The email preset \"{name}\" cannot be found",
|
||||
|
||||
"error.field.converter.invalid": "Invalid converter \"{converter}\"",
|
||||
|
@ -101,6 +116,7 @@
|
|||
"error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")",
|
||||
"error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"",
|
||||
|
||||
"error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.",
|
||||
"error.file.duplicate": "A file with the name \"{filename}\" already exists",
|
||||
"error.file.extension.forbidden": "The extension \"{extension}\" is not allowed",
|
||||
"error.file.extension.invalid": "Invalid extension: {extension}",
|
||||
|
@ -119,6 +135,7 @@
|
|||
"error.file.name.missing": "The filename must not be empty",
|
||||
"error.file.notFound": "The file \"{filename}\" cannot be found",
|
||||
"error.file.orientation": "The orientation of the image must be \"{orientation}\"",
|
||||
"error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"",
|
||||
"error.file.type.forbidden": "You are not allowed to upload {type} files",
|
||||
"error.file.type.invalid": "Invalid file type: {type}",
|
||||
"error.file.undefined": "The file cannot be found",
|
||||
|
@ -162,6 +179,7 @@
|
|||
"error.page.delete": "The page \"{slug}\" cannot be deleted",
|
||||
"error.page.delete.confirm": "Please enter the page title to confirm",
|
||||
"error.page.delete.hasChildren": "The page has subpages and cannot be deleted",
|
||||
"error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.",
|
||||
"error.page.delete.permission": "You are not allowed to delete \"{slug}\"",
|
||||
"error.page.draft.duplicate": "A page draft with the URL appendix \"{slug}\" already exists",
|
||||
"error.page.duplicate": "A page with the URL appendix \"{slug}\" already exists",
|
||||
|
@ -338,6 +356,9 @@
|
|||
"field.blocks.video.url.label": "Video-URL",
|
||||
"field.blocks.video.url.placeholder": "https://youtube.com/?v=",
|
||||
|
||||
"field.entries.delete.confirm.all": "Do you really want to delete all entries?",
|
||||
"field.entries.empty": "No entries yet",
|
||||
|
||||
"field.files.empty": "No files selected yet",
|
||||
"field.files.empty.single": "No file selected yet",
|
||||
|
||||
|
@ -373,10 +394,18 @@
|
|||
"file.sort": "Change position",
|
||||
|
||||
"files": "Files",
|
||||
"files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.",
|
||||
"files.empty": "No files yet",
|
||||
|
||||
"filter": "Filter",
|
||||
|
||||
"form.discard": "Discard changes",
|
||||
"form.discard.confirm": "Do you really want to <strong>discard all your changes</strong>?",
|
||||
"form.locked": "This content is disabled for you as it is currently edited by another user",
|
||||
"form.unsaved": "The current changes have not yet been saved",
|
||||
"form.preview": "Preview changes",
|
||||
"form.preview.draft": "Preview draft",
|
||||
|
||||
"hide": "Hide",
|
||||
"hour": "Hour",
|
||||
"hue": "Hue",
|
||||
|
@ -420,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
@ -452,6 +486,8 @@
|
|||
"license.status.missing.bubble": "Ready to launch your site?",
|
||||
"license.status.missing.info": "No valid license",
|
||||
"license.status.missing.label": "Please activate your license",
|
||||
"license.status.unknown.info": "The license status is unknown",
|
||||
"license.status.unknown.label": "Unknown",
|
||||
"license.manage": "Manage your licenses",
|
||||
"license.purchased": "Purchased",
|
||||
"license.success": "Thank you for supporting Kirby",
|
||||
|
@ -463,7 +499,10 @@
|
|||
"loading": "Loading",
|
||||
|
||||
"lock.unsaved": "Unsaved changes",
|
||||
"lock.unsaved.empty": "There are no more unsaved changes",
|
||||
"lock.unsaved.empty": "There are no unsaved changes",
|
||||
"lock.unsaved.files": "Unsaved files",
|
||||
"lock.unsaved.pages": "Unsaved pages",
|
||||
"lock.unsaved.users": "Unsaved accounts",
|
||||
"lock.isLocked": "Unsaved changes by {email}",
|
||||
"lock.unlock": "Unlock",
|
||||
"lock.unlock.submit": "Unlock and overwrite unsaved changes by <strong>{email}</strong>",
|
||||
|
@ -570,6 +609,7 @@
|
|||
"page.status.unlisted.description": "The page is only accessible via URL",
|
||||
|
||||
"pages": "Pages",
|
||||
"pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.",
|
||||
"pages.empty": "No pages yet",
|
||||
"pages.status.draft": "Drafts",
|
||||
"pages.status.listed": "Published",
|
||||
|
@ -586,6 +626,10 @@
|
|||
"plugins": "Plugins",
|
||||
"prev": "Previous",
|
||||
"preview": "Preview",
|
||||
|
||||
"publish": "Publish",
|
||||
"published": "Published",
|
||||
|
||||
"remove": "Remove",
|
||||
"rename": "Rename",
|
||||
"renew": "Renew",
|
||||
|
@ -605,6 +649,7 @@
|
|||
"role.nobody.title": "Nobody",
|
||||
|
||||
"save": "Save",
|
||||
"saved": "Saved",
|
||||
"search": "Search",
|
||||
"searching": "Searching",
|
||||
"search.min": "Enter {min} characters to search",
|
||||
|
@ -631,7 +676,6 @@
|
|||
|
||||
"system.info.copy": "Copy info",
|
||||
"system.info.copied": "System info copied",
|
||||
"system.issues.api.methods": "Your server does not support PATCH requests",
|
||||
"system.issues.content": "The content folder seems to be exposed",
|
||||
"system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates",
|
||||
"system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates",
|
||||
|
@ -658,6 +702,12 @@
|
|||
"tel": "Phone",
|
||||
"tel.placeholder": "+49123456789",
|
||||
"template": "Template",
|
||||
|
||||
"theme": "Theme",
|
||||
"theme.light": "Lights on",
|
||||
"theme.dark": "Lights off",
|
||||
"theme.automatic": "Match system default",
|
||||
|
||||
"title": "Title",
|
||||
"today": "Today",
|
||||
|
||||
|
@ -728,10 +778,13 @@
|
|||
"users": "Users",
|
||||
|
||||
"version": "Version",
|
||||
"version.changes": "Changed version",
|
||||
"version.compare": "Compare versions",
|
||||
"version.current": "Current version",
|
||||
"version.latest": "Latest version",
|
||||
"versionInformation": "Version information",
|
||||
|
||||
"view": "View",
|
||||
"view.account": "Your account",
|
||||
"view.installation": "Installation",
|
||||
"view.languages": "Languages",
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
|
|
@ -30,13 +30,13 @@
|
|||
"date.select": "Choisir une date",
|
||||
|
||||
"day": "Jour",
|
||||
"days.fri": "Ven",
|
||||
"days.mon": "Lun",
|
||||
"days.sat": "Sam",
|
||||
"days.sun": "Dim",
|
||||
"days.thu": "Jeu",
|
||||
"days.tue": "Mar",
|
||||
"days.wed": "Mer",
|
||||
"days.fri": "Ven.",
|
||||
"days.mon": "Lun.",
|
||||
"days.sat": "Sam.",
|
||||
"days.sun": "Dim.",
|
||||
"days.thu": "Jeu.",
|
||||
"days.tue": "Mar.",
|
||||
"days.wed": "Mer.",
|
||||
|
||||
"debugging": "Débogage",
|
||||
|
||||
|
@ -80,12 +80,12 @@
|
|||
"error.avatar.create.fail": "L’image du profil n’a pu être transférée",
|
||||
"error.avatar.delete.fail": "L’image du profil n’a pu être supprimée",
|
||||
"error.avatar.dimensions.invalid": "Veuillez choisir une image de profil de largeur et hauteur inférieures à 3000 pixels",
|
||||
"error.avatar.mime.forbidden": "L'image du profil utilisateur doit être un fichier JPEG ou PNG",
|
||||
"error.avatar.mime.forbidden": "L’image du profil utilisateur doit être un fichier JPEG ou PNG",
|
||||
|
||||
"error.blueprint.notFound": "Le blueprint « {name} » n’a pu être chargé",
|
||||
|
||||
"error.blocks.max.plural": "Vous ne devez pas ajouter plus de {max} blocs",
|
||||
"error.blocks.max.singular": "Vous ne devez pas ajouter plus d'un bloc",
|
||||
"error.blocks.max.singular": "Vous ne devez pas ajouter plus d’un bloc",
|
||||
"error.blocks.min.plural": "Vous devez ajouter au moins {min} blocs",
|
||||
"error.blocks.min.singular": "Vous devez ajouter au moins un bloc",
|
||||
"error.blocks.validation": "Il y a une erreur sur le champ « {field} » du bloc {index} utilisant le type de bloc « {fieldset} »",
|
||||
|
@ -119,9 +119,9 @@
|
|||
"error.file.delete.multiple": "Tous les fichiers n’ont pu être supprimés. Essayez avec chaque fichier restant individuellement pour voir quelle erreur empêche sa suppression.",
|
||||
"error.file.duplicate": "Un fichier nommé « {filename} » existe déjà",
|
||||
"error.file.extension.forbidden": "L’extension « {extension} » n’est pas autorisée",
|
||||
"error.file.extension.invalid": "Extension incorrecte : {extension}",
|
||||
"error.file.extension.invalid": "Extension invalide : {extension}",
|
||||
"error.file.extension.missing": "L’extension pour « {filename} » est manquante",
|
||||
"error.file.maxheight": "La hauteur de l'image ne doit pas excéder {height} pixels",
|
||||
"error.file.maxheight": "La hauteur de l’image ne doit pas excéder {height} pixels",
|
||||
"error.file.maxsize": "Le fichier est trop volumineux",
|
||||
"error.file.maxwidth": "La largeur de l’image ne doit pas excéder {width} pixels",
|
||||
"error.file.mime.differs": "Le fichier transféré doit être du même type de média « {mime} »",
|
||||
|
@ -143,11 +143,11 @@
|
|||
"error.form.incomplete": "Veuillez corriger toutes les erreurs du formulaire…",
|
||||
"error.form.notSaved": "Le formulaire n’a pu être enregistré",
|
||||
|
||||
"error.language.code": "Veuillez saisir un code correct pour la langue",
|
||||
"error.language.code": "Veuillez saisir un code valide pour la langue",
|
||||
"error.language.create.permission": "Vous n’êtes pas autorisé à créer une langue",
|
||||
"error.language.delete.permission": "Vous n’êtes pas autorisé à supprimer la langue",
|
||||
"error.language.duplicate": "Cette langue existe déjà",
|
||||
"error.language.name": "Veuillez saisir un nom correct pour la langue",
|
||||
"error.language.name": "Veuillez saisir un nom valide pour la langue",
|
||||
"error.language.notFound": "La langue n’a pu être trouvée",
|
||||
"error.language.update.permission": "Vous n’êtes pas autorisé à modifier la langue",
|
||||
|
||||
|
@ -155,7 +155,7 @@
|
|||
"error.layout.validation.settings": "Il y a une erreur dans les paramètres de la disposition {index}",
|
||||
|
||||
"error.license.domain": "Le domaine de la licence est manquant",
|
||||
"error.license.email": "Veuillez saisir un courriel correct",
|
||||
"error.license.email": "Veuillez saisir un courriel valide",
|
||||
"error.license.format": "Veuillez saisir un numéro de licence valide",
|
||||
"error.license.verification": "La licence n’a pu être vérifiée",
|
||||
|
||||
|
@ -168,7 +168,7 @@
|
|||
|
||||
"error.page.changeSlug.permission": "Vous n’êtes pas autorisé à modifier l’identifiant d’URL pour « {slug} »",
|
||||
"error.page.changeSlug.reserved": "Le chemin des pages de premier niveau ne doit pas commencer par « {path} »",
|
||||
"error.page.changeStatus.incomplete": "La page comporte des erreurs et ne peut pas être publiée",
|
||||
"error.page.changeStatus.incomplete": "La page comporte des erreurs et ne peut être publiée",
|
||||
"error.page.changeStatus.permission": "Le statut de cette page ne peut être modifié",
|
||||
"error.page.changeStatus.toDraft.invalid": "La page « {slug} » ne peut être convertie en brouillon",
|
||||
"error.page.changeTemplate.invalid": "Le modèle de la page « {slug} » ne peut être changé",
|
||||
|
@ -192,11 +192,11 @@
|
|||
"error.page.move.permission": "Vous n’êtes pas autorisé à déplacer « {slug} » ",
|
||||
"error.page.move.template": "Le modèle « {template} » n’est pas accepté en tant que sous-page de « {parent} »",
|
||||
"error.page.notFound": "La page « {slug} » n’a pu être trouvée",
|
||||
"error.page.num.invalid": "Veuillez saisir un numéro de position correct. Les numéros ne doivent pas être négatifs.",
|
||||
"error.page.slug.invalid": "Veuillez entrer un identifiant d’URL correct",
|
||||
"error.page.num.invalid": "Veuillez saisir un numéro de position valide. Les numéros ne doivent pas être négatifs.",
|
||||
"error.page.slug.invalid": "Veuillez entrer un identifiant d’URL valide",
|
||||
"error.page.slug.maxlength": "L’identifiant d’URL doit faire moins de « {length} » caractères",
|
||||
"error.page.sort.permission": "La page « {slug} » ne peut être réordonnée",
|
||||
"error.page.status.invalid": "Veuillez choisir un statut de page correct",
|
||||
"error.page.status.invalid": "Veuillez choisir un statut de page valide",
|
||||
"error.page.undefined": "La page n’a pu être trouvée",
|
||||
"error.page.update.permission": "Vous n’êtes pas autorisé à modifier « {slug} »",
|
||||
|
||||
|
@ -211,7 +211,7 @@
|
|||
"error.section.pages.min.singular": "La section « {section} » requiert au moins une page",
|
||||
|
||||
"error.section.notLoaded": "La section « {name} » n’a pu être chargée",
|
||||
"error.section.type.invalid": "Le type de section « {type} » est incorrect",
|
||||
"error.section.type.invalid": "Le type de section « {type} » est invalide",
|
||||
|
||||
"error.site.changeTitle.empty": "Le titre ne peut être vide",
|
||||
"error.site.changeTitle.permission": "Vous n’êtes pas autorisé à modifier le titre du site",
|
||||
|
@ -236,38 +236,38 @@
|
|||
"error.user.delete.lastUser": "Le dernier utilisateur ne peut être supprimé",
|
||||
"error.user.delete.permission": "Vous n’êtes pas autorisé à supprimer l’utilisateur « {name} »",
|
||||
"error.user.duplicate": "Un utilisateur avec le courriel « {email} » existe déjà",
|
||||
"error.user.email.invalid": "Veuillez saisir un courriel correct",
|
||||
"error.user.language.invalid": "Veuillez saisir une langue correcte",
|
||||
"error.user.email.invalid": "Veuillez saisir un courriel valide",
|
||||
"error.user.language.invalid": "Veuillez saisir une langue valide",
|
||||
"error.user.notFound": "L’utilisateur « {name} » n’a pu être trouvé",
|
||||
"error.user.password.excessive": "Veuillez entrer un mot de passe valide. Les mots de passe ne doivent pas dépasser 1000 caractères de long.",
|
||||
"error.user.password.invalid": "Veuillez saisir un mot de passe valide. Les mots de passe doivent comporter au moins 8 caractères.",
|
||||
"error.user.password.notSame": "Les mots de passe ne sont pas identiques",
|
||||
"error.user.password.undefined": "Cet utilisateur n’a pas de mot de passe",
|
||||
"error.user.password.wrong": "Mot de passe incorrect",
|
||||
"error.user.role.invalid": "Veuillez saisir un rôle correct",
|
||||
"error.user.role.invalid": "Veuillez saisir un rôle valide",
|
||||
"error.user.undefined": "L’utilisateur n’a pu être trouvé",
|
||||
"error.user.update.permission": "Vous n’êtes pas autorisé à modifier l’utilisateur « {name} »",
|
||||
|
||||
"error.validation.accepted": "Veuillez confirmer",
|
||||
"error.validation.alpha": "Veuillez saisir uniquement des caractères alphabétiques minuscules",
|
||||
"error.validation.alphanum": "Veuillez ne saisir que des minuscules de a à z et des chiffres de 0 à 9",
|
||||
"error.validation.anchor": "Veuillez entrer un lien ancré correct",
|
||||
"error.validation.anchor": "Veuillez entrer un lien d’ancrage correct",
|
||||
"error.validation.between": "Veuillez saisir une valeur entre « {min} » et « {max} »",
|
||||
"error.validation.boolean": "Veuillez confirmer ou refuser",
|
||||
"error.validation.color": "Veuillez entrer une couleur valide dans le format {format}",
|
||||
"error.validation.contains": "Veuillez saisir une valeur contenant « {needle} »",
|
||||
"error.validation.date": "Veuillez saisir une date correcte",
|
||||
"error.validation.date": "Veuillez saisir une date valide",
|
||||
"error.validation.date.after": "Veuillez saisir une date après {date}",
|
||||
"error.validation.date.before": "Veuillez saisir une date avant {date}",
|
||||
"error.validation.date.between": "Veuillez saisir une date entre {min} et {max}",
|
||||
"error.validation.denied": "Veuillez refuser",
|
||||
"error.validation.different": "La valeur ne doit pas être « {other} »",
|
||||
"error.validation.email": "Veuillez saisir un courriel correct",
|
||||
"error.validation.email": "Veuillez saisir un courriel valide",
|
||||
"error.validation.endswith": "La valeur doit se terminer par « {end} »",
|
||||
"error.validation.filename": "Veuillez saisir un nom de fichier correct",
|
||||
"error.validation.filename": "Veuillez saisir un nom de fichier valide",
|
||||
"error.validation.in": "Veuillez saisir l’un des éléments suivants: ({in})",
|
||||
"error.validation.integer": "Veuillez saisir un entier correct",
|
||||
"error.validation.ip": "Veuillez saisir une adresse IP correcte",
|
||||
"error.validation.integer": "Veuillez saisir un entier valide",
|
||||
"error.validation.ip": "Veuillez saisir une adresse IP valide",
|
||||
"error.validation.less": "Veuillez saisir une valeur inférieure à {max}",
|
||||
"error.validation.linkType": "Le type de lien n’est pas autorisé",
|
||||
"error.validation.match": "La valeur ne correspond pas au modèle attendu",
|
||||
|
@ -280,19 +280,19 @@
|
|||
"error.validation.more": "Veuillez saisir une valeur supérieure à {min}",
|
||||
"error.validation.notcontains": "Veuillez saisir une valeur ne contenant pas « {needle} »",
|
||||
"error.validation.notin": "Veuillez ne saisir aucun des éléments suivants: ({notIn})",
|
||||
"error.validation.option": "Veuillez sélectionner une option correcte",
|
||||
"error.validation.num": "Veuillez saisir un nombre correct",
|
||||
"error.validation.option": "Veuillez sélectionner une option valide",
|
||||
"error.validation.num": "Veuillez saisir un nombre valide",
|
||||
"error.validation.required": "Veuillez saisir quelque chose",
|
||||
"error.validation.same": "Veuillez saisir « {other} »",
|
||||
"error.validation.size": "La grandeur de la valeur doit être « {size} »",
|
||||
"error.validation.startswith": "La valeur doit commencer par « {start} »",
|
||||
"error.validation.tel": "Veuillez saisir un numéro de téléphone non formaté",
|
||||
"error.validation.time": "Veuillez saisir une heure correcte",
|
||||
"error.validation.time": "Veuillez saisir une heure valide",
|
||||
"error.validation.time.after": "Veuillez saisir une heure après {time}",
|
||||
"error.validation.time.before": "Veuillez saisir une heure avant {time}",
|
||||
"error.validation.time.between": "Veuillez saisir une heure entre {min} et {max}",
|
||||
"error.validation.uuid": "Veuillez saisir un UUID valide",
|
||||
"error.validation.url": "Veuillez saisir une URL correcte",
|
||||
"error.validation.url": "Veuillez saisir une URL valide",
|
||||
|
||||
"expand": "Déplier",
|
||||
"expand.all": "Tout déplier",
|
||||
|
@ -371,7 +371,7 @@
|
|||
|
||||
"field.object.empty": "Pas encore d‘information",
|
||||
|
||||
"field.pages.empty": "Pas encore de page sélectionnée",
|
||||
"field.pages.empty": "Pas encore de pages sélectionnées",
|
||||
"field.pages.empty.single": "Pas encore de page sélectionnée",
|
||||
|
||||
"field.structure.delete.confirm": "Voulez-vous vraiment supprimer cette ligne ?",
|
||||
|
@ -401,7 +401,7 @@
|
|||
|
||||
"form.discard": "Annuler les modifications",
|
||||
"form.discard.confirm": "Voulez-vous vraiment <strong>annuler toutes les modifications</strong> ?",
|
||||
"form.locked": "Ce contenu est désactivé pour vous car il est actuellement édité par un autre utilisateur.",
|
||||
"form.locked": "Vous ne pouvez pas modifier ce contenu car il est en cours d'édition par un autre utilisateur.",
|
||||
"form.unsaved": "Les modifications actuelles n’ont pas encore été enregistrées",
|
||||
"form.preview": "Prévisualiser les modifications",
|
||||
"form.preview.draft": "Prévisualiser le brouillon",
|
||||
|
@ -440,7 +440,7 @@
|
|||
"language.direction.ltr": "De gauche à droite",
|
||||
"language.direction.rtl": "De droite à gauche",
|
||||
"language.locale": "Locales PHP",
|
||||
"language.locale.warning": "Vous utilisez une Locale PHP personnalisée. Veuillez la modifier dans le fichier de langue situé dans /site/languages",
|
||||
"language.locale.warning": "Vous utilisez un identifiant régional personnalisée. Veuillez le modifier dans le fichier de langue situé dans /site/languages",
|
||||
"language.name": "Nom",
|
||||
"language.secondary": "Langue secondaire",
|
||||
"language.settings": "Préférences de langue",
|
||||
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "Pas encore de traductions",
|
||||
|
||||
"language.variable.delete.confirm": "Voulez-vous vraiment supprimer la variable pour {key} ?",
|
||||
"language.variable.entries": "Valeurs",
|
||||
"language.variable.entries.help": "Chaque chaîne sera utilisée pour son nombre d’éléments correspondant, par exemple trois chaînes correspondront dans l'ordre à <code>0</code>, <code>1</code>, <code>2 et plus</code> éléments. Utilisez le jeton <code>{count}</code> pour insérer le nombre d’éléments réel.",
|
||||
"language.variable.key": "Clé",
|
||||
"language.variable.multiple": "Dénombrable ?",
|
||||
"language.variable.multiple.text": "Utiliser des chaînes de traduction différentes",
|
||||
"language.variable.multiple.help": "Vous pouvez utiliser des valeurs différentes en fonction d'un nombre d’éléments que vous passez avec la variable de langue, ce qui vous permet de créer des traductions dynamiques, par exemple au singulier et au pluriel.",
|
||||
"language.variable.notFound": "La variable n’a pu être trouvée",
|
||||
"language.variable.value": "Valeur",
|
||||
|
||||
|
@ -463,7 +468,7 @@
|
|||
"license.activate": "Activer maintenant",
|
||||
"license.activate.label": "Veuillez activer votre licence",
|
||||
"license.activate.domain": "Votre licence sera activée pour <strong>{host}</strong>.",
|
||||
"license.activate.local": "Vous êtes sur le point d‘activer votre licence de Kirby pour votre domaine local <strong>{host}</strong>. Si ce site doit être activé sur un domaine publique, veuillez plutôt l‘activer là-bas. Si {host} est bien le domaine pour lequel vous voulez activer votre licence, veuillez continuer.",
|
||||
"license.activate.local": "Vous êtes sur le point d‘activer votre licence de Kirby pour votre domaine local <strong>{host}</strong>. Si ce site doit être activé sur un domaine publique, veuillez plutôt l’activer là-bas. Si {host} est bien le domaine pour lequel vous voulez activer votre licence, veuillez continuer.",
|
||||
"license.activated": "Activée",
|
||||
"license.buy": "Acheter une licence",
|
||||
"license.code": "Code",
|
||||
|
@ -530,7 +535,7 @@
|
|||
"login.totp.enable.confirm.help": "Après cette configuration, nous vous demanderons un code à usage unique à chaque connexion.",
|
||||
"login.totp.enable.success": "Codes à usage unique activés",
|
||||
"login.totp.disable.option": "Désactiver les codes à usage unique",
|
||||
"login.totp.disable.label": "Saisissez votre mot de passe pour désactiver les codes à usage unique.",
|
||||
"login.totp.disable.label": "Saisissez votre mot de passe pour désactiver les codes à usage unique",
|
||||
"login.totp.disable.help": "Un second facteur différent, par exemple un code de connexion envoyé par courriel, vous sera demandé à la connexion. Vous pourrez à nouveau configurer les codes à usage unique ultérieurement.",
|
||||
"login.totp.disable.admin": "<p>Cela désactivera les codes à usage unique pour <strong>{user}</strong>.</p><p>Un second facteur différent, par exemple un code de connexion envoyé par courriel lui sera demandé à la connexion. {user} pourra à nouveau configurer les codes à usage unique ultérieurement.</p>",
|
||||
"login.totp.disable.success": "Codes à usage unique désactivés",
|
||||
|
@ -695,12 +700,12 @@
|
|||
"system.updateStatus.upgrade": "Mise à jour { version } disponible",
|
||||
|
||||
"tel": "Téléphone",
|
||||
"tel.placeholder": "+33123456789",
|
||||
"tel.placeholder": "+3312345678",
|
||||
"template": "Modèle",
|
||||
|
||||
"theme": "Thème",
|
||||
"theme.light": "Allumer",
|
||||
"theme.dark": "Éteindre",
|
||||
"theme.light": "Clair",
|
||||
"theme.dark": "Sombre",
|
||||
"theme.automatic": "Suivre le réglage système",
|
||||
|
||||
"title": "Titre",
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "Engar þýðingar enn",
|
||||
|
||||
"language.variable.delete.confirm": "Ertu viss um að þú viljir nú fjarlægja breytuna fyrir {key}?",
|
||||
"language.variable.entries": "Gildi",
|
||||
"language.variable.entries.help": "Hver strengur verður notaður fyrir samsvarandi fjölda, t.d. þrír strengir munu passa til að telja <code>0</code>, <code>1</code>, <code>2 og fleiri</code>. Notaðu <code>{count}</code> staðgengilinn fyrir raunverulegan fjölda.",
|
||||
"language.variable.key": "Lykill",
|
||||
"language.variable.multiple": "Teljanlegt?",
|
||||
"language.variable.multiple.text": "Notaðu annan textastreng fyrir þýðingu",
|
||||
"language.variable.multiple.help": "Þú getur notað mismunandi gildi eftir því hvaða talningu þú sendir með tungumálabreytunni, sem gerir þér kleift að búa til breytilegar þýðingar, t.d. eintölu og fleirtölu.",
|
||||
"language.variable.notFound": "Breytan fannst hreint ekki",
|
||||
"language.variable.value": "Gildi",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "번역이 없습니다.",
|
||||
|
||||
"language.variable.delete.confirm": "변수({key})를 삭제할까요?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "키",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "변수를 찾을 수 없습니다.",
|
||||
"language.variable.value": "값",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "No translations yet",
|
||||
|
||||
"language.variable.delete.confirm": "Do you really want to delete the variable for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "The variable could not be found",
|
||||
"language.variable.value": "Value",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "Ingen oversettelse enda",
|
||||
|
||||
"language.variable.delete.confirm": "Ønsker du virkelig å slette variablen for {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "Variablen kan ikke bli funnet",
|
||||
"language.variable.value": "Verdi",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "Nog geen vertalingen",
|
||||
|
||||
"language.variable.delete.confirm": "Weet je zeker dat je de variabele voor {key} wil verwijderen?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Key",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "De variabele kan niet gevonden worden",
|
||||
"language.variable.value": "Waarde",
|
||||
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
"coordinates": "Współrzędne",
|
||||
"copy": "Kopiuj",
|
||||
"copy.all": "Skopiuj wszystko",
|
||||
"copy.success": "{count} skopiowanych!",
|
||||
"copy.success": "Skopiowane",
|
||||
"copy.success.multiple": "{count} skopiowanych!",
|
||||
"copy.url": "Copy URL",
|
||||
"copy.url": "Skopiuj URL",
|
||||
"create": "Utwórz",
|
||||
"custom": "Niestandardowe",
|
||||
|
||||
|
@ -92,16 +92,16 @@
|
|||
|
||||
"error.cache.type.invalid": "Nieprawidłowy typ pamięci podręcznej „{type}”",
|
||||
|
||||
"error.content.lock.delete": "The version is locked and cannot be deleted",
|
||||
"error.content.lock.move": "The source version is locked and cannot be moved",
|
||||
"error.content.lock.publish": "This version is already published",
|
||||
"error.content.lock.replace": "The version is locked and cannot be replaced",
|
||||
"error.content.lock.update": "The version is locked and cannot be updated",
|
||||
"error.content.lock.delete": "Ta wersja jest zablokowana i nie można jej usunąć",
|
||||
"error.content.lock.move": "Ta wersja jest zablokowana i nie można jej przenieść",
|
||||
"error.content.lock.publish": "Ta wersja jest już opublikowana",
|
||||
"error.content.lock.replace": "Ta wersja jest zablokowana i nie można jej zastąpić",
|
||||
"error.content.lock.update": "Ta wersja jest zablokowana i nie można jej zaktualizować",
|
||||
|
||||
"error.entries.max.plural": "You must not add more than {max} entries",
|
||||
"error.entries.max.singular": "You must not add more than one entry",
|
||||
"error.entries.min.plural": "You must add at least {min} entries",
|
||||
"error.entries.min.singular": "You must add at least one entry",
|
||||
"error.entries.max.plural": "Nie można dodać więcej niż {max} elementy/-ów",
|
||||
"error.entries.max.singular": "Nie można dodać więcej niż jednego elementu",
|
||||
"error.entries.min.plural": "Musisz dodać co najmniej {min} elementy/-ów",
|
||||
"error.entries.min.singular": "Musisz dodać co najmniej jeden element",
|
||||
"error.entries.supports": "\"{type}\" field type is not supported for the entries field",
|
||||
"error.entries.validation": "Wystąpił błąd w polu \"{field}\" w wierszu {index}",
|
||||
|
||||
|
@ -394,17 +394,17 @@
|
|||
"file.sort": "Zmień pozycję",
|
||||
|
||||
"files": "Pliki",
|
||||
"files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.",
|
||||
"files.delete.confirm.selected": "Czy na pewno chcesz usunąć wybrane pliki? Tej czynności nie można cofnąć.",
|
||||
"files.empty": "Nie ma jeszcze żadnych plików",
|
||||
|
||||
"filter": "Filtr",
|
||||
|
||||
"form.discard": "Discard changes",
|
||||
"form.discard": "Odrzuć zmiany",
|
||||
"form.discard.confirm": "Do you really want to <strong>discard all your changes</strong>?",
|
||||
"form.locked": "This content is disabled for you as it is currently edited by another user",
|
||||
"form.unsaved": "The current changes have not yet been saved",
|
||||
"form.preview": "Preview changes",
|
||||
"form.preview.draft": "Preview draft",
|
||||
"form.unsaved": "Bieżące zmiany nie zostały jeszcze zapisane",
|
||||
"form.preview": "Podejrzyj zmiany",
|
||||
"form.preview.draft": "Podejrzyj szkic",
|
||||
|
||||
"hide": "Ukryj",
|
||||
"hour": "Godzina",
|
||||
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "Nie ma jeszcze żadnych tłumaczeń",
|
||||
|
||||
"language.variable.delete.confirm": "Czy na pewno chcesz usunąć zmienną przypisaną do klucza {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Klucz",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "Nie udało się odnaleźć zmiennej",
|
||||
"language.variable.value": "Wartość",
|
||||
|
||||
|
@ -481,7 +486,7 @@
|
|||
"license.status.missing.bubble": "Gotowy/-a do uruchomienia strony?",
|
||||
"license.status.missing.info": "Brak ważnej licencji",
|
||||
"license.status.missing.label": "Aktywuj swoją licencję",
|
||||
"license.status.unknown.info": "The license status is unknown",
|
||||
"license.status.unknown.info": "Status licencji jest nieznany",
|
||||
"license.status.unknown.label": "Unknown",
|
||||
"license.manage": "Zarządzaj swoimi licencjami",
|
||||
"license.purchased": "Zakupiona",
|
||||
|
@ -495,9 +500,9 @@
|
|||
|
||||
"lock.unsaved": "Niezapisane zmiany",
|
||||
"lock.unsaved.empty": "Nie ma już żadnych niezapisanych zmian",
|
||||
"lock.unsaved.files": "Unsaved files",
|
||||
"lock.unsaved.pages": "Unsaved pages",
|
||||
"lock.unsaved.users": "Unsaved accounts",
|
||||
"lock.unsaved.files": "Niezapisane pliki",
|
||||
"lock.unsaved.pages": "Niezapisane strony",
|
||||
"lock.unsaved.users": "Niezapisane konta",
|
||||
"lock.isLocked": "Niezapisane zmiany autorstwa {email}",
|
||||
"lock.unlock": "Odblokuj",
|
||||
"lock.unlock.submit": "Odblokuj i nadpisz niezapisane zmiany autorstwa <strong>{email}</strong>",
|
||||
|
@ -622,7 +627,7 @@
|
|||
"prev": "Poprzednie",
|
||||
"preview": "Podgląd",
|
||||
|
||||
"publish": "Publish",
|
||||
"publish": "Opublikuj",
|
||||
"published": "Opublikowane",
|
||||
|
||||
"remove": "Usuń",
|
||||
|
@ -644,7 +649,7 @@
|
|||
"role.nobody.title": "Nikt",
|
||||
|
||||
"save": "Zapisz",
|
||||
"saved": "Saved",
|
||||
"saved": "Zapisane",
|
||||
"search": "Szukaj",
|
||||
"searching": "Searching",
|
||||
"search.min": "Aby wyszukać, wprowadź co najmniej {min} znaków",
|
||||
|
@ -698,10 +703,10 @@
|
|||
"tel.placeholder": "+48123456789",
|
||||
"template": "Szablon",
|
||||
|
||||
"theme": "Theme",
|
||||
"theme.light": "Lights on",
|
||||
"theme.dark": "Lights off",
|
||||
"theme.automatic": "Match system default",
|
||||
"theme": "Wygląd",
|
||||
"theme.light": "Jasny",
|
||||
"theme.dark": "Ciemny",
|
||||
"theme.automatic": "Zgodny z ustawieniami systemu",
|
||||
|
||||
"title": "Tytuł",
|
||||
"today": "Dzisiaj",
|
||||
|
@ -761,7 +766,7 @@
|
|||
"user.changeLanguage": "Zmień język",
|
||||
"user.changeName": "Zmień nazwę tego użytkownika",
|
||||
"user.changePassword": "Zmień hasło",
|
||||
"user.changePassword.current": "Your current password",
|
||||
"user.changePassword.current": "Twoje aktualne hasło",
|
||||
"user.changePassword.new": "Nowe hasło",
|
||||
"user.changePassword.new.confirm": "Potwierdź nowe hasło…",
|
||||
"user.changeRole": "Zmień rolę",
|
||||
|
@ -773,13 +778,13 @@
|
|||
"users": "Użytkownicy",
|
||||
|
||||
"version": "Wersja",
|
||||
"version.changes": "Changed version",
|
||||
"version.compare": "Compare versions",
|
||||
"version.changes": "Zmieniona wersja",
|
||||
"version.compare": "Porównaj wersje",
|
||||
"version.current": "Obecna wersja",
|
||||
"version.latest": "Ostatnia wersja",
|
||||
"versionInformation": "Informacje o wersji",
|
||||
|
||||
"view": "View",
|
||||
"view": "Pokaż",
|
||||
"view.account": "Twoje konto",
|
||||
"view.installation": "Instalacja",
|
||||
"view.languages": "Języki",
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "Nenhuma tradução ainda",
|
||||
|
||||
"language.variable.delete.confirm": "Tem a certeza que pretende eliminar a variável {key}?",
|
||||
"language.variable.entries": "Valores",
|
||||
"language.variable.entries.help": "Cada string será usada pela ordem correspondente à contagem. Isto é, três strings serão usadas, respetivamente, para as contagens <code>0</code>, <code>1</code> e <code>2 ou mais</code>. Utilize o marcador <code>{count}</code> para inserir o valor da contagem.",
|
||||
"language.variable.key": "Chave",
|
||||
"language.variable.multiple": "Contável?",
|
||||
"language.variable.multiple.text": "Utilize strings de tradução diferentes",
|
||||
"language.variable.multiple.help": "É possível utilizar valores diferentes consoante a contagem associada à variável de idioma, permitindo assim a criação de traduções dinâmicas, como, por exemplo, para formas singulares e plurais.",
|
||||
"language.variable.notFound": "A variável não foi encontrada",
|
||||
"language.variable.value": "Valor",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "Nenhuma tradução ainda",
|
||||
|
||||
"language.variable.delete.confirm": "Tem a certeza que pretende eliminar a variável {key}?",
|
||||
"language.variable.entries": "Valores",
|
||||
"language.variable.entries.help": "Cada string será utilizada pela ordem correspondente à contagem. Isto é, três strings serão utilizadas, respetivamente, para as contagens <code>0</code>, <code>1</code> e <code>2 ou mais</code>. Utilize o marcador <code>{count}</code> para inserir o valor real da contagem.",
|
||||
"language.variable.key": "Chave",
|
||||
"language.variable.multiple": "Contável?",
|
||||
"language.variable.multiple.text": "Utilize strings de tradução diferentes",
|
||||
"language.variable.multiple.help": "É possível utilizar valores diferentes consoante a contagem associada à variável de idioma, permitindo assim a criação de traduções dinâmicas. Isto é, para formas singulares e plurais.",
|
||||
"language.variable.notFound": "Não foi possível encontrar a variável",
|
||||
"language.variable.value": "Valor",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "Nicio traducere deocamdată",
|
||||
|
||||
"language.variable.delete.confirm": "Chiar vrei să ștergi variabila pentru {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Cheie",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "Variabila nu a fost găsită",
|
||||
"language.variable.value": "Valoare",
|
||||
|
||||
|
|
|
@ -449,7 +449,12 @@
|
|||
"language.variables.empty": "Пока нет переводов",
|
||||
|
||||
"language.variable.delete.confirm": "Вы действительно хотите удалить переменную для {key}?",
|
||||
"language.variable.entries": "Values",
|
||||
"language.variable.entries.help": "Each string will be used for its matching count, e.g. three strings will match in order to counts <code>0</code>, <code>1</code>, <code>2 and more</code>. Use the <code>{count}</code> placeholder to insert the actual count.",
|
||||
"language.variable.key": "Ключ",
|
||||
"language.variable.multiple": "Countable?",
|
||||
"language.variable.multiple.text": "Use different translation strings",
|
||||
"language.variable.multiple.help": "You can use different values depending on a count you pass along with the language variable, allowing you to create dynamic translations, e.g. singular and plural.",
|
||||
"language.variable.notFound": "Переменная не найдена",
|
||||
"language.variable.value": "Значение",
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue