Upgrade to 3.9.0
This commit is contained in:
@@ -18,6 +18,7 @@ use Kirby\Http\Router;
|
||||
use Kirby\Http\Uri;
|
||||
use Kirby\Http\Visitor;
|
||||
use Kirby\Session\AutoSession;
|
||||
use Kirby\Template\Snippet;
|
||||
use Kirby\Text\KirbyTag;
|
||||
use Kirby\Text\KirbyTags;
|
||||
use Kirby\Toolkit\A;
|
||||
@@ -1278,6 +1279,11 @@ class App
|
||||
// set the current locale
|
||||
$this->setCurrentLanguage($language);
|
||||
|
||||
// directly prevent path with incomplete content representation
|
||||
if (Str::endsWith($path, '.') === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// the site is needed a couple times here
|
||||
$site = $this->site();
|
||||
|
||||
@@ -1557,24 +1563,6 @@ class App
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Environment object
|
||||
* @deprecated 3.7.0 Use `$kirby->environment()` instead
|
||||
*
|
||||
* @return \Kirby\Http\Environment
|
||||
* @deprecated Will be removed in Kirby 3.9.0
|
||||
* @todo Remove in 3.9.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function server()
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
Helpers::deprecated('$kirby->server() has been deprecated and will be removed in Kirby 3.9.0. Use $kirby->environment() instead.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return $this->environment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes and returns the Site object
|
||||
*
|
||||
@@ -1630,15 +1618,20 @@ class App
|
||||
* @return string|null
|
||||
* @psalm-return ($return is true ? string : null)
|
||||
*/
|
||||
public function snippet($name, $data = [], bool $return = true): string|null
|
||||
public function snippet($name, $data = [], bool $return = true, bool $slots = false): Snippet|string|null
|
||||
{
|
||||
if (is_object($data) === true) {
|
||||
$data = ['item' => $data];
|
||||
}
|
||||
|
||||
$snippet = ($this->component('snippet'))($this, $name, array_merge($this->data, $data));
|
||||
$snippet = ($this->component('snippet'))(
|
||||
$this,
|
||||
$name,
|
||||
array_merge($this->data, $data),
|
||||
$slots
|
||||
);
|
||||
|
||||
if ($return === true) {
|
||||
if ($return === true || $slots === true) {
|
||||
return $snippet;
|
||||
}
|
||||
|
||||
@@ -1661,7 +1654,7 @@ class App
|
||||
* and return the Template object
|
||||
*
|
||||
* @internal
|
||||
* @return \Kirby\Cms\Template
|
||||
* @return \Kirby\Template\Template
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
* @param string $defaultType
|
||||
|
||||
@@ -82,9 +82,10 @@ trait AppCaches
|
||||
];
|
||||
}
|
||||
|
||||
$prefix = str_replace(['/', ':'], '_', $this->system()->indexUrl()) .
|
||||
'/' .
|
||||
str_replace('.', '/', $key);
|
||||
$prefix =
|
||||
str_replace(['/', ':'], '_', $this->system()->indexUrl()) .
|
||||
'/' .
|
||||
str_replace('.', '/', $key);
|
||||
|
||||
$defaults = [
|
||||
'active' => true,
|
||||
|
||||
@@ -60,10 +60,7 @@ trait AppUsers
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO: switch over in 3.9.0 to
|
||||
// return $callback($userAfter);
|
||||
$proxy = new AppUsersImpersonateProxy($this);
|
||||
return $callback->call($proxy, $userAfter);
|
||||
return $callback($userAfter);
|
||||
} catch (Throwable $e) {
|
||||
throw $e;
|
||||
} finally {
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Kirby\Cms;
|
||||
|
||||
/**
|
||||
* Temporary proxy class to ease transition
|
||||
* of binding the callback for `$kirby->impersonate()`
|
||||
*
|
||||
* @package Kirby Cms
|
||||
* @author Nico Hoffmann <nico@getkirby.com>,
|
||||
* Lukas Bestle <lukas@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://getkirby.com/license
|
||||
*
|
||||
* @internal
|
||||
* @deprecated Will be removed in Kirby 3.9.0
|
||||
* @todo remove in 3.9.0
|
||||
*/
|
||||
class AppUsersImpersonateProxy
|
||||
{
|
||||
public function __construct(protected App $app)
|
||||
{
|
||||
}
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
Helpers::deprecated('Calling $kirby->' . $name . '() as $this->' . $name . '() has been deprecated inside the $kirby->impersonate() callback function. Use a dedicated $kirby object for your call instead of $this. In Kirby 3.9.0 $this will no longer refer to the $kirby object, but the current context of the callback function.');
|
||||
|
||||
return $this->app->$name(...$arguments);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ use Kirby\Http\Idn;
|
||||
use Kirby\Http\Request\Auth\BasicAuth;
|
||||
use Kirby\Session\Session;
|
||||
use Kirby\Toolkit\A;
|
||||
use SensitiveParameter;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
@@ -381,17 +382,16 @@ class Auth
|
||||
/**
|
||||
* Login a user by email and password
|
||||
*
|
||||
* @param string $email
|
||||
* @param string $password
|
||||
* @param bool $long
|
||||
* @return \Kirby\Cms\User
|
||||
*
|
||||
* @throws \Kirby\Exception\PermissionException If the rate limit was exceeded or if any other error occurred with debug mode off
|
||||
* @throws \Kirby\Exception\NotFoundException If the email was invalid
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the password is not valid (via `$user->login()`)
|
||||
*/
|
||||
public function login(string $email, string $password, bool $long = false)
|
||||
{
|
||||
public function login(
|
||||
string $email,
|
||||
#[SensitiveParameter]
|
||||
string $password,
|
||||
bool $long = false
|
||||
): User {
|
||||
// session options
|
||||
$options = [
|
||||
'createMode' => 'cookie',
|
||||
@@ -412,17 +412,16 @@ class Auth
|
||||
* Login a user by email, password and auth challenge
|
||||
* @since 3.5.0
|
||||
*
|
||||
* @param string $email
|
||||
* @param string $password
|
||||
* @param bool $long
|
||||
* @return \Kirby\Cms\Auth\Status
|
||||
*
|
||||
* @throws \Kirby\Exception\PermissionException If the rate limit was exceeded or if any other error occurred with debug mode off
|
||||
* @throws \Kirby\Exception\NotFoundException If the email was invalid
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the password is not valid (via `$user->login()`)
|
||||
*/
|
||||
public function login2fa(string $email, string $password, bool $long = false)
|
||||
{
|
||||
public function login2fa(
|
||||
string $email,
|
||||
#[SensitiveParameter]
|
||||
string $password,
|
||||
bool $long = false
|
||||
): Status {
|
||||
$this->validatePassword($email, $password);
|
||||
return $this->createChallenge($email, $long, '2fa');
|
||||
}
|
||||
@@ -516,16 +515,15 @@ class Auth
|
||||
* Validates the user credentials and returns the user object on success;
|
||||
* otherwise logs the failed attempt
|
||||
*
|
||||
* @param string $email
|
||||
* @param string $password
|
||||
* @return \Kirby\Cms\User
|
||||
*
|
||||
* @throws \Kirby\Exception\PermissionException If the rate limit was exceeded or if any other error occurred with debug mode off
|
||||
* @throws \Kirby\Exception\NotFoundException If the email was invalid
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the password is not valid (via `$user->login()`)
|
||||
*/
|
||||
public function validatePassword(string $email, string $password)
|
||||
{
|
||||
public function validatePassword(
|
||||
string $email,
|
||||
#[SensitiveParameter]
|
||||
string $password
|
||||
): User {
|
||||
$email = Idn::decodeEmail($email);
|
||||
|
||||
try {
|
||||
@@ -798,8 +796,10 @@ class Auth
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If no authentication challenge is active
|
||||
* @throws \Kirby\Exception\LogicException If the authentication challenge is invalid
|
||||
*/
|
||||
public function verifyChallenge(string $code)
|
||||
{
|
||||
public function verifyChallenge(
|
||||
#[SensitiveParameter]
|
||||
string $code
|
||||
) {
|
||||
try {
|
||||
$session = $this->kirby->session();
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Kirby\Cms\Auth;
|
||||
|
||||
use Kirby\Cms\User;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Template class for authentication challenges
|
||||
@@ -48,8 +49,11 @@ abstract class Challenge
|
||||
* @param string $code Code to verify
|
||||
* @return bool
|
||||
*/
|
||||
public static function verify(User $user, string $code): bool
|
||||
{
|
||||
public static function verify(
|
||||
User $user,
|
||||
#[SensitiveParameter]
|
||||
string $code
|
||||
): bool {
|
||||
$hash = $user->kirby()->session()->get('kirby.challenge.code');
|
||||
if (is_string($hash) !== true) {
|
||||
return false;
|
||||
|
||||
@@ -115,8 +115,9 @@ class ContentTranslation
|
||||
*/
|
||||
public function exists(): bool
|
||||
{
|
||||
return empty($this->content) === false ||
|
||||
file_exists($this->contentFile()) === true;
|
||||
return
|
||||
empty($this->content) === false ||
|
||||
file_exists($this->contentFile()) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -132,7 +132,7 @@ class Email
|
||||
*
|
||||
* @param string $name Template name
|
||||
* @param string|null $type `html` or `text`
|
||||
* @return \Kirby\Cms\Template
|
||||
* @return \Kirby\Template\Template
|
||||
*/
|
||||
protected function getTemplate(string $name, string $type = null)
|
||||
{
|
||||
|
||||
@@ -169,11 +169,12 @@ trait FileActions
|
||||
* way of generating files.
|
||||
*
|
||||
* @param array $props
|
||||
* @param bool $move If set to `true`, the source will be deleted
|
||||
* @return static
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
* @throws \Kirby\Exception\LogicException
|
||||
*/
|
||||
public static function create(array $props)
|
||||
public static function create(array $props, bool $move = false)
|
||||
{
|
||||
if (isset($props['source'], $props['parent']) === false) {
|
||||
throw new InvalidArgumentException('Please provide the "source" and "parent" props for the File');
|
||||
@@ -204,12 +205,16 @@ trait FileActions
|
||||
$file = $file->clone(['content' => $form->strings(true)]);
|
||||
|
||||
// run the hook
|
||||
return $file->commit('create', compact('file', 'upload'), function ($file, $upload) {
|
||||
$arguments = compact('file', 'upload');
|
||||
return $file->commit('create', $arguments, function ($file, $upload) use ($move) {
|
||||
// remove all public versions, lock and clear UUID cache
|
||||
$file->unpublish();
|
||||
|
||||
// only move the original source if intended
|
||||
$method = $move === true ? 'move' : 'copy';
|
||||
|
||||
// overwrite the original
|
||||
if (F::copy($upload->root(), $file->root(), true) !== true) {
|
||||
if (F::$method($upload->root(), $file->root(), true) !== true) {
|
||||
throw new LogicException('The file could not be created');
|
||||
}
|
||||
|
||||
@@ -280,10 +285,11 @@ trait FileActions
|
||||
* source.
|
||||
*
|
||||
* @param string $source
|
||||
* @param bool $move If set to `true`, the source will be deleted
|
||||
* @return static
|
||||
* @throws \Kirby\Exception\LogicException
|
||||
*/
|
||||
public function replace(string $source)
|
||||
public function replace(string $source, bool $move = false)
|
||||
{
|
||||
$file = $this->clone();
|
||||
|
||||
@@ -292,12 +298,15 @@ trait FileActions
|
||||
'upload' => $file->asset($source)
|
||||
];
|
||||
|
||||
return $this->commit('replace', $arguments, function ($file, $upload) {
|
||||
return $this->commit('replace', $arguments, function ($file, $upload) use ($move) {
|
||||
// delete all public versions
|
||||
$file->unpublish(true);
|
||||
|
||||
// only move the original source if intended
|
||||
$method = $move === true ? 'move' : 'copy';
|
||||
|
||||
// overwrite the original
|
||||
if (F::copy($upload->root(), $file->root(), true) !== true) {
|
||||
if (F::$method($upload->root(), $file->root(), true) !== true) {
|
||||
throw new LogicException('The file could not be created');
|
||||
}
|
||||
|
||||
|
||||
@@ -57,16 +57,17 @@ trait HasFiles
|
||||
* Creates a new file
|
||||
*
|
||||
* @param array $props
|
||||
* @param bool $move If set to `true`, the source will be deleted
|
||||
* @return \Kirby\Cms\File
|
||||
*/
|
||||
public function createFile(array $props)
|
||||
public function createFile(array $props, bool $move = false)
|
||||
{
|
||||
$props = array_merge($props, [
|
||||
'parent' => $this,
|
||||
'url' => null
|
||||
]);
|
||||
|
||||
return File::create($props);
|
||||
return File::create($props, $move);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,7 +26,10 @@ class Helpers
|
||||
*/
|
||||
public static function deprecated(string $message): bool
|
||||
{
|
||||
if (App::instance()->option('debug') === true) {
|
||||
if (
|
||||
App::instance()->option('debug') === true ||
|
||||
(defined('KIRBY_TESTING') === true && KIRBY_TESTING === true)
|
||||
) {
|
||||
return trigger_error($message, E_USER_DEPRECATED) === true;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,8 @@ class Html extends \Kirby\Toolkit\Html
|
||||
}
|
||||
}
|
||||
|
||||
// only valid value for 'rel' is 'alternate stylesheet', if 'title' is given as well
|
||||
// only valid value for 'rel' is 'alternate stylesheet',
|
||||
// if 'title' is given as well
|
||||
if (
|
||||
($options['rel'] ?? '') !== 'alternate stylesheet' ||
|
||||
($options['title'] ?? '') === ''
|
||||
|
||||
@@ -658,12 +658,8 @@ class Language extends Model
|
||||
*/
|
||||
public function url(): string
|
||||
{
|
||||
$url = $this->url;
|
||||
|
||||
if ($url === null) {
|
||||
$url = '/' . $this->code;
|
||||
}
|
||||
|
||||
$url = $this->url;
|
||||
$url ??= '/' . $this->code;
|
||||
return Url::makeAbsolute($url, $this->kirby()->url());
|
||||
}
|
||||
|
||||
|
||||
@@ -629,13 +629,11 @@ abstract class ModelWithContent extends Model implements Identifiable
|
||||
]);
|
||||
|
||||
// validate the input
|
||||
if ($validate === true) {
|
||||
if ($form->isInvalid() === true) {
|
||||
throw new InvalidArgumentException([
|
||||
'fallback' => 'Invalid form with errors',
|
||||
'details' => $form->errors()
|
||||
]);
|
||||
}
|
||||
if ($validate === true && $form->isInvalid() === true) {
|
||||
throw new InvalidArgumentException([
|
||||
'fallback' => 'Invalid form with errors',
|
||||
'details' => $form->errors()
|
||||
]);
|
||||
}
|
||||
|
||||
$arguments = [static::CLASS_ALIAS => $this, 'values' => $form->data(), 'strings' => $form->strings(), 'languageCode' => $languageCode];
|
||||
|
||||
@@ -96,7 +96,7 @@ class Page extends ModelWithContent
|
||||
* The template, that should be loaded
|
||||
* if it exists
|
||||
*
|
||||
* @var \Kirby\Cms\Template
|
||||
* @var \Kirby\Template\Template
|
||||
*/
|
||||
protected $intendedTemplate;
|
||||
|
||||
@@ -143,7 +143,7 @@ class Page extends ModelWithContent
|
||||
/**
|
||||
* The intended page template
|
||||
*
|
||||
* @var \Kirby\Cms\Template
|
||||
* @var \Kirby\Template\Template
|
||||
*/
|
||||
protected $template;
|
||||
|
||||
@@ -504,7 +504,7 @@ class Page extends ModelWithContent
|
||||
* Returns the template that should be
|
||||
* loaded if it exists.
|
||||
*
|
||||
* @return \Kirby\Cms\Template
|
||||
* @return \Kirby\Template\Template
|
||||
*/
|
||||
public function intendedTemplate()
|
||||
{
|
||||
@@ -1096,7 +1096,7 @@ class Page extends ModelWithContent
|
||||
/**
|
||||
* @internal
|
||||
* @param mixed $type
|
||||
* @return \Kirby\Cms\Template
|
||||
* @return \Kirby\Template\Template
|
||||
* @throws \Kirby\Exception\NotFoundException If the content representation cannot be found
|
||||
*/
|
||||
public function representation($type)
|
||||
@@ -1277,13 +1277,13 @@ class Page extends ModelWithContent
|
||||
public function slug(string $languageCode = null): string
|
||||
{
|
||||
if ($this->kirby()->multilang() === true) {
|
||||
if ($languageCode === null) {
|
||||
$languageCode = $this->kirby()->languageCode();
|
||||
}
|
||||
|
||||
$languageCode ??= $this->kirby()->languageCode();
|
||||
$defaultLanguageCode = $this->kirby()->defaultLanguage()->code();
|
||||
|
||||
if ($languageCode !== $defaultLanguageCode && $translation = $this->translations()->find($languageCode)) {
|
||||
if (
|
||||
$languageCode !== $defaultLanguageCode &&
|
||||
$translation = $this->translations()->find($languageCode)
|
||||
) {
|
||||
return $translation->slug() ?? $this->slug;
|
||||
}
|
||||
}
|
||||
@@ -1313,7 +1313,7 @@ class Page extends ModelWithContent
|
||||
/**
|
||||
* Returns the final template
|
||||
*
|
||||
* @return \Kirby\Cms\Template
|
||||
* @return \Kirby\Template\Template
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
|
||||
@@ -223,7 +223,7 @@ trait PageActions
|
||||
throw new InvalidArgumentException('Use the changeSlug method to change the slug for the default language');
|
||||
}
|
||||
|
||||
$arguments = ['page' => $this, 'slug' => $slug, 'languageCode' => $languageCode];
|
||||
$arguments = ['page' => $this, 'slug' => $slug, 'languageCode' => $language->code()];
|
||||
return $this->commit('changeSlug', $arguments, function ($page, $slug, $languageCode) {
|
||||
// remove the slug if it's the same as the folder name
|
||||
if ($slug === $page->uid()) {
|
||||
@@ -620,9 +620,7 @@ trait PageActions
|
||||
->count();
|
||||
|
||||
// default positioning at the end
|
||||
if ($num === null) {
|
||||
$num = $max;
|
||||
}
|
||||
$num ??= $max;
|
||||
|
||||
// avoid zeros or negative numbers
|
||||
if ($num < 1) {
|
||||
|
||||
@@ -255,9 +255,7 @@ class Plugin extends Model
|
||||
}
|
||||
}
|
||||
|
||||
if ($option === null) {
|
||||
$option = $kirby->option('updates') ?? true;
|
||||
}
|
||||
$option ??= $kirby->option('updates') ?? true;
|
||||
|
||||
if ($option !== true) {
|
||||
return null;
|
||||
|
||||
@@ -31,63 +31,37 @@ use Throwable;
|
||||
*/
|
||||
class System
|
||||
{
|
||||
/**
|
||||
* @var \Kirby\Cms\App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
// cache
|
||||
protected UpdateStatus|null $updateStatus = null;
|
||||
|
||||
/**
|
||||
* @param \Kirby\Cms\App $app
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
public function __construct(protected App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
|
||||
// try to create all folders that could be missing
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a writable accounts folder
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function accounts(): bool
|
||||
{
|
||||
return is_writable($this->app->root('accounts'));
|
||||
return is_writable($this->app->root('accounts')) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a writable content folder
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function content(): bool
|
||||
{
|
||||
return is_writable($this->app->root('content'));
|
||||
return is_writable($this->app->root('content')) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for an existing curl extension
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function curl(): bool
|
||||
{
|
||||
return extension_loaded('curl');
|
||||
return extension_loaded('curl') === true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,7 +70,6 @@ class System
|
||||
* root. Otherwise it will return null.
|
||||
*
|
||||
* @param string $folder 'git', 'content', 'site', 'kirby'
|
||||
* @return string|null
|
||||
*/
|
||||
public function exposedFileUrl(string $folder): string|null
|
||||
{
|
||||
@@ -142,19 +115,20 @@ class System
|
||||
* root. Otherwise it will return null.
|
||||
*
|
||||
* @param string $folder 'git', 'content', 'site', 'kirby'
|
||||
* @return string|null
|
||||
*/
|
||||
public function folderUrl(string $folder): string|null
|
||||
{
|
||||
$index = $this->app->root('index');
|
||||
$root = match ($folder) {
|
||||
'git' => $index . '/.git',
|
||||
default => $this->app->root($folder)
|
||||
};
|
||||
|
||||
if ($folder === 'git') {
|
||||
$root = $index . '/.git';
|
||||
} else {
|
||||
$root = $this->app->root($folder);
|
||||
}
|
||||
|
||||
if ($root === null || is_dir($root) === false || is_dir($index) === false) {
|
||||
if (
|
||||
$root === null ||
|
||||
is_dir($root) === false ||
|
||||
is_dir($index) === false
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -180,22 +154,22 @@ class System
|
||||
/**
|
||||
* Returns the app's human-readable
|
||||
* index URL without scheme
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function indexUrl(): string
|
||||
{
|
||||
return $this->app->url('index', true)->setScheme(null)->setSlash(false)->toString();
|
||||
return $this->app->url('index', true)
|
||||
->setScheme(null)
|
||||
->setSlash(false)
|
||||
->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the most important folders
|
||||
* if they don't exist yet
|
||||
*
|
||||
* @return void
|
||||
* @throws \Kirby\Exception\PermissionException
|
||||
*/
|
||||
public function init()
|
||||
public function init(): void
|
||||
{
|
||||
// init /site/accounts
|
||||
try {
|
||||
@@ -231,18 +205,16 @@ class System
|
||||
* On a public server the panel.install
|
||||
* option must be explicitly set to true
|
||||
* to get the installer up and running.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInstallable(): bool
|
||||
{
|
||||
return $this->isLocal() === true || $this->app->option('panel.install', false) === true;
|
||||
return
|
||||
$this->isLocal() === true ||
|
||||
$this->app->option('panel.install', false) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Kirby is already installed
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInstalled(): bool
|
||||
{
|
||||
@@ -251,8 +223,6 @@ class System
|
||||
|
||||
/**
|
||||
* Check if this is a local installation
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLocal(): bool
|
||||
{
|
||||
@@ -261,8 +231,6 @@ class System
|
||||
|
||||
/**
|
||||
* Check if all tests pass
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isOk(): bool
|
||||
{
|
||||
@@ -311,7 +279,9 @@ class System
|
||||
$pubKey = F::read($this->app->root('kirby') . '/kirby.pub');
|
||||
|
||||
// verify the license signature
|
||||
if (openssl_verify(json_encode($data), hex2bin($license['signature']), $pubKey, 'RSA-SHA256') !== 1) {
|
||||
$data = json_encode($data);
|
||||
$signature = hex2bin($license['signature']);
|
||||
if (openssl_verify($data, $signature, $pubKey, 'RSA-SHA256') !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -338,9 +308,7 @@ class System
|
||||
*/
|
||||
protected function licenseUrl(string $url = null): string
|
||||
{
|
||||
if ($url === null) {
|
||||
$url = $this->indexUrl();
|
||||
}
|
||||
$url ??= $this->indexUrl();
|
||||
|
||||
// remove common "testing" subdomains as well as www.
|
||||
// to ensure that installations of the same site have
|
||||
@@ -371,8 +339,6 @@ class System
|
||||
* Returns the configured UI modes for the login form
|
||||
* with their respective options
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the configuration is invalid
|
||||
* (only in debug mode)
|
||||
*/
|
||||
@@ -431,45 +397,38 @@ class System
|
||||
|
||||
/**
|
||||
* Check for an existing mbstring extension
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function mbString(): bool
|
||||
{
|
||||
return extension_loaded('mbstring');
|
||||
return extension_loaded('mbstring') === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a writable media folder
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function media(): bool
|
||||
{
|
||||
return is_writable($this->app->root('media'));
|
||||
return is_writable($this->app->root('media')) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a valid PHP version
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function php(): bool
|
||||
{
|
||||
return
|
||||
version_compare(PHP_VERSION, '8.0.0', '>=') === true &&
|
||||
version_compare(PHP_VERSION, '8.2.0', '<') === true;
|
||||
version_compare(PHP_VERSION, '8.3.0', '<') === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sorted collection of all
|
||||
* installed plugins
|
||||
*
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function plugins()
|
||||
public function plugins(): Collection
|
||||
{
|
||||
return (new Collection(App::instance()->plugins()))->sortBy('name', 'asc');
|
||||
$plugins = new Collection($this->app->plugins());
|
||||
return $plugins->sortBy('name', 'asc');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -477,24 +436,17 @@ class System
|
||||
* and adds it to the .license file in the config
|
||||
* folder if possible.
|
||||
*
|
||||
* @param string|null $license
|
||||
* @param string|null $email
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\Exception
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function register(string $license = null, string $email = null): bool
|
||||
{
|
||||
if (Str::startsWith($license, 'K3-PRO-') === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'license.format'
|
||||
]);
|
||||
throw new InvalidArgumentException(['key' => 'license.format']);
|
||||
}
|
||||
|
||||
if (V::email($email) === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'license.email'
|
||||
]);
|
||||
throw new InvalidArgumentException(['key' => 'license.email']);
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
@@ -534,8 +486,6 @@ class System
|
||||
|
||||
/**
|
||||
* Check for a valid server environment
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function server(): bool
|
||||
{
|
||||
@@ -544,8 +494,6 @@ class System
|
||||
|
||||
/**
|
||||
* Returns the detected server software
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function serverSoftware(): string|null
|
||||
{
|
||||
@@ -566,18 +514,14 @@ class System
|
||||
|
||||
/**
|
||||
* Check for a writable sessions folder
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function sessions(): bool
|
||||
{
|
||||
return is_writable($this->app->root('sessions'));
|
||||
return is_writable($this->app->root('sessions')) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an status array of all checks
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function status(): array
|
||||
{
|
||||
@@ -597,23 +541,18 @@ class System
|
||||
* Returns the site's title as defined in the
|
||||
* content file or `site.yml` blueprint
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function title(): string
|
||||
{
|
||||
$site = $this->app->site();
|
||||
|
||||
if ($site->title()->isNotEmpty()) {
|
||||
if ($site->title()->isNotEmpty() === true) {
|
||||
return $site->title()->value();
|
||||
}
|
||||
|
||||
return $site->blueprint()->title();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->status();
|
||||
@@ -633,7 +572,9 @@ class System
|
||||
}
|
||||
|
||||
$kirby = $this->app;
|
||||
$option = $kirby->option('updates.kirby') ?? $kirby->option('updates') ?? true;
|
||||
$option =
|
||||
$kirby->option('updates.kirby') ??
|
||||
$kirby->option('updates', true);
|
||||
|
||||
if ($option === false) {
|
||||
return null;
|
||||
@@ -648,11 +589,8 @@ class System
|
||||
|
||||
/**
|
||||
* Upgrade to the new folder separator
|
||||
*
|
||||
* @param string $root
|
||||
* @return void
|
||||
*/
|
||||
public static function upgradeContent(string $root)
|
||||
public static function upgradeContent(string $root): void
|
||||
{
|
||||
$index = Dir::read($root);
|
||||
|
||||
@@ -666,4 +604,12 @@ class System
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,9 +435,7 @@ class UpdateStatus
|
||||
// verify that we found at least one possible version;
|
||||
// otherwise try the `$maxVersion` as a last chance before
|
||||
// concluding at the top that we cannot solve the task
|
||||
if ($incidentVersion === null) {
|
||||
$incidentVersion = $maxVersion;
|
||||
}
|
||||
$incidentVersion ??= $maxVersion;
|
||||
|
||||
// we need a version that fixes all vulnerabilities, so use the
|
||||
// "largest of the smallest" fixed versions
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Kirby\Cms;
|
||||
|
||||
use Exception;
|
||||
use Kirby\Filesystem\F;
|
||||
use Kirby\Toolkit\Tpl;
|
||||
|
||||
/**
|
||||
* Represents a Kirby template and takes care
|
||||
* of loading the correct file.
|
||||
*
|
||||
* @package Kirby Cms
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://getkirby.com/license
|
||||
*/
|
||||
class Template
|
||||
{
|
||||
/**
|
||||
* Global template data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $data = [];
|
||||
|
||||
/**
|
||||
* The name of the template
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Template type (html, json, etc.)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Default template type if no specific type is set
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultType;
|
||||
|
||||
/**
|
||||
* Creates a new template object
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
* @param string $defaultType
|
||||
*/
|
||||
public function __construct(string $name, string $type = 'html', string $defaultType = 'html')
|
||||
{
|
||||
$this->name = strtolower($name);
|
||||
$this->type = $type;
|
||||
$this->defaultType = $defaultType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to a simple string
|
||||
* This is used in template filters for example
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the template exists
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(): bool
|
||||
{
|
||||
if ($file = $this->file()) {
|
||||
return file_exists($file);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expected template file extension
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function extension(): string
|
||||
{
|
||||
return 'php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default template type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function defaultType(): string
|
||||
{
|
||||
return $this->defaultType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the place where templates are located
|
||||
* in the site folder and and can be found in extensions
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function store(): string
|
||||
{
|
||||
return 'templates';
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the location of the template file
|
||||
* if it exists.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function file(): string|null
|
||||
{
|
||||
if ($this->hasDefaultType() === true) {
|
||||
try {
|
||||
// Try the default template in the default template directory.
|
||||
return F::realpath($this->root() . '/' . $this->name() . '.' . $this->extension(), $this->root());
|
||||
} catch (Exception) {
|
||||
// ignore errors, continue searching
|
||||
}
|
||||
|
||||
// Look for the default template provided by an extension.
|
||||
$path = App::instance()->extension($this->store(), $this->name());
|
||||
|
||||
if ($path !== null) {
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
||||
$name = $this->name() . '.' . $this->type();
|
||||
|
||||
try {
|
||||
// Try the template with type extension in the default template directory.
|
||||
return F::realpath($this->root() . '/' . $name . '.' . $this->extension(), $this->root());
|
||||
} catch (Exception) {
|
||||
// Look for the template with type extension provided by an extension.
|
||||
// This might be null if the template does not exist.
|
||||
return App::instance()->extension($this->store(), $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the template name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function render(array $data = []): string
|
||||
{
|
||||
return Tpl::load($this->file(), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root to the templates directory
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function root(): string
|
||||
{
|
||||
return App::instance()->root($this->store());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the template type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the template uses the default type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDefaultType(): bool
|
||||
{
|
||||
$type = $this->type();
|
||||
|
||||
return $type === null || $type === $this->defaultType();
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ use Kirby\Filesystem\F;
|
||||
use Kirby\Panel\User as Panel;
|
||||
use Kirby\Session\Session;
|
||||
use Kirby\Toolkit\Str;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* The `$user` object represents a
|
||||
@@ -274,11 +275,11 @@ class User extends ModelWithContent
|
||||
* which will leave it as `null`
|
||||
*
|
||||
* @internal
|
||||
* @param string|null $password
|
||||
* @return string|null
|
||||
*/
|
||||
public static function hashPassword($password): string|null
|
||||
{
|
||||
public static function hashPassword(
|
||||
#[SensitiveParameter]
|
||||
string $password = null
|
||||
): string|null {
|
||||
if ($password !== null) {
|
||||
$password = password_hash($password, PASSWORD_DEFAULT);
|
||||
}
|
||||
@@ -372,8 +373,9 @@ class User extends ModelWithContent
|
||||
*/
|
||||
public function isLastAdmin(): bool
|
||||
{
|
||||
return $this->role()->isAdmin() === true &&
|
||||
$this->kirby()->users()->filter('role', 'admin')->count() <= 1;
|
||||
return
|
||||
$this->role()->isAdmin() === true &&
|
||||
$this->kirby()->users()->filter('role', 'admin')->count() <= 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -410,12 +412,13 @@ class User extends ModelWithContent
|
||||
/**
|
||||
* Logs the user in
|
||||
*
|
||||
* @param string $password
|
||||
* @param \Kirby\Session\Session|array|null $session Session options or session object to set the user in
|
||||
* @return bool
|
||||
*/
|
||||
public function login(string $password, $session = null): bool
|
||||
{
|
||||
public function login(
|
||||
#[SensitiveParameter]
|
||||
string $password,
|
||||
$session = null
|
||||
): bool {
|
||||
$this->validatePassword($password);
|
||||
$this->loginPasswordless($session);
|
||||
|
||||
@@ -750,11 +753,12 @@ class User extends ModelWithContent
|
||||
/**
|
||||
* Sets the user's password hash
|
||||
*
|
||||
* @param string $password|null
|
||||
* @return $this
|
||||
*/
|
||||
protected function setPassword(string $password = null)
|
||||
{
|
||||
protected function setPassword(
|
||||
#[SensitiveParameter]
|
||||
string $password = null
|
||||
): static {
|
||||
$this->password = $password;
|
||||
return $this;
|
||||
}
|
||||
@@ -848,15 +852,14 @@ class User extends ModelWithContent
|
||||
/**
|
||||
* Compares the given password with the stored one
|
||||
*
|
||||
* @param string $password|null
|
||||
* @return bool
|
||||
*
|
||||
* @throws \Kirby\Exception\NotFoundException If the user has no password
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the entered password is not valid
|
||||
* or does not match the user password
|
||||
*/
|
||||
public function validatePassword(string $password = null): bool
|
||||
{
|
||||
public function validatePassword(
|
||||
#[SensitiveParameter]
|
||||
string $password = null
|
||||
): bool {
|
||||
if (empty($this->password()) === true) {
|
||||
throw new NotFoundException(['key' => 'user.password.undefined']);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use Kirby\Filesystem\F;
|
||||
use Kirby\Form\Form;
|
||||
use Kirby\Http\Idn;
|
||||
use Kirby\Toolkit\Str;
|
||||
use SensitiveParameter;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
@@ -102,12 +103,11 @@ trait UserActions
|
||||
|
||||
/**
|
||||
* Changes the user password
|
||||
*
|
||||
* @param string $password
|
||||
* @return static
|
||||
*/
|
||||
public function changePassword(string $password)
|
||||
{
|
||||
public function changePassword(
|
||||
#[SensitiveParameter]
|
||||
string $password
|
||||
): static {
|
||||
return $this->commit('changePassword', ['user' => $this, 'password' => $password], function ($user, $password) {
|
||||
$user = $user->clone([
|
||||
'password' => $password = User::hashPassword($password)
|
||||
@@ -379,12 +379,11 @@ trait UserActions
|
||||
|
||||
/**
|
||||
* Writes the password to disk
|
||||
*
|
||||
* @param string|null $password
|
||||
* @return bool
|
||||
*/
|
||||
protected function writePassword(string $password = null): bool
|
||||
{
|
||||
protected function writePassword(
|
||||
#[SensitiveParameter]
|
||||
string $password = null
|
||||
): bool {
|
||||
return F::write($this->root() . '/.htpasswd', $password);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use Kirby\Exception\LogicException;
|
||||
use Kirby\Exception\PermissionException;
|
||||
use Kirby\Toolkit\Str;
|
||||
use Kirby\Toolkit\V;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Validators for all user actions
|
||||
@@ -83,13 +84,13 @@ class UserRules
|
||||
/**
|
||||
* Validates if the password can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\User $user
|
||||
* @param string $password
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the password
|
||||
*/
|
||||
public static function changePassword(User $user, string $password): bool
|
||||
{
|
||||
public static function changePassword(
|
||||
User $user,
|
||||
#[SensitiveParameter]
|
||||
string $password
|
||||
): bool {
|
||||
if ($user->permissions()->changePassword() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'user.changePassword.permission',
|
||||
@@ -193,12 +194,13 @@ class UserRules
|
||||
}
|
||||
|
||||
// check user permissions (if not on install)
|
||||
if ($user->kirby()->users()->count() > 0) {
|
||||
if ($user->permissions()->create() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'user.create.permission'
|
||||
]);
|
||||
}
|
||||
if (
|
||||
$user->kirby()->users()->count() > 0 &&
|
||||
$user->permissions()->create() !== true
|
||||
) {
|
||||
throw new PermissionException([
|
||||
'key' => 'user.create.permission'
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -332,13 +334,13 @@ class UserRules
|
||||
/**
|
||||
* Validates a password
|
||||
*
|
||||
* @param \Kirby\Cms\User $user
|
||||
* @param string $password
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the password is too short
|
||||
*/
|
||||
public static function validPassword(User $user, string $password): bool
|
||||
{
|
||||
public static function validPassword(
|
||||
User $user,
|
||||
#[SensitiveParameter]
|
||||
string $password
|
||||
): bool {
|
||||
if (Str::length($password ?? null) < 8) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'user.password.invalid',
|
||||
|
||||
Reference in New Issue
Block a user