Upgrade to 3.5.1
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Kirby\Cache;
|
||||
|
||||
use APCuIterator;
|
||||
use APCUIterator;
|
||||
|
||||
/**
|
||||
* APCu Cache Driver
|
||||
@@ -35,7 +35,7 @@ class ApcuCache extends Cache
|
||||
public function flush(): bool
|
||||
{
|
||||
if (empty($this->options['prefix']) === false) {
|
||||
return apcu_delete(new APCuIterator('!^' . preg_quote($this->options['prefix']) . '!'));
|
||||
return apcu_delete(new APCUIterator('!^' . preg_quote($this->options['prefix']) . '!'));
|
||||
} else {
|
||||
return apcu_clear_cache();
|
||||
}
|
||||
|
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Kirby\Cache;
|
||||
|
||||
use Memcached as MemcachedExt;
|
||||
|
||||
/**
|
||||
* Memcached Driver
|
||||
*
|
||||
@@ -15,7 +17,7 @@ class MemCached extends Cache
|
||||
{
|
||||
/**
|
||||
* store for the memache connection
|
||||
* @var Memcached
|
||||
* @var \Memcached
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
@@ -36,7 +38,7 @@ class MemCached extends Cache
|
||||
|
||||
parent::__construct(array_merge($defaults, $options));
|
||||
|
||||
$this->connection = new \Memcached();
|
||||
$this->connection = new MemcachedExt();
|
||||
$this->connection->addServer($this->options['host'], $this->options['port']);
|
||||
}
|
||||
|
||||
|
@@ -41,8 +41,11 @@ class Api extends BaseApi
|
||||
|
||||
$allowImpersonation = $this->kirby()->option('api.allowImpersonation', false);
|
||||
if ($user = $this->kirby->user(null, $allowImpersonation)) {
|
||||
$this->kirby->setCurrentTranslation($user->language());
|
||||
$translation = $user->language();
|
||||
} else {
|
||||
$translation = $this->kirby->panelLanguage();
|
||||
}
|
||||
$this->kirby->setCurrentTranslation($translation);
|
||||
|
||||
return parent::call($path, $method, $requestData);
|
||||
}
|
||||
|
@@ -852,7 +852,7 @@ class App
|
||||
*/
|
||||
public function markdown(string $text = null, bool $inline = false): string
|
||||
{
|
||||
return $this->component('markdown')($this, $text, $this->options['markdown'] ?? [], $inline);
|
||||
return ($this->component('markdown'))($this, $text, $this->options['markdown'] ?? [], $inline);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1101,6 +1101,7 @@ class App
|
||||
if ($page) {
|
||||
try {
|
||||
$response = $this->response();
|
||||
$output = $page->render([], $extension);
|
||||
|
||||
// attach a MIME type based on the representation
|
||||
// only if no custom MIME type was set
|
||||
@@ -1108,7 +1109,7 @@ class App
|
||||
$response->type($extension);
|
||||
}
|
||||
|
||||
return $response->body($page->render([], $extension));
|
||||
return $response->body($output);
|
||||
} catch (NotFoundException $e) {
|
||||
return null;
|
||||
}
|
||||
@@ -1373,7 +1374,7 @@ class App
|
||||
}
|
||||
}
|
||||
|
||||
return $this->component('smartypants')($this, $text, $options);
|
||||
return ($this->component('smartypants'))($this, $text, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1387,7 +1388,7 @@ class App
|
||||
*/
|
||||
public function snippet($name, array $data = []): ?string
|
||||
{
|
||||
return $this->component('snippet')($this, $name, array_merge($this->data, $data));
|
||||
return ($this->component('snippet'))($this, $name, array_merge($this->data, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1412,7 +1413,7 @@ class App
|
||||
*/
|
||||
public function template(string $name, string $type = 'html', string $defaultType = 'html')
|
||||
{
|
||||
return $this->component('template')($this, $name, $type, $defaultType);
|
||||
return ($this->component('template'))($this, $name, $type, $defaultType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1425,7 +1426,7 @@ class App
|
||||
*/
|
||||
public function thumb(string $src, string $dst, array $options = []): string
|
||||
{
|
||||
return $this->component('thumb')($this, $src, $dst, $options);
|
||||
return ($this->component('thumb'))($this, $src, $dst, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Kirby\Cms;
|
||||
|
||||
use Kirby\Cache\Cache;
|
||||
use Kirby\Cache\NullCache;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
|
||||
@@ -73,7 +72,7 @@ trait AppCaches
|
||||
*/
|
||||
protected function cacheOptions(string $key): array
|
||||
{
|
||||
$options = $this->option($cacheKey = $this->cacheOptionsKey($key), false);
|
||||
$options = $this->option($this->cacheOptionsKey($key), false);
|
||||
|
||||
if ($options === false) {
|
||||
return [
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Kirby\Cms;
|
||||
|
||||
use Kirby\Data\Data;
|
||||
use Kirby\Toolkit\I18n;
|
||||
use Kirby\Toolkit\Locale;
|
||||
use Kirby\Toolkit\Str;
|
||||
@@ -46,6 +45,7 @@ trait AppTranslations
|
||||
return $data;
|
||||
};
|
||||
|
||||
// the actual locale is set using $app->setCurrentTranslation()
|
||||
I18n::$locale = function (): string {
|
||||
if ($this->multilang() === true) {
|
||||
return $this->defaultLanguage()->code();
|
||||
@@ -54,11 +54,25 @@ trait AppTranslations
|
||||
}
|
||||
};
|
||||
|
||||
I18n::$fallback = function (): string {
|
||||
I18n::$fallback = function (): array {
|
||||
if ($this->multilang() === true) {
|
||||
return $this->defaultLanguage()->code();
|
||||
// first try to fall back to the configured default language
|
||||
$defaultCode = $this->defaultLanguage()->code();
|
||||
$fallback = [$defaultCode];
|
||||
|
||||
// if the default language is specified with a country code
|
||||
// (e.g. `en-us`), also try with just the language code
|
||||
if (preg_match('/^([a-z]{2})-[a-z]+$/i', $defaultCode, $matches) === 1) {
|
||||
$fallback[] = $matches[1];
|
||||
}
|
||||
|
||||
// fall back to the complete English translation
|
||||
// as a last resort
|
||||
$fallback[] = 'en';
|
||||
|
||||
return $fallback;
|
||||
} else {
|
||||
return 'en';
|
||||
return ['en'];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -74,6 +88,30 @@ trait AppTranslations
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language code that will be used
|
||||
* for the Panel if no user is logged in or if
|
||||
* no language is configured for the user
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function panelLanguage(): string
|
||||
{
|
||||
if ($this->multilang() === true) {
|
||||
$defaultCode = $this->defaultLanguage()->code();
|
||||
|
||||
// extract the language code from a language that
|
||||
// contains the country code (e.g. `en-us`)
|
||||
if (preg_match('/^([a-z]{2})-[a-z]+$/i', $defaultCode, $matches) === 1) {
|
||||
$defaultCode = $matches[1];
|
||||
}
|
||||
} else {
|
||||
$defaultCode = 'en';
|
||||
}
|
||||
|
||||
return $this->option('panel.language', $defaultCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and set the current language if it exists
|
||||
* Otherwise fall back to the default language
|
||||
@@ -132,10 +170,10 @@ trait AppTranslations
|
||||
/**
|
||||
* Load a specific translation by locale
|
||||
*
|
||||
* @param string|null $locale
|
||||
* @param string|null $locale Locale name or `null` for the current locale
|
||||
* @return \Kirby\Cms\Translation|null
|
||||
*/
|
||||
public function translation(string $locale = null)
|
||||
public function translation(?string $locale = null)
|
||||
{
|
||||
$locale = $locale ?? I18n::locale();
|
||||
$locale = basename($locale);
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Kirby\Cms;
|
||||
|
||||
use Kirby\Cms\Auth\Status;
|
||||
use Kirby\Data\Data;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Exception\LogicException;
|
||||
@@ -32,9 +33,41 @@ class Auth
|
||||
*/
|
||||
public static $challenges = [];
|
||||
|
||||
/**
|
||||
* Currently impersonated user
|
||||
*
|
||||
* @var \Kirby\Cms\User|null
|
||||
*/
|
||||
protected $impersonate;
|
||||
|
||||
/**
|
||||
* Kirby instance
|
||||
*
|
||||
* @var \Kirby\Cms\App
|
||||
*/
|
||||
protected $kirby;
|
||||
|
||||
/**
|
||||
* Cache of the auth status object
|
||||
*
|
||||
* @var \Kirby\Cms\Auth\Status
|
||||
*/
|
||||
protected $status;
|
||||
|
||||
/**
|
||||
* Instance of the currently logged in user or
|
||||
* `false` if the user was not yet determined
|
||||
*
|
||||
* @var \Kirby\Cms\User|null|false
|
||||
*/
|
||||
protected $user = false;
|
||||
|
||||
/**
|
||||
* Exception that was thrown while
|
||||
* determining the current user
|
||||
*
|
||||
* @var \Throwable
|
||||
*/
|
||||
protected $userException;
|
||||
|
||||
/**
|
||||
@@ -53,15 +86,13 @@ class Auth
|
||||
* @param string $email
|
||||
* @param bool $long If `true`, a long session will be created
|
||||
* @param string $mode Either 'login' or 'password-reset'
|
||||
* @return string|null Name of the challenge that was created;
|
||||
* `null` if the user does not exist or no
|
||||
* challenge was available for the user
|
||||
* @return \Kirby\Cms\Auth\Status
|
||||
*
|
||||
* @throws \Kirby\Exception\LogicException If there is no suitable authentication challenge (only in debug mode)
|
||||
* @throws \Kirby\Exception\NotFoundException If the user does not exist (only in debug mode)
|
||||
* @throws \Kirby\Exception\PermissionException If the rate limit is exceeded
|
||||
*/
|
||||
public function createChallenge(string $email, bool $long = false, string $mode = 'login'): ?string
|
||||
public function createChallenge(string $email, bool $long = false, string $mode = 'login')
|
||||
{
|
||||
// ensure that email addresses with IDN domains are in Unicode format
|
||||
$email = Idn::decodeEmail($email);
|
||||
@@ -91,8 +122,7 @@ class Auth
|
||||
if ($user = $this->kirby->users()->find($email)) {
|
||||
$timeout = $this->kirby->option('auth.challenge.timeout', 10 * 60);
|
||||
|
||||
$challenges = $this->kirby->option('auth.challenges', ['email']);
|
||||
foreach (A::wrap($challenges) as $name) {
|
||||
foreach ($this->enabledChallenges() as $name) {
|
||||
$class = static::$challenges[$name] ?? null;
|
||||
if (
|
||||
$class &&
|
||||
@@ -115,7 +145,7 @@ class Auth
|
||||
}
|
||||
|
||||
// if no suitable challenge was found, `$challenge === null` at this point;
|
||||
// only leak this in debug mode, otherwise `null` is returned below
|
||||
// only leak this in debug mode
|
||||
if ($challenge === null && $this->kirby->option('debug') === true) {
|
||||
throw new LogicException('Could not find a suitable authentication challenge');
|
||||
}
|
||||
@@ -142,7 +172,10 @@ class Auth
|
||||
// avoid leaking whether the user exists
|
||||
usleep(random_int(1000, 300000));
|
||||
|
||||
return $challenge;
|
||||
// clear the status cache
|
||||
$this->status = null;
|
||||
|
||||
return $this->status($session, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,15 +263,7 @@ class Auth
|
||||
*/
|
||||
public function currentUserFromSession($session = null)
|
||||
{
|
||||
// use passed session options or session object if set
|
||||
if (is_array($session) === true) {
|
||||
$session = $this->kirby->session($session);
|
||||
}
|
||||
|
||||
// try session in header or cookie
|
||||
if (is_a($session, 'Kirby\Session\Session') === false) {
|
||||
$session = $this->kirby->session(['detect' => true]);
|
||||
}
|
||||
$session = $this->session($session);
|
||||
|
||||
$id = $session->data()->get('kirby.userId');
|
||||
|
||||
@@ -256,6 +281,17 @@ class Auth
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of enabled challenges in the
|
||||
* configured order
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function enabledChallenges(): array
|
||||
{
|
||||
return A::wrap($this->kirby->option('auth.challenges', ['email']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Become any existing user or disable the current user
|
||||
*
|
||||
@@ -268,6 +304,9 @@ class Auth
|
||||
*/
|
||||
public function impersonate(?string $who = null)
|
||||
{
|
||||
// clear the status cache
|
||||
$this->status = null;
|
||||
|
||||
switch ($who) {
|
||||
case null:
|
||||
return $this->impersonate = null;
|
||||
@@ -359,6 +398,9 @@ class Auth
|
||||
$user = $this->validatePassword($email, $password);
|
||||
$user->loginPasswordless($options);
|
||||
|
||||
// clear the status cache
|
||||
$this->status = null;
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
@@ -368,8 +410,7 @@ class Auth
|
||||
* @param string $email
|
||||
* @param string $password
|
||||
* @param bool $long
|
||||
* @return string|null Name of the challenge that was created;
|
||||
* `null` if no challenge was available for the user
|
||||
* @return \Kirby\Cms\Auth\Status
|
||||
*
|
||||
* @throws \Kirby\Exception\PermissionException If the rate limit was exceeded or if any other error occured with debug mode off
|
||||
* @throws \Kirby\Exception\NotFoundException If the email was invalid
|
||||
@@ -394,6 +435,57 @@ class Auth
|
||||
$this->impersonate = null;
|
||||
|
||||
$this->user = $user;
|
||||
|
||||
// clear the status cache
|
||||
$this->status = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication status object
|
||||
*
|
||||
* @param \Kirby\Session\Session|array|null $session
|
||||
* @param bool $allowImpersonation If set to false, only the actually
|
||||
* logged in user will be returned
|
||||
* @return \Kirby\Cms\Auth\Status
|
||||
*/
|
||||
public function status($session = null, bool $allowImpersonation = true)
|
||||
{
|
||||
// try to return from cache
|
||||
if ($this->status && $session === null && $allowImpersonation === true) {
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
$sessionObj = $this->session($session);
|
||||
|
||||
$props = ['kirby' => $this->kirby];
|
||||
if ($user = $this->user($sessionObj, $allowImpersonation)) {
|
||||
// a user is currently logged in
|
||||
if ($allowImpersonation === true && $this->impersonate !== null) {
|
||||
$props['status'] = 'impersonated';
|
||||
} else {
|
||||
$props['status'] = 'active';
|
||||
}
|
||||
|
||||
$props['email'] = $user->email();
|
||||
} elseif ($email = $sessionObj->get('kirby.challenge.email')) {
|
||||
// a challenge is currently pending
|
||||
$props['status'] = 'pending';
|
||||
$props['email'] = $email;
|
||||
$props['challenge'] = $sessionObj->get('kirby.challenge.type');
|
||||
$props['challengeFallback'] = A::last($this->enabledChallenges());
|
||||
} else {
|
||||
// no active authentication
|
||||
$props['status'] = 'inactive';
|
||||
}
|
||||
|
||||
$status = new Status($props);
|
||||
|
||||
// only cache the default object
|
||||
if ($session === null && $allowImpersonation === true) {
|
||||
$this->status = $status;
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -534,6 +626,9 @@ class Auth
|
||||
$session->remove('kirby.challenge.email');
|
||||
$session->remove('kirby.challenge.timeout');
|
||||
$session->remove('kirby.challenge.type');
|
||||
|
||||
// clear the status cache
|
||||
$this->status = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -545,6 +640,7 @@ class Auth
|
||||
public function flush(): void
|
||||
{
|
||||
$this->impersonate = null;
|
||||
$this->status = null;
|
||||
$this->user = null;
|
||||
}
|
||||
|
||||
@@ -718,6 +814,9 @@ class Auth
|
||||
$this->logout();
|
||||
$user->loginPasswordless();
|
||||
|
||||
// clear the status cache
|
||||
$this->status = null;
|
||||
|
||||
return $user;
|
||||
} else {
|
||||
throw new PermissionException(['key' => 'access.code']);
|
||||
@@ -744,4 +843,25 @@ class Auth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a session object from the passed options
|
||||
*
|
||||
* @param \Kirby\Session\Session|array|null $session
|
||||
* @return \Kirby\Session\Session
|
||||
*/
|
||||
protected function session($session = null)
|
||||
{
|
||||
// use passed session options or session object if set
|
||||
if (is_array($session) === true) {
|
||||
return $this->kirby->session($session);
|
||||
}
|
||||
|
||||
// try session in header or cookie
|
||||
if (is_a($session, 'Kirby\Session\Session') === false) {
|
||||
return $this->kirby->session(['detect' => true]);
|
||||
}
|
||||
|
||||
return $session;
|
||||
}
|
||||
}
|
||||
|
218
kirby/src/Cms/Auth/Status.php
Executable file
218
kirby/src/Cms/Auth/Status.php
Executable file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
|
||||
namespace Kirby\Cms\Auth;
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Toolkit\Properties;
|
||||
|
||||
/**
|
||||
* Information container for the
|
||||
* authentication status
|
||||
*
|
||||
* @package Kirby Cms
|
||||
* @author Lukas Bestle <lukas@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier GmbH
|
||||
* @license https://getkirby.com/license
|
||||
*/
|
||||
class Status
|
||||
{
|
||||
use Properties;
|
||||
|
||||
/**
|
||||
* Type of the active challenge
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $challenge = null;
|
||||
|
||||
/**
|
||||
* Challenge type to use as a fallback
|
||||
* when $challenge is `null`
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $challengeFallback = null;
|
||||
|
||||
/**
|
||||
* Email address of the current/pending user
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $email = null;
|
||||
|
||||
/**
|
||||
* Kirby instance for user lookup
|
||||
*
|
||||
* @var \Kirby\Cms\App
|
||||
*/
|
||||
protected $kirby;
|
||||
|
||||
/**
|
||||
* Authentication status:
|
||||
* `active|impersonated|pending|inactive`
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $status;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(array $props)
|
||||
{
|
||||
$this->setProperties($props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication status
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->status();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the active challenge
|
||||
*
|
||||
* @param bool $automaticFallback If set to `false`, no faked challenge is returned;
|
||||
* WARNING: never send the resulting `null` value to the
|
||||
* user to avoid leaking whether the pending user exists
|
||||
* @return string|null
|
||||
*/
|
||||
public function challenge(bool $automaticFallback = true): ?string
|
||||
{
|
||||
// never return a challenge type if the status doesn't match
|
||||
if ($this->status() !== 'pending') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($automaticFallback === false) {
|
||||
return $this->challenge;
|
||||
} else {
|
||||
return $this->challenge ?? $this->challengeFallback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the email address of the current/pending user
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function email(): ?string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication status
|
||||
*
|
||||
* @return string `active|impersonated|pending|inactive`
|
||||
*/
|
||||
public function status(): string
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all public status data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'challenge' => $this->challenge(),
|
||||
'email' => $this->email(),
|
||||
'status' => $this->status()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently logged in user
|
||||
*
|
||||
* @return \Kirby\Cms\User
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
// for security, only return the user if they are
|
||||
// already logged in
|
||||
if (in_array($this->status(), ['active', 'impersonated']) !== true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->kirby->user($this->email());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of the active challenge
|
||||
*
|
||||
* @param string|null $challenge
|
||||
* @return self
|
||||
*/
|
||||
protected function setChallenge(?string $challenge = null)
|
||||
{
|
||||
$this->challenge = $challenge;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the challenge type to use as
|
||||
* a fallback when $challenge is `null`
|
||||
*
|
||||
* @param string|null $challengeFallback
|
||||
* @return self
|
||||
*/
|
||||
protected function setChallengeFallback(?string $challengeFallback = null)
|
||||
{
|
||||
$this->challengeFallback = $challengeFallback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the email address of the current/pending user
|
||||
*
|
||||
* @param string|null $email
|
||||
* @return self
|
||||
*/
|
||||
protected function setEmail(?string $email = null)
|
||||
{
|
||||
$this->email = $email;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Kirby instance for user lookup
|
||||
*
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @return self
|
||||
*/
|
||||
protected function setKirby(App $kirby)
|
||||
{
|
||||
$this->kirby = $kirby;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication status
|
||||
*
|
||||
* @param string $status `active|impersonated|pending|inactive`
|
||||
* @return self
|
||||
*/
|
||||
protected function setStatus(string $status)
|
||||
{
|
||||
if (in_array($status, ['active', 'impersonated', 'pending', 'inactive']) !== true) {
|
||||
throw new InvalidArgumentException([
|
||||
'data' => ['argument' => '$props[\'status\']', 'method' => 'Status::__construct']
|
||||
]);
|
||||
}
|
||||
|
||||
$this->status = $status;
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -194,7 +194,7 @@ class Block extends Item
|
||||
* object. This can be used further
|
||||
* with all available field methods
|
||||
*
|
||||
* @return \Kirby\Cms\Field;
|
||||
* @return \Kirby\Cms\Field
|
||||
*/
|
||||
public function toField()
|
||||
{
|
||||
|
@@ -74,7 +74,10 @@ class BlockConverter
|
||||
'type' => 'list'
|
||||
];
|
||||
|
||||
for ($x = $listStart+1; $x <= $listStart + count($list); $x++) {
|
||||
$start = $listStart + 1;
|
||||
$end = $listStart + count($list);
|
||||
|
||||
for ($x = $start; $x <= $end; $x++) {
|
||||
$blocks[$x] = false;
|
||||
}
|
||||
|
||||
|
@@ -56,12 +56,12 @@ class Blocks extends Items
|
||||
* @param array $params
|
||||
* @return \Kirby\Cms\Blocks
|
||||
*/
|
||||
public static function factory(array $blocks = null, array $params = [])
|
||||
public static function factory(array $items = null, array $params = [])
|
||||
{
|
||||
$blocks = static::extractFromLayouts($blocks);
|
||||
$blocks = BlockConverter::editorBlocks($blocks);
|
||||
$items = static::extractFromLayouts($items);
|
||||
$items = BlockConverter::editorBlocks($items);
|
||||
|
||||
return parent::factory($blocks, $params);
|
||||
return parent::factory($items, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -86,7 +86,7 @@ class Collection extends BaseCollection
|
||||
*/
|
||||
public function add($object)
|
||||
{
|
||||
if (is_a($object, static::class) === true) {
|
||||
if (is_a($object, self::class) === true) {
|
||||
$this->data = array_merge($this->data, $object->data);
|
||||
} elseif (is_object($object) === true && method_exists($object, 'id') === true) {
|
||||
$this->__set($object->id(), $object);
|
||||
|
@@ -50,7 +50,6 @@ class Fieldset extends Item
|
||||
$this->disabled = $params['disabled'] ?? false;
|
||||
$this->icon = $params['icon'] ?? null;
|
||||
$this->model = $this->parent;
|
||||
$this->kirby = $this->parent->kirby();
|
||||
$this->name = $this->createName($params['name'] ?? Str::ucfirst($this->type));
|
||||
$this->label = $this->createLabel($params['label'] ?? null);
|
||||
$this->preview = $params['preview'] ?? null;
|
||||
@@ -61,8 +60,8 @@ class Fieldset extends Item
|
||||
|
||||
if (
|
||||
$this->translate === false &&
|
||||
$this->kirby->multilang() === true &&
|
||||
$this->kirby->language()->isDefault() === false
|
||||
$this->kirby()->multilang() === true &&
|
||||
$this->kirby()->language()->isDefault() === false
|
||||
) {
|
||||
// disable and unset the fieldset if it's not translatable
|
||||
$this->unset = true;
|
||||
|
@@ -66,9 +66,9 @@ class Fieldsets extends Items
|
||||
];
|
||||
}
|
||||
|
||||
public static function factory(array $fieldsets = null, array $options = [])
|
||||
public static function factory(array $items = null, array $params = [])
|
||||
{
|
||||
$fieldsets = $fieldsets ?? option('blocks.fieldsets', [
|
||||
$items = $items ?? option('blocks.fieldsets', [
|
||||
'code' => 'blocks/code',
|
||||
'gallery' => 'blocks/gallery',
|
||||
'heading' => 'blocks/heading',
|
||||
@@ -80,9 +80,9 @@ class Fieldsets extends Items
|
||||
'video' => 'blocks/video',
|
||||
]);
|
||||
|
||||
$result = static::createFieldsets($fieldsets);
|
||||
$result = static::createFieldsets($items);
|
||||
|
||||
return parent::factory($result['fieldsets'], ['groups' => $result['groups']] + $options);
|
||||
return parent::factory($result['fieldsets'], ['groups' => $result['groups']] + $params);
|
||||
}
|
||||
|
||||
public function groups(): array
|
||||
|
@@ -5,6 +5,7 @@ namespace Kirby\Cms;
|
||||
use Kirby\Image\Image;
|
||||
use Kirby\Toolkit\A;
|
||||
use Kirby\Toolkit\F;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* The `$file` object provides a set
|
||||
@@ -504,6 +505,31 @@ class File extends ModelWithContent
|
||||
return parent::panelImageSource($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all actions
|
||||
* that can be performed in the Panel
|
||||
*
|
||||
* @since 3.3.0 This also checks for the lock status
|
||||
* @since 3.5.1 This also checks for matching accept settings
|
||||
*
|
||||
* @param array $unlock An array of options that will be force-unlocked
|
||||
* @return array
|
||||
*/
|
||||
public function panelOptions(array $unlock = []): array
|
||||
{
|
||||
$options = parent::panelOptions($unlock);
|
||||
|
||||
try {
|
||||
// check if the file type is allowed at all,
|
||||
// otherwise it cannot be replaced
|
||||
$this->match($this->blueprint()->accept());
|
||||
} catch (Throwable $e) {
|
||||
$options['replace'] = false;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full path without leading slash
|
||||
*
|
||||
@@ -767,6 +793,6 @@ class File extends ModelWithContent
|
||||
*/
|
||||
public function url(): string
|
||||
{
|
||||
return $this->url ?? $this->url = $this->kirby()->component('file::url')($this->kirby(), $this);
|
||||
return $this->url ?? $this->url = ($this->kirby()->component('file::url'))($this->kirby(), $this);
|
||||
}
|
||||
}
|
||||
|
@@ -191,7 +191,7 @@ trait FileModifications
|
||||
return $this;
|
||||
}
|
||||
|
||||
$result = $this->kirby()->component('file::version')($this->kirby(), $this, $options);
|
||||
$result = ($this->kirby()->component('file::version'))($this->kirby(), $this, $options);
|
||||
|
||||
if (is_a($result, 'Kirby\Cms\FileVersion') === false && is_a($result, 'Kirby\Cms\File') === false) {
|
||||
throw new InvalidArgumentException('The file::version component must return a File or FileVersion object');
|
||||
|
@@ -36,7 +36,7 @@ class Files extends Collection
|
||||
public function add($object)
|
||||
{
|
||||
// add a page collection
|
||||
if (is_a($object, static::class) === true) {
|
||||
if (is_a($object, self::class) === true) {
|
||||
$this->data = array_merge($this->data, $object->data);
|
||||
|
||||
// add a file by id
|
||||
|
@@ -33,6 +33,11 @@ class Item
|
||||
*/
|
||||
protected $params;
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\Page|\Kirby\Cms\Site|\Kirby\Cms\File|\Kirby\Cms\User
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\Items
|
||||
*/
|
||||
@@ -98,7 +103,7 @@ class Item
|
||||
/**
|
||||
* Returns the parent model
|
||||
*
|
||||
* @return \Kirby\Cms\Page | \Kirby\Cms\Site | \Kirby\Cms\File | \Kirby\Cms\User
|
||||
* @return \Kirby\Cms\Page|\Kirby\Cms\Site|\Kirby\Cms\File|\Kirby\Cms\User
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
@@ -109,7 +114,8 @@ class Item
|
||||
* Returns the sibling collection
|
||||
* This is required by the HasSiblings trait
|
||||
*
|
||||
* @return \Kirby\Editor\Blocks
|
||||
* @return \Kirby\Cms\Items
|
||||
* @psalm-return self::ITEMS_CLASS
|
||||
*/
|
||||
protected function siblingsCollection()
|
||||
{
|
||||
|
@@ -40,6 +40,7 @@ class LanguageRoutes
|
||||
|
||||
// jump through to the fallback if nothing
|
||||
// can be found for this language
|
||||
/** @var \Kirby\Http\Route $this */
|
||||
$this->next();
|
||||
}
|
||||
];
|
||||
|
@@ -15,25 +15,25 @@ class Layouts extends Items
|
||||
{
|
||||
const ITEM_CLASS = '\Kirby\Cms\Layout';
|
||||
|
||||
public static function factory(array $layouts = null, array $options = [])
|
||||
public static function factory(array $items = null, array $params = [])
|
||||
{
|
||||
$first = $layouts[0] ?? [];
|
||||
$first = $items[0] ?? [];
|
||||
|
||||
// if there are no wrapping layouts for blocks yet …
|
||||
if (array_key_exists('content', $first) === true || array_key_exists('type', $first) === true) {
|
||||
$layouts = [
|
||||
$items = [
|
||||
[
|
||||
'id' => uuid(),
|
||||
'columns' => [
|
||||
[
|
||||
'width' => '1/1',
|
||||
'blocks' => $layouts
|
||||
'blocks' => $items
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return parent::factory($layouts, $options);
|
||||
return parent::factory($items, $params);
|
||||
}
|
||||
}
|
||||
|
@@ -837,17 +837,17 @@ trait PageActions
|
||||
* Updates the page data
|
||||
*
|
||||
* @param array|null $input
|
||||
* @param string|null $language
|
||||
* @param string|null $languageCode
|
||||
* @param bool $validate
|
||||
* @return self
|
||||
*/
|
||||
public function update(array $input = null, string $language = null, bool $validate = false)
|
||||
public function update(array $input = null, string $languageCode = null, bool $validate = false)
|
||||
{
|
||||
if ($this->isDraft() === true) {
|
||||
$validate = false;
|
||||
}
|
||||
|
||||
$page = parent::update($input, $language, $validate);
|
||||
$page = parent::update($input, $languageCode, $validate);
|
||||
|
||||
// if num is created from page content, update num on content update
|
||||
if ($page->isListed() === true && in_array($page->blueprint()->num(), ['zero', 'default']) === false) {
|
||||
|
@@ -313,14 +313,14 @@ class PageRules
|
||||
$drafts = $page->parentModel()->drafts();
|
||||
$slug = $page->slug();
|
||||
|
||||
if ($duplicate = $siblings->find($slug)) {
|
||||
if ($siblings->find($slug)) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'page.duplicate',
|
||||
'data' => ['slug' => $slug]
|
||||
]);
|
||||
}
|
||||
|
||||
if ($duplicate = $drafts->find($slug)) {
|
||||
if ($drafts->find($slug)) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'page.draft.duplicate',
|
||||
'data' => ['slug' => $slug]
|
||||
|
@@ -48,7 +48,7 @@ class Pages extends Collection
|
||||
public function add($object)
|
||||
{
|
||||
// add a page collection
|
||||
if (is_a($object, static::class) === true) {
|
||||
if (is_a($object, self::class) === true) {
|
||||
$this->data = array_merge($this->data, $object->data);
|
||||
|
||||
// add a page by id
|
||||
@@ -398,7 +398,7 @@ class Pages extends Collection
|
||||
}
|
||||
|
||||
// merge an entire collection
|
||||
if (is_a($args[0], static::class) === true) {
|
||||
if (is_a($args[0], self::class) === true) {
|
||||
$collection = clone $this;
|
||||
$collection->data = array_merge($collection->data, $args[0]->data);
|
||||
return $collection;
|
||||
|
@@ -28,7 +28,7 @@ class Panel
|
||||
* Returns custom css path for panel ui
|
||||
*
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @return bool|string
|
||||
* @return string|false
|
||||
*/
|
||||
public static function customCss(App $kirby)
|
||||
{
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Kirby\Cms;
|
||||
|
||||
use Kirby\Session\Session;
|
||||
use Kirby\Toolkit\Facade;
|
||||
|
||||
/**
|
||||
|
@@ -37,7 +37,7 @@ class Search
|
||||
public static function collection(Collection $collection, string $query = null, $params = [])
|
||||
{
|
||||
$kirby = App::instance();
|
||||
return $kirby->component('search')($kirby, $collection, $query, $params);
|
||||
return ($kirby->component('search'))($kirby, $collection, $query, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -44,6 +44,10 @@ class Section extends Component
|
||||
throw new InvalidArgumentException('Undefined section model');
|
||||
}
|
||||
|
||||
if (is_a($attrs['model'], 'Kirby\Cms\Model') === false) {
|
||||
throw new InvalidArgumentException('Invalid section model');
|
||||
}
|
||||
|
||||
// use the type as fallback for the name
|
||||
$attrs['name'] = $attrs['name'] ?? $type;
|
||||
$attrs['type'] = $type;
|
||||
@@ -65,7 +69,7 @@ class Section extends Component
|
||||
*/
|
||||
public function kirby()
|
||||
{
|
||||
return $this->model->kirby();
|
||||
return $this->model()->kirby();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -62,8 +62,8 @@ class Url extends BaseUrl
|
||||
{
|
||||
$kirby = App::instance();
|
||||
|
||||
return $kirby->component('url')($kirby, $path, $options, function (string $path = null, $options = null) use ($kirby) {
|
||||
return $kirby->nativeComponent('url')($kirby, $path, $options);
|
||||
return ($kirby->component('url'))($kirby, $path, $options, function (string $path = null, $options = null) use ($kirby) {
|
||||
return ($kirby->nativeComponent('url'))($kirby, $path, $options);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -400,7 +400,7 @@ class User extends ModelWithContent
|
||||
*/
|
||||
public function language(): string
|
||||
{
|
||||
return $this->language ?? $this->language = $this->credentials()['language'] ?? $this->kirby()->option('panel.language', 'en');
|
||||
return $this->language ?? $this->language = $this->credentials()['language'] ?? $this->kirby()->panelLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -302,13 +302,13 @@ trait UserActions
|
||||
* Updates the user data
|
||||
*
|
||||
* @param array|null $input
|
||||
* @param string|null $language
|
||||
* @param string|null $languageCode
|
||||
* @param bool $validate
|
||||
* @return self
|
||||
*/
|
||||
public function update(array $input = null, string $language = null, bool $validate = false)
|
||||
public function update(array $input = null, string $languageCode = null, bool $validate = false)
|
||||
{
|
||||
$user = parent::update($input, $language, $validate);
|
||||
$user = parent::update($input, $languageCode, $validate);
|
||||
|
||||
// set auth user data only if the current user is this user
|
||||
if ($user->isLoggedIn() === true) {
|
||||
|
@@ -43,7 +43,7 @@ class Users extends Collection
|
||||
public function add($object)
|
||||
{
|
||||
// add a page collection
|
||||
if (is_a($object, static::class) === true) {
|
||||
if (is_a($object, self::class) === true) {
|
||||
$this->data = array_merge($this->data, $object->data);
|
||||
|
||||
// add a user by id
|
||||
|
@@ -38,7 +38,7 @@ class PHP extends Handler
|
||||
return "[\n" . implode(",\n", $array) . "\n" . $indent . ']';
|
||||
case 'boolean':
|
||||
return $data ? 'true' : 'false';
|
||||
case 'int':
|
||||
case 'integer':
|
||||
case 'double':
|
||||
return $data;
|
||||
default:
|
||||
|
@@ -3,7 +3,6 @@
|
||||
namespace Kirby\Database;
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Toolkit\A;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
@@ -73,6 +73,24 @@ class PHPMailer extends Email
|
||||
$mailer->Password = $this->transport()['password'] ?? null;
|
||||
$mailer->SMTPSecure = $this->transport()['security'] ?? 'ssl';
|
||||
$mailer->Port = $this->transport()['port'] ?? null;
|
||||
|
||||
if ($mailer->SMTPSecure === true) {
|
||||
switch ($mailer->Port) {
|
||||
case null:
|
||||
case 587:
|
||||
$mailer->SMTPSecure = 'tls';
|
||||
$mailer->Port = 587;
|
||||
break;
|
||||
case 465:
|
||||
$mailer->SMTPSecure = 'ssl';
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException(
|
||||
'Could not automatically detect the "security" protocol from the ' .
|
||||
'"port" option, please set it explicitly to "tls" or "ssl".'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// accessible phpMailer instance
|
||||
|
@@ -84,7 +84,10 @@ class Field extends Component
|
||||
*/
|
||||
public function api()
|
||||
{
|
||||
if (isset($this->options['api']) === true && is_callable($this->options['api']) === true) {
|
||||
if (
|
||||
isset($this->options['api']) === true &&
|
||||
is_a($this->options['api'], 'Closure') === true
|
||||
) {
|
||||
return $this->options['api']->call($this);
|
||||
}
|
||||
}
|
||||
@@ -107,11 +110,13 @@ class Field extends Component
|
||||
|
||||
if ($save === false) {
|
||||
return null;
|
||||
} elseif (is_callable($save) === true) {
|
||||
return $save->call($this, $value);
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_a($save, 'Closure') === true) {
|
||||
return $save->call($this, $value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,16 +212,19 @@ class Field extends Component
|
||||
],
|
||||
'computed' => [
|
||||
'after' => function () {
|
||||
/** @var \Kirby\Form\Field $this */
|
||||
if ($this->after !== null) {
|
||||
return $this->model()->toString($this->after);
|
||||
}
|
||||
},
|
||||
'before' => function () {
|
||||
/** @var \Kirby\Form\Field $this */
|
||||
if ($this->before !== null) {
|
||||
return $this->model()->toString($this->before);
|
||||
}
|
||||
},
|
||||
'default' => function () {
|
||||
/** @var \Kirby\Form\Field $this */
|
||||
if ($this->default === null) {
|
||||
return;
|
||||
}
|
||||
@@ -228,6 +236,7 @@ class Field extends Component
|
||||
return $this->model()->toString($this->default);
|
||||
},
|
||||
'help' => function () {
|
||||
/** @var \Kirby\Form\Field $this */
|
||||
if ($this->help) {
|
||||
$help = $this->model()->toString($this->help);
|
||||
$help = $this->kirby()->kirbytext($help);
|
||||
@@ -235,11 +244,13 @@ class Field extends Component
|
||||
}
|
||||
},
|
||||
'label' => function () {
|
||||
/** @var \Kirby\Form\Field $this */
|
||||
if ($this->label !== null) {
|
||||
return $this->model()->toString($this->label);
|
||||
}
|
||||
},
|
||||
'placeholder' => function () {
|
||||
/** @var \Kirby\Form\Field $this */
|
||||
if ($this->placeholder !== null) {
|
||||
return $this->model()->toString($this->placeholder);
|
||||
}
|
||||
@@ -350,13 +361,13 @@ class Field extends Component
|
||||
*/
|
||||
public function kirby()
|
||||
{
|
||||
return $this->model->kirby();
|
||||
return $this->model()->kirby();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent model
|
||||
*
|
||||
* @return mixed|null
|
||||
* @return mixed
|
||||
*/
|
||||
public function model()
|
||||
{
|
||||
|
@@ -20,8 +20,10 @@ class BlocksField extends FieldClass
|
||||
use Max;
|
||||
use Min;
|
||||
|
||||
protected $fieldsets;
|
||||
protected $blocks;
|
||||
protected $fieldsets;
|
||||
protected $group;
|
||||
protected $pretty;
|
||||
protected $value = [];
|
||||
|
||||
public function __construct(array $params = [])
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Kirby\Form\Field;
|
||||
|
||||
use Kirby\Cms\Blueprint;
|
||||
use Kirby\Cms\Fieldset;
|
||||
use Kirby\Cms\Form;
|
||||
use Kirby\Cms\Layout;
|
||||
@@ -19,7 +20,7 @@ class LayoutField extends BlocksField
|
||||
{
|
||||
$this->setModel($params['model'] ?? site());
|
||||
$this->setLayouts($params['layouts'] ?? ['1/1']);
|
||||
$this->setSettings($params['settings'] ?? []);
|
||||
$this->setSettings($params['settings'] ?? null);
|
||||
|
||||
parent::__construct($params);
|
||||
}
|
||||
@@ -120,13 +121,15 @@ class LayoutField extends BlocksField
|
||||
}, $layouts);
|
||||
}
|
||||
|
||||
protected function setSettings(array $settings = [])
|
||||
protected function setSettings($settings = null)
|
||||
{
|
||||
if (empty($settings) === true) {
|
||||
$this->settings = null;
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = Blueprint::extend($settings);
|
||||
|
||||
$settings['icon'] = 'dashboard';
|
||||
$settings['type'] = 'layout';
|
||||
$settings['parent'] = $this->model();
|
||||
|
@@ -353,6 +353,7 @@ abstract class FieldClass
|
||||
'saveable' => $this->isSaveable(),
|
||||
'translate' => $this->translate(),
|
||||
'type' => $this->type(),
|
||||
'when' => $this->when(),
|
||||
'width' => $this->width(),
|
||||
];
|
||||
}
|
||||
@@ -614,6 +615,16 @@ abstract class FieldClass
|
||||
return Data::encode($value, 'yaml');
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditions when the field will be shown
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function when(): ?array
|
||||
{
|
||||
return $this->when;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the field in
|
||||
* the Panel grid
|
||||
|
@@ -29,6 +29,7 @@ class Options
|
||||
return [
|
||||
'Kirby\Cms\File' => 'file',
|
||||
'Kirby\Toolkit\Obj' => 'arrayItem',
|
||||
'Kirby\Cms\Block' => 'block',
|
||||
'Kirby\Cms\Page' => 'page',
|
||||
'Kirby\Cms\StructureObject' => 'structureItem',
|
||||
'Kirby\Cms\User' => 'user',
|
||||
@@ -169,6 +170,7 @@ class Options
|
||||
// default text setup
|
||||
$text = [
|
||||
'arrayItem' => '{{ arrayItem.value }}',
|
||||
'block' => '{{ block.type }}: {{ block.id }}',
|
||||
'file' => '{{ file.filename }}',
|
||||
'page' => '{{ page.title }}',
|
||||
'structureItem' => '{{ structureItem.title }}',
|
||||
@@ -178,6 +180,7 @@ class Options
|
||||
// default value setup
|
||||
$value = [
|
||||
'arrayItem' => '{{ arrayItem.value }}',
|
||||
'block' => '{{ block.id }}',
|
||||
'file' => '{{ file.id }}',
|
||||
'page' => '{{ page.id }}',
|
||||
'structureItem' => '{{ structureItem.id }}',
|
||||
|
@@ -98,7 +98,8 @@ class Cookie
|
||||
*/
|
||||
public static function forever(string $key, string $value, array $options = []): bool
|
||||
{
|
||||
$options['lifetime'] = 253402214400; // 9999-12-31
|
||||
// 9999-12-31 if supported (lower on 32-bit servers)
|
||||
$options['lifetime'] = min(253402214400, PHP_INT_MAX);
|
||||
return static::set($key, $value, $options);
|
||||
}
|
||||
|
||||
|
@@ -119,6 +119,12 @@ class Params extends Query
|
||||
* @param bool $leadingSlash
|
||||
* @param bool $trailingSlash
|
||||
* @return string|null
|
||||
*
|
||||
* @todo The argument $leadingSlash is incompatible with
|
||||
* Query::toString($questionMark = false); the Query class
|
||||
* should be extracted into a common parent class for both
|
||||
* Query and Params
|
||||
* @psalm-suppress ParamNameMismatch
|
||||
*/
|
||||
public function toString($leadingSlash = false, $trailingSlash = false): string
|
||||
{
|
||||
|
@@ -129,7 +129,7 @@ class Uri
|
||||
/**
|
||||
* Creates a new URI object
|
||||
*
|
||||
* @param array $props
|
||||
* @param array|string $props
|
||||
* @param array $inject
|
||||
*/
|
||||
public function __construct($props = [], array $inject = [])
|
||||
|
@@ -161,7 +161,7 @@ class Url
|
||||
* @param string|array|null $url
|
||||
* @param bool $leadingSlash
|
||||
* @param bool $trailingSlash
|
||||
* @return xtring
|
||||
* @return string
|
||||
*/
|
||||
public static function path($url = null, bool $leadingSlash = false, bool $trailingSlash = false): string
|
||||
{
|
||||
|
@@ -3,7 +3,7 @@
|
||||
namespace Kirby\Parsley;
|
||||
|
||||
use DOMElement;
|
||||
use DOMXpath;
|
||||
use DOMXPath;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
class Element
|
||||
|
@@ -35,8 +35,6 @@ class Inline
|
||||
|
||||
public function parseNode($node)
|
||||
{
|
||||
$html = '';
|
||||
|
||||
if (is_a($node, 'DOMText') === true) {
|
||||
return $node->textContent;
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ class Parsley
|
||||
protected $blocks = [];
|
||||
protected $body;
|
||||
protected $doc;
|
||||
protected $inline;
|
||||
protected $marks = [];
|
||||
protected $nodes = [];
|
||||
protected $schema;
|
||||
|
@@ -115,7 +115,6 @@ class FileSessionStore extends SessionStore
|
||||
public function lock(int $expiryTime, string $id)
|
||||
{
|
||||
$name = $this->name($expiryTime, $id);
|
||||
$path = $this->path($name);
|
||||
|
||||
// check if the file is already locked
|
||||
if (isset($this->isLocked[$name])) {
|
||||
@@ -153,7 +152,6 @@ class FileSessionStore extends SessionStore
|
||||
public function unlock(int $expiryTime, string $id)
|
||||
{
|
||||
$name = $this->name($expiryTime, $id);
|
||||
$path = $this->path($name);
|
||||
|
||||
// check if the file is already unlocked or doesn't exist
|
||||
if (!isset($this->isLocked[$name])) {
|
||||
@@ -258,7 +256,6 @@ class FileSessionStore extends SessionStore
|
||||
public function set(int $expiryTime, string $id, string $data)
|
||||
{
|
||||
$name = $this->name($expiryTime, $id);
|
||||
$path = $this->path($name);
|
||||
$handle = $this->handle($name);
|
||||
|
||||
// validate that we have an exclusive lock already
|
||||
|
@@ -335,6 +335,10 @@ class Session
|
||||
public function commit()
|
||||
{
|
||||
// nothing to do if nothing changed or the session has been just created or destroyed
|
||||
/**
|
||||
* @todo The $this->destroyed check gets flagged by Psalm for unknown reasons
|
||||
* @psalm-suppress ParadoxicalCondition
|
||||
*/
|
||||
if ($this->writeMode !== true || $this->tokenExpiry === null || $this->destroyed === true) {
|
||||
return;
|
||||
}
|
||||
@@ -515,12 +519,20 @@ class Session
|
||||
// using $session->ensureToken() -> lazy session creation
|
||||
// - destroyed sessions are never written to
|
||||
// - no need to lock and re-init if we are already in write mode
|
||||
/**
|
||||
* @todo The $this->destroyed check gets flagged by Psalm for unknown reasons
|
||||
* @psalm-suppress ParadoxicalCondition
|
||||
*/
|
||||
if ($this->tokenExpiry === null || $this->destroyed === true || $this->writeMode === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// don't allow writing for read-only sessions
|
||||
// (only the case for moved sessions)
|
||||
/**
|
||||
* @todo This check gets flagged by Psalm for unknown reasons
|
||||
* @psalm-suppress ParadoxicalCondition
|
||||
*/
|
||||
if ($this->tokenKey === null) {
|
||||
throw new LogicException([
|
||||
'key' => 'session.readonly',
|
||||
|
@@ -242,7 +242,7 @@ class Collection extends Iterator implements Countable
|
||||
* custom filter function or an array of filters
|
||||
*
|
||||
* @param string|array|\Closure $field
|
||||
* @param array ...$args
|
||||
* @param mixed ...$args
|
||||
* @return \Kirby\Toolkit\Collection
|
||||
*/
|
||||
public function filter($field, ...$args)
|
||||
|
@@ -178,7 +178,7 @@ class Component
|
||||
protected function applyProps(array $props): void
|
||||
{
|
||||
foreach ($props as $propName => $propFunction) {
|
||||
if (is_callable($propFunction) === true) {
|
||||
if (is_a($propFunction, 'Closure') === true) {
|
||||
if (isset($this->attrs[$propName]) === true) {
|
||||
try {
|
||||
$this->$propName = $this->props[$propName] = $propFunction->call($this, $this->attrs[$propName]);
|
||||
@@ -208,7 +208,7 @@ class Component
|
||||
protected function applyComputed(array $computed): void
|
||||
{
|
||||
foreach ($computed as $computedName => $computedFunction) {
|
||||
if (is_callable($computedFunction) === true) {
|
||||
if (is_a($computedFunction, 'Closure') === true) {
|
||||
$this->$computedName = $this->computed[$computedName] = $computedFunction->call($this);
|
||||
}
|
||||
}
|
||||
|
@@ -588,7 +588,7 @@ class F
|
||||
return $newRoot;
|
||||
}
|
||||
|
||||
if (F::move($file, $newRoot) !== true) {
|
||||
if (F::move($file, $newRoot, $overwrite) !== true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -175,6 +175,8 @@ class Html extends Xml
|
||||
* @param string|null $string
|
||||
* @param bool $keepTags If true, existing tags won't be escaped
|
||||
* @return string The HTML string
|
||||
*
|
||||
* @psalm-suppress ParamNameMismatch
|
||||
*/
|
||||
public static function encode(?string $string, bool $keepTags = false): string
|
||||
{
|
||||
|
@@ -38,35 +38,51 @@ class I18n
|
||||
public static $translations = [];
|
||||
|
||||
/**
|
||||
* The fallback locale
|
||||
* The fallback locale or a
|
||||
* list of fallback locales
|
||||
*
|
||||
* @var string
|
||||
* @var string|array
|
||||
*/
|
||||
public static $fallback = 'en';
|
||||
public static $fallback = ['en'];
|
||||
|
||||
/**
|
||||
* Cache of `NumberFormatter` objects by locale
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $decimalNumberFormatters = [];
|
||||
protected static $decimalsFormatters = [];
|
||||
|
||||
/**
|
||||
* Returns the fallback code
|
||||
* Returns the first fallback locale
|
||||
*
|
||||
* @deprecated 3.5.1 Use \Kirby\Toolkit\I18n::fallbacks() instead
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function fallback(): string
|
||||
{
|
||||
if (is_string(static::$fallback) === true) {
|
||||
return static::$fallback;
|
||||
return static::fallbacks()[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of fallback locales
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function fallbacks(): array
|
||||
{
|
||||
if (
|
||||
is_array(static::$fallback) === true ||
|
||||
is_string(static::$fallback) === true
|
||||
) {
|
||||
return A::wrap(static::$fallback);
|
||||
}
|
||||
|
||||
if (is_callable(static::$fallback) === true) {
|
||||
return static::$fallback = (static::$fallback)();
|
||||
return static::$fallback = A::wrap((static::$fallback)());
|
||||
}
|
||||
|
||||
return static::$fallback = 'en';
|
||||
return static::$fallback = ['en'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,8 +170,15 @@ class I18n
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
if ($locale !== static::fallback()) {
|
||||
return static::translation(static::fallback())[$key] ?? null;
|
||||
foreach (static::fallbacks() as $fallback) {
|
||||
// skip locales we have already tried
|
||||
if ($locale === $fallback) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($translation = static::translation($fallback)[$key] ?? null) {
|
||||
return $translation;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -166,12 +189,12 @@ class I18n
|
||||
* placeholders in the text
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $fallback
|
||||
* @param array $replace
|
||||
* @param string $locale
|
||||
* @param string|array|null $fallback
|
||||
* @param array|null $replace
|
||||
* @param string|null $locale
|
||||
* @return string
|
||||
*/
|
||||
public static function template(string $key, $fallback = null, array $replace = null, string $locale = null)
|
||||
public static function template(string $key, $fallback = null, ?array $replace = null, ?string $locale = null): string
|
||||
{
|
||||
if (is_array($fallback) === true) {
|
||||
$replace = $fallback;
|
||||
@@ -223,15 +246,15 @@ class I18n
|
||||
*/
|
||||
protected static function decimalNumberFormatter(string $locale): ?NumberFormatter
|
||||
{
|
||||
if (isset(static::$decimalNumberFormatters[$locale])) {
|
||||
return static::$decimalNumberFormatters[$locale];
|
||||
if (isset(static::$decimalsFormatters[$locale])) {
|
||||
return static::$decimalsFormatters[$locale];
|
||||
}
|
||||
|
||||
if (extension_loaded('intl') !== true || class_exists('NumberFormatter') !== true) {
|
||||
return null;
|
||||
return null; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
return static::$decimalNumberFormatters[$locale] = new NumberFormatter($locale, NumberFormatter::DECIMAL);
|
||||
return static::$decimalsFormatters[$locale] = new NumberFormatter($locale, NumberFormatter::DECIMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -76,7 +76,7 @@ class Query
|
||||
* @param string $query
|
||||
* @return mixed
|
||||
*
|
||||
* @throws Kirby\Exception\BadMethodCallException If an invalid method is accessed by the query
|
||||
* @throws \Kirby\Exception\BadMethodCallException If an invalid method is accessed by the query
|
||||
*/
|
||||
protected function resolve(string $query)
|
||||
{
|
||||
@@ -227,7 +227,7 @@ class Query
|
||||
* @param string $label Type of the name (`method`, `property` or `method/property`)
|
||||
* @return void
|
||||
*
|
||||
* @throws Kirby\Exception\BadMethodCallException
|
||||
* @throws \Kirby\Exception\BadMethodCallException
|
||||
*/
|
||||
protected static function accessError($data, string $name, string $label): void
|
||||
{
|
||||
|
Reference in New Issue
Block a user