Upgrade to 3.1.3
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "getkirby/cms",
|
||||
"description": "The Kirby 3 core",
|
||||
"version": "3.1.2",
|
||||
"version": "3.1.3",
|
||||
"license": "proprietary",
|
||||
"keywords": ["kirby", "cms", "core"],
|
||||
"homepage": "https://getkirby.com",
|
||||
|
@@ -8,6 +8,14 @@ return [
|
||||
'default' => function ($default = null) {
|
||||
return $default;
|
||||
},
|
||||
|
||||
/**
|
||||
* Defines a custom format that is used when the field is saved
|
||||
*/
|
||||
'format' => function (string $format = null) {
|
||||
return $format;
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the calendar icon to something custom
|
||||
*/
|
||||
|
@@ -4,6 +4,7 @@ use Kirby\Api\Api;
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Cms\Media;
|
||||
use Kirby\Cms\Panel;
|
||||
use Kirby\Cms\PanelPlugins;
|
||||
use Kirby\Cms\PluginAssets;
|
||||
use Kirby\Cms\Response;
|
||||
use Kirby\Exception\NotFoundException;
|
||||
@@ -16,6 +17,16 @@ use Kirby\Toolkit\View;
|
||||
return function ($kirby) {
|
||||
$api = $kirby->option('api.slug', 'api');
|
||||
$panel = $kirby->option('panel.slug', 'panel');
|
||||
$index = $kirby->url('index');
|
||||
$media = $kirby->url('media');
|
||||
|
||||
if (Str::startsWith($media, $index) === true) {
|
||||
$media = Str::after($media, $index);
|
||||
} else {
|
||||
// media URL is outside of the site, we can't make routing work;
|
||||
// fall back to the standard media route
|
||||
$media = 'media';
|
||||
}
|
||||
|
||||
/**
|
||||
* Before routes are running before the
|
||||
@@ -43,17 +54,31 @@ return function ($kirby) {
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'media/plugins/index.(css|js)',
|
||||
'pattern' => $media . '/panel/(:any)/plugins/(css|js)/(:any)/index.(css|js)',
|
||||
'env' => 'media',
|
||||
'action' => function (string $extension) use ($kirby) {
|
||||
'action' => function (string $version, string $type) use ($kirby) {
|
||||
$plugins = new PanelPlugins($type);
|
||||
$plugins->publish();
|
||||
|
||||
return $kirby
|
||||
->response()
|
||||
->type($extension)
|
||||
->body(PluginAssets::index($extension));
|
||||
->type($type)
|
||||
->body($plugins->read());
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'media/plugins/(:any)/(:any)/(:all).(css|gif|js|jpg|png|svg|webp|woff2|woff)',
|
||||
'pattern' => $media . '/panel/plugins/index.(css|js)',
|
||||
'env' => 'media',
|
||||
'action' => function (string $type) use ($kirby) {
|
||||
$plugins = new PanelPlugins($type);
|
||||
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($plugins->url(), 302);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => $media . '/plugins/(:any)/(:any)/(:all).(css|gif|js|jpg|png|svg|webp|woff2|woff)',
|
||||
'env' => 'media',
|
||||
'action' => function (string $provider, string $pluginName, string $filename, string $extension) use ($kirby) {
|
||||
return PluginAssets::resolve($provider . '/' . $pluginName, $filename . '.' . $extension);
|
||||
@@ -71,28 +96,28 @@ return function ($kirby) {
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'media/pages/(:all)/(:any)/(:any)',
|
||||
'pattern' => $media . '/pages/(:all)/(:any)/(:any)',
|
||||
'env' => 'media',
|
||||
'action' => function ($path, $hash, $filename) use ($kirby) {
|
||||
return Media::link($kirby->page($path), $hash, $filename);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'media/site/(:any)/(:any)',
|
||||
'pattern' => $media . '/site/(:any)/(:any)',
|
||||
'env' => 'media',
|
||||
'action' => function ($hash, $filename) use ($kirby) {
|
||||
return Media::link($kirby->site(), $hash, $filename);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'media/users/(:any)/(:any)/(:any)',
|
||||
'pattern' => $media . '/users/(:any)/(:any)/(:any)',
|
||||
'env' => 'media',
|
||||
'action' => function ($id, $hash, $filename) use ($kirby) {
|
||||
return Media::link($kirby->user($id), $hash, $filename);
|
||||
}
|
||||
],
|
||||
[
|
||||
'pattern' => 'media/assets/(:all)/(:any)/(:any)',
|
||||
'pattern' => $media . '/assets/(:all)/(:any)/(:any)',
|
||||
'env' => 'media',
|
||||
'action' => function ($path, $hash, $filename) use ($kirby) {
|
||||
return Media::thumb($path, $hash, $filename);
|
||||
|
@@ -14,7 +14,7 @@ return [
|
||||
* Sets the default page for the pagination. This will overwrite default pagination.
|
||||
*/
|
||||
'page' => function (int $page = null) {
|
||||
return $page ?? get('page', 1);
|
||||
return get('page', $page ?? 1);
|
||||
},
|
||||
],
|
||||
'methods' => [
|
||||
|
@@ -249,9 +249,7 @@ return [
|
||||
$templates = empty($this->create) === false ? $this->create : $this->templates;
|
||||
|
||||
if (empty($templates) === true) {
|
||||
foreach (glob(App::instance()->root('blueprints') . '/pages/*.yml') as $blueprint) {
|
||||
$templates[] = F::name($blueprint);
|
||||
}
|
||||
$templates = $this->kirby()->blueprints();
|
||||
}
|
||||
|
||||
// convert every template to a usable option array
|
||||
@@ -285,6 +283,8 @@ return [
|
||||
'headline' => $this->headline,
|
||||
'layout' => $this->layout,
|
||||
'link' => $this->link,
|
||||
'max' => $this->max,
|
||||
'min' => $this->min,
|
||||
'size' => $this->size,
|
||||
'sortable' => $this->sortable
|
||||
],
|
||||
|
2
kirby/panel/dist/css/app.css
vendored
2
kirby/panel/dist/css/app.css
vendored
File diff suppressed because one or more lines are too long
2
kirby/panel/dist/js/app.js
vendored
2
kirby/panel/dist/js/app.js
vendored
File diff suppressed because one or more lines are too long
10
kirby/panel/dist/js/vendor.js
vendored
10
kirby/panel/dist/js/vendor.js
vendored
File diff suppressed because one or more lines are too long
@@ -538,7 +538,7 @@ class Api
|
||||
'file' => ltrim($e->getFile(), $_SERVER['DOCUMENT_ROOT'] ?? null),
|
||||
'line' => $e->getLine(),
|
||||
'code' => empty($e->getCode()) === false ? $e->getCode() : 500,
|
||||
'route' => $this->route->pattern()
|
||||
'route' => $this->route ? $this->route->pattern() : null
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Kirby\Cache;
|
||||
|
||||
use APCUIterator;
|
||||
|
||||
/**
|
||||
* APCu Cache Driver
|
||||
*
|
||||
@@ -14,6 +16,53 @@ namespace Kirby\Cache;
|
||||
class ApcuCache extends Cache
|
||||
{
|
||||
|
||||
/**
|
||||
* Checks if the current key exists in cache
|
||||
*
|
||||
* @param string $key
|
||||
* @return boolean
|
||||
*/
|
||||
public function exists(string $key): bool
|
||||
{
|
||||
return apcu_exists($this->key($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the entire cache directory
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function flush(): bool
|
||||
{
|
||||
if (empty($this->options['prefix']) === false) {
|
||||
return apcu_delete(new APCUIterator('!^' . preg_quote($this->options['prefix']) . '!'));
|
||||
} else {
|
||||
return apcu_clear_cache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an item from the cache
|
||||
*
|
||||
* @param string $key
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove(string $key): bool
|
||||
{
|
||||
return apcu_delete($this->key($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an item from the cache.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function retrieve(string $key)
|
||||
{
|
||||
return Value::fromJson(apcu_fetch($this->key($key)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an item to the cache for a given number of minutes.
|
||||
*
|
||||
@@ -29,49 +78,6 @@ class ApcuCache extends Cache
|
||||
*/
|
||||
public function set(string $key, $value, int $minutes = 0)
|
||||
{
|
||||
return apcu_store($key, $this->value($value, $minutes)->toJson(), $this->expiration($minutes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an item from the cache.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function retrieve(string $key)
|
||||
{
|
||||
return Value::fromJson(apcu_fetch($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current key exists in cache
|
||||
*
|
||||
* @param string $key
|
||||
* @return boolean
|
||||
*/
|
||||
public function exists(string $key): bool
|
||||
{
|
||||
return apcu_exists($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an item from the cache
|
||||
*
|
||||
* @param string $key
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove(string $key): bool
|
||||
{
|
||||
return apcu_delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the entire cache directory
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function flush(): bool
|
||||
{
|
||||
return apcu_clear_cache();
|
||||
return apcu_store($this->key($key), $this->value($value, $minutes)->toJson(), $this->expiration($minutes));
|
||||
}
|
||||
}
|
||||
|
@@ -52,6 +52,21 @@ class Cache
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the prefix to the key if given
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function key(string $key): string
|
||||
{
|
||||
if (empty($this->options['prefix']) === false) {
|
||||
$key = $this->options['prefix'] . '/' . $key;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method to retrieve the cache value
|
||||
* This needs to be defined by the driver
|
||||
|
@@ -35,11 +35,6 @@ class FileCache extends Cache
|
||||
|
||||
// try to create the directory
|
||||
Dir::make($this->options['root'], true);
|
||||
|
||||
// check for a valid cache directory
|
||||
if (is_dir($this->options['root']) === false) {
|
||||
throw new Exception('The cache directory does not exist');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,7 +46,8 @@ class FileCache extends Cache
|
||||
protected function file(string $key): string
|
||||
{
|
||||
$extension = isset($this->options['extension']) ? '.' . $this->options['extension'] : '';
|
||||
return $this->options['root'] . '/' . $key . $extension;
|
||||
|
||||
return $this->options['root'] . '/' . $this->key($key) . $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,7 +113,13 @@ class FileCache extends Cache
|
||||
*/
|
||||
public function flush(): bool
|
||||
{
|
||||
if (Dir::remove($this->options['root']) === true && Dir::make($this->options['root']) === true) {
|
||||
$root = $this->options['root'];
|
||||
|
||||
if (empty($this->options['prefix']) === false) {
|
||||
$root = $root . '/' . $this->options['prefix'];
|
||||
}
|
||||
|
||||
if (Dir::remove($root) === true && Dir::make($root) === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -232,6 +232,33 @@ class App
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all available blueprints for this installation
|
||||
*
|
||||
* @param string $type
|
||||
* @return array
|
||||
*/
|
||||
public function blueprints(string $type = 'pages')
|
||||
{
|
||||
$blueprints = [];
|
||||
|
||||
foreach ($this->extensions('blueprints') as $name => $blueprint) {
|
||||
if (dirname($name) === $type) {
|
||||
$name = basename($name);
|
||||
$blueprints[$name] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (glob($this->root('blueprints') . '/' . $type . '/*.yml') as $blueprint) {
|
||||
$name = F::name($blueprint);
|
||||
$blueprints[$name] = $name;
|
||||
}
|
||||
|
||||
ksort($blueprints);
|
||||
|
||||
return array_values($blueprints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls any Kirby route
|
||||
*
|
||||
@@ -406,7 +433,7 @@ class App
|
||||
$visitor = $this->visitor();
|
||||
|
||||
foreach ($visitor->acceptedLanguages() as $lang) {
|
||||
if ($language = $languages->findBy('locale', $lang->locale())) {
|
||||
if ($language = $languages->findBy('locale', $lang->locale(LC_ALL))) {
|
||||
return $language;
|
||||
}
|
||||
}
|
||||
@@ -1156,9 +1183,11 @@ class App
|
||||
public function trigger(string $name, ...$arguments)
|
||||
{
|
||||
if ($functions = $this->extension('hooks', $name)) {
|
||||
static $level = 0;
|
||||
static $triggered = [];
|
||||
$level++;
|
||||
|
||||
foreach ($functions as $function) {
|
||||
foreach ($functions as $index => $function) {
|
||||
if (in_array($function, $triggered[$name] ?? []) === true) {
|
||||
continue;
|
||||
}
|
||||
@@ -1169,6 +1198,12 @@ class App
|
||||
// bind the App object to the hook
|
||||
$function->call($this, ...$arguments);
|
||||
}
|
||||
|
||||
$level--;
|
||||
|
||||
if ($level === 0) {
|
||||
$triggered = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1204,6 +1239,16 @@ class App
|
||||
return static::$version = static::$version ?? Data::read(static::$root . '/composer.json')['version'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hash of the version number
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function versionHash(): string
|
||||
{
|
||||
return md5(static::version());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the visitor object
|
||||
*
|
||||
|
@@ -82,8 +82,13 @@ class ContentTranslation
|
||||
*/
|
||||
public function content(): array
|
||||
{
|
||||
$parent = $this->parent();
|
||||
$content = $this->content ?? $parent->readContent($this->code());
|
||||
$parent = $this->parent();
|
||||
|
||||
if ($this->content === null) {
|
||||
$this->content = $parent->readContent($this->code());
|
||||
}
|
||||
|
||||
$content = $this->content;
|
||||
|
||||
// merge with the default content
|
||||
if ($this->isDefault() === false && $defaultLanguage = $parent->kirby()->defaultLanguage()) {
|
||||
|
@@ -21,7 +21,8 @@ class Form extends BaseForm
|
||||
|
||||
if ($kirby->multilang() === true) {
|
||||
$fields = $props['fields'] ?? [];
|
||||
$isDefaultLanguage = $kirby->language()->isDefault();
|
||||
$languageCode = $props['language'] ?? $kirby->language()->code();
|
||||
$isDefaultLanguage = $languageCode === $kirby->defaultLanguage()->code();
|
||||
|
||||
foreach ($fields as $fieldName => $fieldProps) {
|
||||
// switch untranslatable fields to readonly
|
||||
@@ -40,7 +41,7 @@ class Form extends BaseForm
|
||||
public static function for(Model $model, array $props = [])
|
||||
{
|
||||
// get the original model data
|
||||
$original = $model->content()->toArray();
|
||||
$original = $model->content($props['language'] ?? null)->toArray();
|
||||
$values = $props['values'] ?? [];
|
||||
|
||||
// convert closures to values
|
||||
|
@@ -46,7 +46,7 @@ class Language extends Model
|
||||
protected $direction;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var array
|
||||
*/
|
||||
protected $locale;
|
||||
|
||||
@@ -293,13 +293,18 @@ class Language extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the PHP locale setting string
|
||||
* Returns the PHP locale setting array
|
||||
*
|
||||
* @return string
|
||||
* @param int $category If passed, returns the locale for the specified category (e.g. LC_ALL) as string
|
||||
* @return array|string
|
||||
*/
|
||||
public function locale(): string
|
||||
public function locale(int $category = null)
|
||||
{
|
||||
return $this->locale;
|
||||
if ($category !== null) {
|
||||
return $this->locale[$category] ?? $this->locale[LC_ALL] ?? null;
|
||||
} else {
|
||||
return $this->locale;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -401,12 +406,21 @@ class Language extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $locale
|
||||
* @param string|array $locale
|
||||
* @return self
|
||||
*/
|
||||
protected function setLocale(string $locale = null): self
|
||||
protected function setLocale($locale = null): self
|
||||
{
|
||||
$this->locale = $locale ?? $this->code;
|
||||
if (is_array($locale)) {
|
||||
$this->locale = $locale;
|
||||
} elseif (is_string($locale)) {
|
||||
$this->locale = [LC_ALL => $locale];
|
||||
} elseif ($locale === null) {
|
||||
$this->locale = [LC_ALL => $this->code];
|
||||
} else {
|
||||
throw new InvalidArgumentException('Locale must be string or array');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@@ -67,7 +67,7 @@ class Languages extends Collection
|
||||
$files = glob(App::instance()->root('languages') . '/*.php');
|
||||
|
||||
foreach ($files as $file) {
|
||||
$props = include_once $file;
|
||||
$props = include $file;
|
||||
|
||||
if (is_array($props) === true) {
|
||||
|
||||
|
@@ -405,15 +405,16 @@ abstract class ModelWithContent extends Model
|
||||
* Updates the model data
|
||||
*
|
||||
* @param array $input
|
||||
* @param string $language
|
||||
* @param string $languageCode
|
||||
* @param boolean $validate
|
||||
* @return self
|
||||
*/
|
||||
public function update(array $input = null, string $languageCode = null, bool $validate = false)
|
||||
{
|
||||
$form = Form::for($this, [
|
||||
'input' => $input,
|
||||
'ignoreDisabled' => $validate === false,
|
||||
'input' => $input,
|
||||
'language' => $languageCode,
|
||||
]);
|
||||
|
||||
// validate the input
|
||||
|
@@ -30,7 +30,7 @@ class Panel
|
||||
{
|
||||
$mediaRoot = $kirby->root('media') . '/panel';
|
||||
$panelRoot = $kirby->root('panel') . '/dist';
|
||||
$versionHash = md5($kirby->version());
|
||||
$versionHash = $kirby->versionHash();
|
||||
$versionRoot = $mediaRoot . '/' . $versionHash;
|
||||
|
||||
// check if the version already exists
|
||||
@@ -72,12 +72,15 @@ class Panel
|
||||
// get the uri object for the panel url
|
||||
$uri = new Uri($url = $kirby->url('panel'));
|
||||
|
||||
$pluginCss = new PanelPlugins('css');
|
||||
$pluginJs = new PanelPlugins('js');
|
||||
|
||||
$view = new View($kirby->root('kirby') . '/views/panel.php', [
|
||||
'kirby' => $kirby,
|
||||
'config' => $kirby->option('panel'),
|
||||
'assetUrl' => $kirby->url('media') . '/panel/' . md5($kirby->version()),
|
||||
'pluginCss' => $kirby->url('media') . '/plugins/index.css',
|
||||
'pluginJs' => $kirby->url('media') . '/plugins/index.js',
|
||||
'assetUrl' => $kirby->url('media') . '/panel/' . $kirby->versionHash(),
|
||||
'pluginCss' => $pluginCss->url(),
|
||||
'pluginJs' => $pluginJs->url(),
|
||||
'icons' => F::read($kirby->root('panel') . '/dist/img/icons.svg'),
|
||||
'panelUrl' => $uri->path()->toString(true) . '/',
|
||||
'options' => [
|
||||
|
232
kirby/src/Cms/PanelPlugins.php
Executable file
232
kirby/src/Cms/PanelPlugins.php
Executable file
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
namespace Kirby\Cms;
|
||||
|
||||
use Kirby\Toolkit\Dir;
|
||||
use Kirby\Toolkit\F;
|
||||
|
||||
/**
|
||||
* The PanelPlugins class takes care of collecting
|
||||
* js and css plugin files for the panel and caches
|
||||
* them in the media folder
|
||||
*/
|
||||
class PanelPlugins
|
||||
{
|
||||
|
||||
/**
|
||||
* Cache of all collected plugin files
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $files;
|
||||
|
||||
/**
|
||||
* Cache of the unique plugin hash for the url and root
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $hash;
|
||||
|
||||
/**
|
||||
* css or js
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* Creates a new panel plugin instance by type (css or js)
|
||||
*
|
||||
* @param string $type
|
||||
*/
|
||||
public function __construct(string $type)
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects and returns the plugin files for all plugins
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function files(): array
|
||||
{
|
||||
if ($this->files !== null) {
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
$this->files = [];
|
||||
|
||||
foreach (App::instance()->plugins() as $plugin) {
|
||||
$file = $plugin->root() . '/index.' . $this->type;
|
||||
|
||||
if (file_exists($file) === true) {
|
||||
$this->files[] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the cache exists
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function exist(): bool
|
||||
{
|
||||
return file_exists($this->root());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the cache folder
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function folder(): string
|
||||
{
|
||||
return 'panel/' . App::versionHash() . '/plugins/' . $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects and removes garbage from old plugin versions
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function gc(): bool
|
||||
{
|
||||
$folder = App::instance()->root('media') . '/' . $this->folder();
|
||||
|
||||
foreach (glob($folder . '/*') as $dir) {
|
||||
$name = basename($dir);
|
||||
|
||||
if ($name !== $this->hash()) {
|
||||
Dir::remove($dir);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique hash for the cache file
|
||||
* The hash is generated from all plugin filenames
|
||||
* and the max modification date to make sure changes
|
||||
* will always be cached properly
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function hash(): string
|
||||
{
|
||||
if ($this->hash !== null) {
|
||||
return $this->hash;
|
||||
}
|
||||
|
||||
return $this->hash = $this->id() . '-' . $this->modified();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unique id based on all
|
||||
* plugin file roots
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return crc32(implode(array_values($this->files())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification
|
||||
* of the collected plugin files
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function modified(): int
|
||||
{
|
||||
$files = $this->files();
|
||||
$modified = [0];
|
||||
|
||||
foreach ($files as $file) {
|
||||
$modified[] = F::modified($file);
|
||||
}
|
||||
|
||||
return max($modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full path to the cache file
|
||||
* This is used for the root and url methods
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function path(): string
|
||||
{
|
||||
return $this->folder() . '/' . $this->hash() . '/index.' . $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the files from all plugins and concatenate them
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function read(): string
|
||||
{
|
||||
$dist = [];
|
||||
|
||||
foreach ($this->files() as $file) {
|
||||
$dist[] = file_get_contents($file);
|
||||
}
|
||||
|
||||
return implode(PHP_EOL, $dist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the cache exists and
|
||||
* otherwise (re)creates it
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function publish(): bool
|
||||
{
|
||||
if ($this->exist() === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->write();
|
||||
$this->gc();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute path to the cache file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function root(): string
|
||||
{
|
||||
return App::instance()->root('media') . '/' . $this->path();
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute url to the cache file
|
||||
* This is used by the panel to link the plugins
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function url(): string
|
||||
{
|
||||
return App::instance()->url('media') . '/' . $this->path();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the cache file
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function write(): bool
|
||||
{
|
||||
return F::write($this->root(), $this->read());
|
||||
}
|
||||
}
|
@@ -13,9 +13,10 @@ class Permissions
|
||||
{
|
||||
protected $actions = [
|
||||
'access' => [
|
||||
'panel' => true,
|
||||
'users' => true,
|
||||
'site' => true
|
||||
'panel' => true,
|
||||
'settings' => true,
|
||||
'site' => true,
|
||||
'users' => true,
|
||||
],
|
||||
'files' => [
|
||||
'changeName' => true,
|
||||
|
@@ -13,49 +13,6 @@ use Kirby\Toolkit\F;
|
||||
*/
|
||||
class PluginAssets
|
||||
{
|
||||
|
||||
/**
|
||||
* Concatenate all plugin js and css files into
|
||||
* a single file and copy them to /media/plugins/index.css or /media/plugins/index.js
|
||||
*
|
||||
* @param string $extension
|
||||
* @return string
|
||||
*/
|
||||
public static function index(string $extension): string
|
||||
{
|
||||
$kirby = App::instance();
|
||||
$cache = $kirby->root('media') . '/plugins/.index.' . $extension;
|
||||
$build = false;
|
||||
$modified = [0];
|
||||
$assets = [];
|
||||
|
||||
foreach ($kirby->plugins() as $plugin) {
|
||||
$file = $plugin->root() . '/index.' . $extension;
|
||||
|
||||
if (file_exists($file) === true) {
|
||||
$assets[] = $file;
|
||||
$modified[] = F::modified($file);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($assets)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file_exists($cache) === false || filemtime($cache) < max($modified)) {
|
||||
$dist = [];
|
||||
foreach ($assets as $asset) {
|
||||
$dist[] = file_get_contents($asset);
|
||||
}
|
||||
$dist = implode(PHP_EOL, $dist);
|
||||
F::write($cache, $dist);
|
||||
} else {
|
||||
$dist = file_get_contents($cache);
|
||||
}
|
||||
|
||||
return $dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean old/deprecated assets on every resolve
|
||||
*
|
||||
@@ -105,6 +62,9 @@ class PluginAssets
|
||||
$target = $plugin->mediaRoot() . '/' . $filename;
|
||||
$url = $plugin->mediaUrl() . '/' . $filename;
|
||||
|
||||
// create the plugin directory first
|
||||
Dir::make($plugin->mediaRoot(), true);
|
||||
|
||||
if (F::link($source, $target, 'symlink') === true) {
|
||||
return Response::redirect($url);
|
||||
}
|
||||
|
@@ -284,9 +284,7 @@ trait UserActions
|
||||
*/
|
||||
protected function writeCredentials(array $credentials): bool
|
||||
{
|
||||
$export = '<?php' . PHP_EOL . PHP_EOL . 'return ' . var_export($credentials, true) . ';';
|
||||
|
||||
return F::write($this->root() . '/index.php', $export);
|
||||
return Data::write($this->root() . '/index.php', $credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -82,6 +82,11 @@ class PHP extends Handler
|
||||
$php = static::encode($data);
|
||||
$php = "<?php\n\nreturn $php;";
|
||||
|
||||
return F::write($file, $php);
|
||||
if (F::write($file, $php) === true) {
|
||||
F::invalidateOpcodeCache($file);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -56,6 +56,8 @@ class Yaml extends Handler
|
||||
return $yaml;
|
||||
}
|
||||
|
||||
// remove bom
|
||||
$yaml = str_replace("\xEF\xBB\xBF", '', $yaml);
|
||||
$result = Spyc::YAMLLoadString($yaml);
|
||||
|
||||
if (is_array($result)) {
|
||||
|
@@ -352,7 +352,7 @@ class FileSessionStore extends SessionStore
|
||||
foreach ($iterator as $file) {
|
||||
// make sure that the file is a session file
|
||||
// prevents deleting files like .gitignore or other unrelated files
|
||||
if (preg_match('/[0-9]+\.[a-z0-9]+\.sess/', $file->getFilename()) !== 1) {
|
||||
if (preg_match('/^[0-9]+\.[a-z0-9]+\.sess$/', $file->getFilename()) !== 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -364,9 +364,6 @@ class FileSessionStore extends SessionStore
|
||||
if ($expiryTime < $currentTime) {
|
||||
// the session has expired, delete it
|
||||
$this->destroy($expiryTime, $id);
|
||||
} else {
|
||||
// the following files are all going to be still valid
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -57,8 +57,12 @@ class A
|
||||
* returned if no element has been found
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get(array $array, $key, $default = null)
|
||||
public static function get($array, $key, $default = null)
|
||||
{
|
||||
if (is_array($array) === false) {
|
||||
return $array;
|
||||
}
|
||||
|
||||
// return the entire array if the key is null
|
||||
if ($key === null) {
|
||||
return $array;
|
||||
@@ -79,17 +83,28 @@ class A
|
||||
|
||||
// support dot notation
|
||||
if (strpos($key, '.') !== false) {
|
||||
$keys = explode('.', $key);
|
||||
$keys = explode('.', $key);
|
||||
$firstKey = array_shift($keys);
|
||||
|
||||
foreach ($keys as $innerKey) {
|
||||
if (isset($array[$innerKey]) === false) {
|
||||
return $default;
|
||||
if (isset($array[$firstKey]) === false) {
|
||||
$currentKey = $firstKey;
|
||||
|
||||
while ($innerKey = array_shift($keys)) {
|
||||
$currentKey = $currentKey . '.' . $innerKey;
|
||||
|
||||
if (isset($array[$currentKey]) === true) {
|
||||
return static::get($array[$currentKey], implode('.', $keys), $default);
|
||||
}
|
||||
}
|
||||
|
||||
$array = $array[$innerKey];
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $array;
|
||||
if (is_array($array[$firstKey]) === true) {
|
||||
return static::get($array[$firstKey], implode('.', $keys), $default);
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $default;
|
||||
|
@@ -27,12 +27,7 @@ class Controller
|
||||
|
||||
foreach ($info->getParameters() as $parameter) {
|
||||
$name = $parameter->getName();
|
||||
|
||||
if (isset($data[$name]) === false) {
|
||||
throw new Exception(sprintf('The "%s" parameter is missing', $name));
|
||||
}
|
||||
|
||||
$args[] = $data[$name];
|
||||
$args[] = $data[$name] ?? null;
|
||||
}
|
||||
|
||||
return $args;
|
||||
|
@@ -264,6 +264,21 @@ class F
|
||||
return pathinfo($name, PATHINFO_BASENAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate opcode cache for file.
|
||||
*
|
||||
* @param string $file The path of the file
|
||||
* @return boolean
|
||||
*/
|
||||
public static function invalidateOpcodeCache(string $file): bool
|
||||
{
|
||||
if (function_exists('opcache_invalidate') && strlen(ini_get('opcache.restrict_api')) === 0) {
|
||||
return opcache_invalidate($file, true);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a file is of a certain type
|
||||
*
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Не можете да добавяте повече от един файл в секция \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Добавете поне {min} файла в секция \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Добавете поне един файл в секция \"{section}\"",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Не можете да добавяте повече от {max} страници в секция \"{section}\"",
|
||||
"error.section.pages.max.singular":
|
||||
"Не можете да добавяте повече от една страница в секция \"{section}\"",
|
||||
"error.section.pages.min.plural":
|
||||
"Добавете поне {min} страници в секция \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Добавете поне една страница в секция \"{section}\"",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "Секция \"{name}\" не може да бъде заредена",
|
||||
"error.section.type.invalid": "Типът \"{type}\" на секция не е валиден",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"No podeu afegir més d'un fitxer a la secció \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Afegeix com a mínim {min} fitxers a la secció \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Afegeix com a mínim un fitxer a la secció \"{section}\"",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"No heu d'afegir més de {max} pàgines a la secció \"{section}\"",
|
||||
"error.section.pages.max.singular":
|
||||
"No podeu afegir més d'una pàgina a la secció \"{section}\"",
|
||||
"error.section.pages.min.plural":
|
||||
"Afegiu com a mínim {min} pàgines a la secció \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Afegeix com a mínim una pàgina a la secció \"{section}\"",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "No s'ha pogut carregar la secció \"{name}\"",
|
||||
"error.section.type.invalid": "La secció tipus \"{type}\" no és vàlida",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Sekce \"{section}\" může obsahovat nejvýše jeden soubor",
|
||||
"error.section.files.min.plural":
|
||||
"Do sekce \"{section}\" přidejte alepoň {min} souborů",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Do sekce \"{section}\" přidejte alepoň jeden soubor",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Sekce \"{section}\" nesmí obsahovat více jak {max} stránek",
|
||||
"error.section.pages.max.singular":
|
||||
"Sekce \"{section}\" může obsahovat nejvýše jednu stránku",
|
||||
"error.section.pages.min.plural":
|
||||
"Do sekce \"{section}\" přidejte alepoň {min} stránek",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Do sekce \"{section}\" přidejte alespoň jednu stránku",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "Nelze načíst sekci \"{name}\"",
|
||||
"error.section.type.invalid": "Typ sekce \"{type}\" není platný",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Du kan ikke tilføje mere end en fil til \"{section}\" sektionen",
|
||||
"error.section.files.min.plural":
|
||||
"Tilføj mindst {min} filer til \"{section}\" sektionen",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Tilføj mindst en fil til \"{section}\" sektionen",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Du kan ikke tilføje flere end {max} sider til \"{section}\" sektionen",
|
||||
"error.section.pages.max.singular":
|
||||
"Du kan ikke tilføje mere end een side til \"{section}\" sektionen",
|
||||
"error.section.pages.min.plural":
|
||||
"Tilføj minimum {min} sider til \"{section}\" sektionen",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Tilføj minimum een side til \"{section}\" sektionen",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "Sektionen \"{section}\" kunne ikke indlæses",
|
||||
"error.section.type.invalid": "Sektionstypen \"{type}\" er ikke gyldig",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Bitte füge nicht mehr als eine Datei zum Bereich \"{section}\" hinzu",
|
||||
"error.section.files.min.plural":
|
||||
"Bitte füge mindestens {min} Dateien zum Bereich \"{section}\" hinzu",
|
||||
"Der Bereich \"{section}\" benötigt mindestens {min} Dateien",
|
||||
"error.section.files.min.singular":
|
||||
"Bitte füge mindestens eine Datei zum Bereich \"{section}\" hinzu",
|
||||
"Der Bereich \"{section}\" benötigt mindestens eine Datei",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Bitte füge nicht mehr als {max} Seiten zum Bereich \"{section}\" hinzu",
|
||||
"error.section.pages.max.singular":
|
||||
"Bitte füge nicht mehr als eine Seite zum Bereich \"{section}\" hinzu",
|
||||
"error.section.pages.min.plural":
|
||||
"Bitte füge mindestens {min} Seiten zum Bereich \"{section}\" hinzu",
|
||||
"Der Bereich \"{section}\" benötigt mindestens {min} Seiten",
|
||||
"error.section.pages.min.singular":
|
||||
"Bitte füge mindestens eine Seite zum Bereich \"{section}\" hinzu",
|
||||
"Der Bereich \"{section}\" benötigt mindestens eine Seite",
|
||||
|
||||
"error.section.notLoaded": "Der Bereich \"{name}\" konnte nicht geladen werden",
|
||||
"error.section.type.invalid": "Der Bereichstyp \"{type}\" ist nicht gültig",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Δεν πρέπει να προσθέσετε περισσότερα από ένα αρχεία στην ενότητα \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Προσθέστε τουλάχιστον {min} αρχεία στην ενότητα \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Προσθέστε τουλάχιστον ένα αρχείο στην ενότητα \"{section}\"",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Δεν μπορείτε να προσθέσετε περισσότερες από {max} σελίδες στην ενότητα \"{section}\"",
|
||||
"error.section.pages.max.singular":
|
||||
"Δεν μπορείτε να προσθέσετε περισσότερες από μία σελίδες στην ενότητα \"{section}\"",
|
||||
"error.section.pages.min.plural":
|
||||
"Προσθέστε τουλάχιστον {min} σελίδες στην ενότητα \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Προσθέστε τουλάχιστον μία σελίδα στην ενότητα \"{section}\"",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "Δεν ήταν δυνατή η φόρτωση της ενότητας \"{name}\"",
|
||||
"error.section.type.invalid": "Ο τύπος ενότητας \"{type}\" δεν είναι έγκυρος",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"You must not add more than one file to the \"{section}\" section",
|
||||
"error.section.files.min.plural":
|
||||
"Add at least {min} files to the \"{section}\" section",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Add at least one file to the \"{section}\" section",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"You must not add more than {max} pages to the \"{section}\" section",
|
||||
"error.section.pages.max.singular":
|
||||
"You must not add more than one page to the \"{section}\" section",
|
||||
"error.section.pages.min.plural":
|
||||
"Add at least {min} pages to the \"{section}\" section",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Add at least one page to the \"{section}\" section",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "The section \"{name}\" could not be loaded",
|
||||
"error.section.type.invalid": "The section type \"{type}\" is not valid",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"No debes agregar más de un archivo a la sección \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Agrega al menos {min} archivos a la sección \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Agrega al menos un archivo a la sección \"{section}\"",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"No debes agregar más de {max} páginas a la sección \"{section}\"",
|
||||
"error.section.pages.max.singular":
|
||||
"No debes agregar más de una página a la sección \"{section}\"",
|
||||
"error.section.pages.min.plural":
|
||||
"Agrega al menos {min} páginas a la sección \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Agrega al menos una página a la sección \"{section}\"",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "La sección \"{name}\" no se pudo cargar",
|
||||
"error.section.type.invalid": "La sección \"{type}\" no es valida",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"No debes agregar más de 1 archivo a la sección \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Agregue al menos {min} archivos a la sección \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Agregue al menos un archivo a la sección \"{section}\"",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"No debe agregar más de {max} páginas a la sección \"{section}\"",
|
||||
"error.section.pages.max.singular":
|
||||
"No debe agregar más de una página a la sección \"{section}\"",
|
||||
"error.section.pages.min.plural":
|
||||
"Agregue al menos {min} páginas a la sección \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Añadir al menos una página a la sección \"{section}\"",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "La sección \"{name}\" no pudo ser cargada",
|
||||
"error.section.type.invalid": "El sección tipo \"{tipo}\" no es válido",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"نباید بیش از یک فایل به بخش «{section}» اضافه کنید",
|
||||
"error.section.files.min.plural":
|
||||
"حداقل {min} فایل به بخش «{section}» اضافه کنید",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"حداقل یک فایل به بخش «{section}» اضافه کنید",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"نباید بیش از {max} صفحه به بخش «{section}» اضافه کنید",
|
||||
"error.section.pages.max.singular":
|
||||
"نباید بیش از یک صفحه به بخش «{section}» اضافه کنید",
|
||||
"error.section.pages.min.plural":
|
||||
"حداقل {min} صفحه به بخش «{section}» اضافه کنید",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"حداقل یک صفحه به بخش «{section}» اضافه کنید",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "بخش «{name}» قابل بارکذاری نیست",
|
||||
"error.section.type.invalid": "نوع بخش «{type}» غیرمجاز است",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Et voi lisätä enempää kuin yhden tiedoston osioon \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Lisää ainakin {min} tiedostoa osioon \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Lisää ainakin yksi tiedosto osioon \"{section}\"",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Et voi lisätä enemmän kuin {max} sivua osioon \"{section}\"",
|
||||
"error.section.pages.max.singular":
|
||||
"Et voi lisätä enempää kuin yhden sivun osioon \"{section}\"",
|
||||
"error.section.pages.min.plural":
|
||||
"Lisää ainakin {min} sivua osioon \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Lisää ainakin yksi sivu osioon \"{section}\"",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "Osiota \"{name}\" ei voitu ladata",
|
||||
"error.section.type.invalid": "Osion tyyppi \"{type}\" ei ole kelvollinen",
|
||||
@@ -366,8 +366,8 @@
|
||||
"toolbar.button.heading.3": "Otsikko 3",
|
||||
"toolbar.button.italic": "Kursivointi",
|
||||
"toolbar.button.file": "Tiedosto",
|
||||
"toolbar.button.file.select": "Select a file",
|
||||
"toolbar.button.file.upload": "Upload a file",
|
||||
"toolbar.button.file.select": "Valitse tiedosto",
|
||||
"toolbar.button.file.upload": "Lähetä tiedosto",
|
||||
"toolbar.button.link": "Linkki",
|
||||
"toolbar.button.ol": "Järjestetty lista",
|
||||
"toolbar.button.ul": "Järjestämätön lista",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Vous ne pouvez ajouter plus d’un fichier à la section « {section} »",
|
||||
"error.section.files.min.plural":
|
||||
"Ajoutez au moins {min} fichiers à la section « {section} »",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Ajoutez au moins un fichier à la section « {section} »",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Vous ne pouvez ajouter plus de {max} pages à la section « {section} »",
|
||||
"error.section.pages.max.singular":
|
||||
"Vous ne pouvez ajouter plus d’une page à la section « {section} »",
|
||||
"error.section.pages.min.plural":
|
||||
"Ajoutez au moins {min} pages à la section « {section} »",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Ajoutez au moins une page à la section « {section} »",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "La section « {name} » n’a pu être chargée",
|
||||
"error.section.type.invalid": "Le type de section « {type} » est incorrect",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Nem adhatsz hozzá egynél több fájlt a(z) \"{section}\" szekcióhoz",
|
||||
"error.section.files.min.plural":
|
||||
"Adj hozzá legalább {min} fájlt a(z) \"{section}\" szekcióhoz",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Adj hozzá legalább egy fájlt a(z) \"{section}\" szekcióhoz",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Maximum {max} oldalt adhatsz hozzá a(z) \"{section}\" szekcióhoz",
|
||||
"error.section.pages.max.singular":
|
||||
"Nem adhatsz hozzá egynél több oldalt a(z) \"{section}\" szekcióhoz",
|
||||
"error.section.pages.min.plural":
|
||||
"Adj hozzá legalább {min} oldalt a(z) \"{section}\" szekcióhoz",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Adj hozzá legalább egy oldalt a(z) \"{section}\" szekcióhoz",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "A(z) \"{name}\" szekció nem tölthető be",
|
||||
"error.section.type.invalid": "A szekció típusa (\"{type}\") nem megfelelő",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Anda hanya boleh menambahkan satu berkas ke bagian \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Tambahkan setidaknya {min} berkas ke bagian \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Tambahkan setidaknya satu berkas ke bagian \"{section}\"",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Anda hanya boleh menambahkan maksimal {max} halaman ke bagian \"{section}\"",
|
||||
"error.section.pages.max.singular":
|
||||
"Anda hanya boleh menambahkan satu halaman ke bagian \"{section}\"",
|
||||
"error.section.pages.min.plural":
|
||||
"Tambahkan setidaknya {min} halaman ke bagian \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Tambahkan setidaknya satu halaman ke bagian \"{section}\"",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "Bagian \"{name}\" tidak dapat dimuat",
|
||||
"error.section.type.invalid": "Tipe bagian \"{type}\" tidak valid",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Non puoi aggiungere più di un file alla sezione \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Aggiungi almeno {min} file alla sezione \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Aggiungi almeno un file alla sezione \"{section}\"",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Non puoi aggiungere più di {max} pagine alla sezione \"{section}\"",
|
||||
"error.section.pages.max.singular":
|
||||
"Non puoi aggiungere più di una pagina alla sezione \"{section}\"",
|
||||
"error.section.pages.min.plural":
|
||||
"Aggiungi almeno {min} pagine alla sezione \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Aggiungi almeno una pagina alla sezione \"{section}\"",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "Non è stato possibile caricare la sezione \"{name}\"",
|
||||
"error.section.type.invalid": "Il tipo di sezione \"{type}\" non è valido",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"이 섹션({section})에는 파일을 하나 이상 추가할 수 없습니다.",
|
||||
"error.section.files.min.plural":
|
||||
"이 섹션({section})에는 파일을 {min}개 이상 추가하세요.",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"이 섹션({section})에는 파일을 하나 이상 추가하세요.",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"이 섹션({section})에는 페이지를 {max}개 이상 추가할 수 없습니다.",
|
||||
"error.section.pages.max.singular":
|
||||
"이 섹션({section})에는 페이지를 하나 이상 추가할 수 없습니다.",
|
||||
"error.section.pages.min.plural":
|
||||
"이 섹션({section})에는 페이지를 {min}개 이상 추가하세요.",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"이 섹션({section})에는 페이지를 하나 이상 추가하세요.",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "섹션({name})을 불러올 수 없습니다.",
|
||||
"error.section.type.invalid": "섹션의 형식({type})이 올바르지 않습니다.",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Det er ikke mulig å legge til mer enn én fil i seksjonen \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Legg til minst {min} filer i \"{section}\" seksjonen",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Legg til minst én fil i \"{section}\" seksjonen",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Det er ikke mulig å legge til mer enn {max} sider i \"{section}\" seksjonen",
|
||||
"error.section.pages.max.singular":
|
||||
"Det er ikke mulig å legge til mer enn én side i \"{section}\" seksjonen",
|
||||
"error.section.pages.min.plural":
|
||||
"Legg til mist {min} sider i \"{section}\" seksjonen",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Legg til minst én side i \"{section}\" seksjonen",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "Seksjonen \"{name}\" kunne ikke lastes inn",
|
||||
"error.section.type.invalid": "Seksjonstypen \"{type}\" er ikke gyldig",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Je kunt niet meer dan 1 bestand toevoegen aan de zone \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Voeg minmaal {min} bestanden toe aan de zone \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Voeg minimaal 1 bestand toe aan de zone \"{section}\"",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Je kunt niet meer dan {max} pagina's toevoegen aan de zone \"{section}\"",
|
||||
"error.section.pages.max.singular":
|
||||
"Je kunt niet meer dan 1 pagina toevoegen aan de zone \"{section}\"",
|
||||
"error.section.pages.min.plural":
|
||||
"Voeg minimaal {min} pagina's toe aan de zone \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Voeg minimaal 1 pagina toe aan de zone \"{section}\"",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "De zone \"{name}\" kan niet worden geladen",
|
||||
"error.section.type.invalid": "Zone-type \"{type}\" is niet geldig",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Do sekcji \"{section}\" można dodać tylko jeden plik",
|
||||
"error.section.files.min.plural":
|
||||
"Dodaj co najmniej {min} pliki do sekcji \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Dodaj co najmniej jeden plik do sekcji \"{section}\"",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Do sekcji \"{section}\" można dodać nie więcej niż {max} stron",
|
||||
"error.section.pages.max.singular":
|
||||
"Do sekcji \"{section}\" można dodać tylko jedną stronę",
|
||||
"error.section.pages.min.plural":
|
||||
"Dodaj co najmniej {min} stron/-y do sekcji \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Dodaj co najmniej jedną stronę do sekcji \"{section}\"",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "Nie udało się załadować sekcji \"{name}\"",
|
||||
"error.section.type.invalid": "Typ sekcji \"{type}\" jest nieprawidłowy",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Você não pode adicionar mais do que um arquivo à seção \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Adicione pelo menos {min} arquivos à seção \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Adicione pelo menos um arquivo à seção \"{section}\"",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Você não pode adicionar mais do que {max} página à seção \"{section}\"",
|
||||
"error.section.pages.max.singular":
|
||||
"Você não pode adicionar mais do que uma página à seção \"{section}\"",
|
||||
"error.section.pages.min.plural":
|
||||
"Adicione pelo menos {min} páginas à seção \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Adicione pelo menos uma página à seção \"{section}\"",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "A seção \"{name}\" não pôde ser carregada",
|
||||
"error.section.type.invalid": "O tipo da seção \"{type}\" não é válido",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Não pode adicionar mais do que um arquivo à seção \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Adicione pelo menos {min} arquivos à seção \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Adicione pelo menos um arquivo à seção \"{section}\"",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Não pode adicionar mais do que {max} página à seção \"{section}\"",
|
||||
"error.section.pages.max.singular":
|
||||
"Não pode adicionar mais do que uma página à seção \"{section}\"",
|
||||
"error.section.pages.min.plural":
|
||||
"Adicione pelo menos {min} páginas à seção \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Adicione pelo menos uma página à seção \"{section}\"",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "A seção \"{name}\" não pôde ser carregada",
|
||||
"error.section.type.invalid": "O tipo da seção \"{type}\" não é válido",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Nemôžete pridať viac ako 1 súbor do sekcie \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Pridajte aspoň {min} súbory/ov do sekcie \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"Pridajte aspoň 1 súbor do sekcie \"{section}\"",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Nemôžete pridať viac ako {max} stránky/ok do sekcie \"{section}\"",
|
||||
"error.section.pages.max.singular":
|
||||
"Nemôžete pridať viac ako 1 stránku do sekcie \"{section}\"",
|
||||
"error.section.pages.min.plural":
|
||||
"Pridajte aspoň {min} stránky/ok do sekcie \"{section}\"",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"Pridajte aspoň 1 stránku do sekcie \"{section}\"",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "Sekciu \"{name}\" sa nepodarilo nahrať",
|
||||
"error.section.type.invalid": "Typ sekcie \"{type}\" nie je platný",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"Du får inte lägga till mer än en fil i sektionen \"{section}\"",
|
||||
"error.section.files.min.plural":
|
||||
"Lägg minst {min} filer till sektionen \"{section}\"",
|
||||
"Sektionen \"{section}\" kräver minst {min} filer",
|
||||
"error.section.files.min.singular":
|
||||
"Lägg minst en fil till sektionen \"{section}\"",
|
||||
"Sektionen \"{section}\" kräver minst en fil",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"Du får inte lägga till mer än {max} sidor till sektionen \"{section}\"",
|
||||
"error.section.pages.max.singular":
|
||||
"Du får inte lägga till mer än en sida i sektionen \"{section}\"",
|
||||
"error.section.pages.min.plural":
|
||||
"Lägg till minst {min} sidor till sektionen \"{section}\"",
|
||||
"Sektionen \"{section}\" kräver minst {min} sidor",
|
||||
"error.section.pages.min.singular":
|
||||
"Lägg till minst en sida till sektionen \"{section}\"",
|
||||
"Sektionen \"{section}\" kräver minst en sida",
|
||||
|
||||
"error.section.notLoaded": "Sektionen \"{name}\" kunde inte laddas",
|
||||
"error.section.type.invalid": "Sektionstypen \"{type}\" är inte giltig",
|
||||
@@ -366,8 +366,8 @@
|
||||
"toolbar.button.heading.3": "Rubrik 3",
|
||||
"toolbar.button.italic": "Kursiv",
|
||||
"toolbar.button.file": "Fil",
|
||||
"toolbar.button.file.select": "Select a file",
|
||||
"toolbar.button.file.upload": "Upload a file",
|
||||
"toolbar.button.file.select": "Välj en fil",
|
||||
"toolbar.button.file.upload": "Ladda upp en fil",
|
||||
"toolbar.button.link": "L\u00e4nk",
|
||||
"toolbar.button.ol": "Sorterad lista",
|
||||
"toolbar.button.ul": "Punktlista",
|
||||
|
@@ -104,18 +104,18 @@
|
||||
"error.section.files.max.singular":
|
||||
"\"{section}\" bölümüne birden fazla dosya eklememelisiniz",
|
||||
"error.section.files.min.plural":
|
||||
"\"{section}\" bölümüne en az {min} dosya ekleyin",
|
||||
"The \"{section}\" section requires at least {min} files",
|
||||
"error.section.files.min.singular":
|
||||
"\"{section}\" bölümüne en az bir dosya ekleyin",
|
||||
"The \"{section}\" section requires at least one file",
|
||||
|
||||
"error.section.pages.max.plural":
|
||||
"\"{section}\" bölümüne maksimum {max} sayfadan fazla ekleyemezsiniz",
|
||||
"error.section.pages.max.singular":
|
||||
"\"{section}\" bölümüne birden fazla sayfa ekleyemezsiniz",
|
||||
"error.section.pages.min.plural":
|
||||
"\"{section}\" bölümüne en az {min} sayfa ekleyin",
|
||||
"The \"{section}\" section requires at least {min} pages",
|
||||
"error.section.pages.min.singular":
|
||||
"\"{section}\" bölümüne en az bir sayfa ekleyin",
|
||||
"The \"{section}\" section requires at least one page",
|
||||
|
||||
"error.section.notLoaded": "\"{name}\" bölümü yüklenemedi",
|
||||
"error.section.type.invalid": "\"{type}\" tipi geçerli değil",
|
||||
|
Reference in New Issue
Block a user