Upgrade to 4.3.0
This commit is contained in:
@@ -30,6 +30,7 @@ use Kirby\Toolkit\A;
|
||||
use Kirby\Toolkit\Config;
|
||||
use Kirby\Toolkit\Controller;
|
||||
use Kirby\Toolkit\LazyValue;
|
||||
use Kirby\Toolkit\Locale;
|
||||
use Kirby\Toolkit\Str;
|
||||
use Kirby\Uuid\Uuid;
|
||||
use Throwable;
|
||||
@@ -170,7 +171,7 @@ class App
|
||||
'roots' => $this->roots(),
|
||||
'site' => $this->site(),
|
||||
'urls' => $this->urls(),
|
||||
'version' => $this->version(),
|
||||
'version' => static::version(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -255,7 +256,7 @@ class App
|
||||
foreach ($this->options as $key => $value) {
|
||||
// detect option keys with the `vendor.plugin.option` format
|
||||
if (preg_match('/^([a-z0-9-]+\.[a-z0-9-]+)\.(.*)$/i', $key, $matches) === 1) {
|
||||
list(, $plugin, $option) = $matches;
|
||||
[, $plugin, $option] = $matches;
|
||||
|
||||
// verify that it's really a plugin option
|
||||
if (isset(static::$plugins[str_replace('.', '/', $plugin)]) !== true) {
|
||||
@@ -538,6 +539,14 @@ class App
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current language, if set by `static::setCurrentLanguage`
|
||||
*/
|
||||
public function currentLanguage(): Language|null
|
||||
{
|
||||
return $this->language ??= $this->defaultLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default language object
|
||||
*/
|
||||
@@ -623,7 +632,7 @@ class App
|
||||
return Uuid::for($path, $parent?->files())->model();
|
||||
}
|
||||
|
||||
$parent = $parent ?? $this->site();
|
||||
$parent ??= $this->site();
|
||||
$id = dirname($path);
|
||||
$filename = basename($path);
|
||||
|
||||
@@ -879,7 +888,8 @@ class App
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current language
|
||||
* Returns the language by code or shortcut (`default`, `current`).
|
||||
* Passing `null` is an alias for passing `current`
|
||||
*/
|
||||
public function language(string $code = null): Language|null
|
||||
{
|
||||
@@ -887,19 +897,11 @@ class App
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($code === 'default') {
|
||||
return $this->defaultLanguage();
|
||||
}
|
||||
|
||||
// if requesting a non-default language,
|
||||
// find it but don't cache it
|
||||
if ($code !== null) {
|
||||
return $this->languages()->find($code);
|
||||
}
|
||||
|
||||
// otherwise return language set by `AppTranslation::setCurrentLanguage`
|
||||
// or default language
|
||||
return $this->language ??= $this->defaultLanguage();
|
||||
return match ($code ?? 'current') {
|
||||
'default' => $this->defaultLanguage(),
|
||||
'current' => $this->currentLanguage(),
|
||||
default => $this->languages()->find($code)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1141,7 +1143,7 @@ class App
|
||||
return null;
|
||||
}
|
||||
|
||||
$parent = $parent ?? $this->site();
|
||||
$parent ??= $this->site();
|
||||
|
||||
if ($page = $parent->find($id)) {
|
||||
/**
|
||||
@@ -1212,7 +1214,7 @@ class App
|
||||
* @internal
|
||||
* @throws \Kirby\Exception\NotFoundException if the home page cannot be found
|
||||
*/
|
||||
public function resolve(string $path = null, string $language = null): mixed
|
||||
public function resolve(string|null $path = null, string|null $language = null): mixed
|
||||
{
|
||||
// set the current translation
|
||||
$this->setCurrentTranslation($language);
|
||||
@@ -1410,6 +1412,30 @@ class App
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and set the current language if it exists
|
||||
* Otherwise fall back to the default language
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function setCurrentLanguage(
|
||||
string|null $languageCode = null
|
||||
): Language|null {
|
||||
if ($this->multilang() === false) {
|
||||
Locale::set($this->option('locale', 'en_US.utf-8'));
|
||||
return $this->language = null;
|
||||
}
|
||||
|
||||
$this->language = $this->language($languageCode) ?? $this->defaultLanguage();
|
||||
|
||||
Locale::set($this->language->locale());
|
||||
|
||||
// add language slug rules to Str class
|
||||
Str::$language = $this->language->rules();
|
||||
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create your own set of languages
|
||||
*
|
||||
|
||||
@@ -713,17 +713,25 @@ trait AppPlugins
|
||||
*/
|
||||
public static function plugin(
|
||||
string $name,
|
||||
array $extends = null
|
||||
array $extends = null,
|
||||
array $info = [],
|
||||
string|null $root = null,
|
||||
string|null $version = null
|
||||
): PLugin|null {
|
||||
if ($extends === null) {
|
||||
return static::$plugins[$name] ?? null;
|
||||
}
|
||||
|
||||
// get the correct root for the plugin
|
||||
$extends['root'] = $extends['root'] ?? dirname(debug_backtrace()[0]['file']);
|
||||
$plugin = new Plugin(
|
||||
name: $name,
|
||||
extends: $extends,
|
||||
info: $info,
|
||||
// TODO: Remove fallback to $extends in v7
|
||||
root: $root ?? $extends['root'] ?? dirname(debug_backtrace()[0]['file']),
|
||||
version: $version
|
||||
);
|
||||
|
||||
$plugin = new Plugin($name, $extends);
|
||||
$name = $plugin->name();
|
||||
$name = $plugin->name();
|
||||
|
||||
if (isset(static::$plugins[$name]) === true) {
|
||||
throw new DuplicateException('The plugin "' . $name . '" has already been registered');
|
||||
@@ -792,7 +800,11 @@ trait AppPlugins
|
||||
// register as anonymous plugin (without actual extensions)
|
||||
// to be picked up by the Panel\Document class when
|
||||
// rendering the Panel view
|
||||
static::plugin('plugins/' . $dirname, ['root' => $dir]);
|
||||
static::plugin(
|
||||
name: 'plugins/' . $dirname,
|
||||
extends: [],
|
||||
root: $dir
|
||||
);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Kirby\Cms;
|
||||
|
||||
use Kirby\Toolkit\I18n;
|
||||
use Kirby\Toolkit\Locale;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
/**
|
||||
@@ -104,33 +103,6 @@ trait AppTranslations
|
||||
return $this->option('panel.language', $defaultCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and set the current language if it exists
|
||||
* Otherwise fall back to the default language
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function setCurrentLanguage(
|
||||
string $languageCode = null
|
||||
): Language|null {
|
||||
if ($this->multilang() === false) {
|
||||
Locale::set($this->option('locale', 'en_US.utf-8'));
|
||||
return $this->language = null;
|
||||
}
|
||||
|
||||
$this->language = $this->language($languageCode);
|
||||
$this->language ??= $this->defaultLanguage();
|
||||
|
||||
if ($this->language) {
|
||||
Locale::set($this->language->locale());
|
||||
}
|
||||
|
||||
// add language slug rules to Str class
|
||||
Str::$language = $this->language->rules();
|
||||
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current translation
|
||||
*
|
||||
@@ -148,7 +120,7 @@ trait AppTranslations
|
||||
*/
|
||||
public function translation(string|null $locale = null): Translation
|
||||
{
|
||||
$locale = $locale ?? I18n::locale();
|
||||
$locale ??= I18n::locale();
|
||||
$locale = basename($locale);
|
||||
|
||||
// prefer loading them from the translations collection
|
||||
|
||||
@@ -575,8 +575,8 @@ class Auth
|
||||
}
|
||||
|
||||
// ensure that the category arrays are defined
|
||||
$log['by-ip'] = $log['by-ip'] ?? [];
|
||||
$log['by-email'] = $log['by-email'] ?? [];
|
||||
$log['by-ip'] ??= [];
|
||||
$log['by-email'] ??= [];
|
||||
|
||||
// remove all elements on the top level with different keys (old structure)
|
||||
$log = array_intersect_key($log, array_flip(['by-ip', 'by-email']));
|
||||
|
||||
@@ -65,7 +65,7 @@ class Blueprint
|
||||
unset($props['model']);
|
||||
|
||||
// extend the blueprint in general
|
||||
$props = $this->extend($props);
|
||||
$props = static::extend($props);
|
||||
|
||||
// apply any blueprint preset
|
||||
$props = $this->preset($props);
|
||||
@@ -652,7 +652,7 @@ class Blueprint
|
||||
}
|
||||
|
||||
// extend options if possible
|
||||
$options = $this->extend($options);
|
||||
$options = static::extend($options);
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
$alias = $aliases[$key] ?? null;
|
||||
@@ -686,7 +686,7 @@ class Blueprint
|
||||
}
|
||||
|
||||
// inject all section extensions
|
||||
$sectionProps = $this->extend($sectionProps);
|
||||
$sectionProps = static::extend($sectionProps);
|
||||
|
||||
$sections[$sectionName] = $sectionProps = [
|
||||
...$sectionProps,
|
||||
@@ -699,14 +699,14 @@ class Blueprint
|
||||
'name' => $sectionName,
|
||||
'label' => 'Invalid section type for section "' . $sectionName . '"',
|
||||
'type' => 'info',
|
||||
'text' => 'The following section types are available: ' . $this->helpList(array_keys(Section::$types))
|
||||
'text' => 'The following section types are available: ' . static::helpList(array_keys(Section::$types))
|
||||
];
|
||||
} elseif (isset(Section::$types[$type]) === false) {
|
||||
$sections[$sectionName] = [
|
||||
'name' => $sectionName,
|
||||
'label' => 'Invalid section type ("' . $type . '")',
|
||||
'type' => 'info',
|
||||
'text' => 'The following section types are available: ' . $this->helpList(array_keys(Section::$types))
|
||||
'text' => 'The following section types are available: ' . static::helpList(array_keys(Section::$types))
|
||||
];
|
||||
}
|
||||
|
||||
@@ -764,7 +764,7 @@ class Blueprint
|
||||
}
|
||||
|
||||
// inject all tab extensions
|
||||
$tabProps = $this->extend($tabProps);
|
||||
$tabProps = static::extend($tabProps);
|
||||
|
||||
// inject a preset if available
|
||||
$tabProps = $this->preset($tabProps);
|
||||
|
||||
@@ -47,7 +47,9 @@ trait FileActions
|
||||
string|null $extension = null
|
||||
): static {
|
||||
if ($sanitize === true) {
|
||||
$name = F::safeName($name);
|
||||
// sanitize the basename part only
|
||||
// as the extension isn't included in $name
|
||||
$name = F::safeBasename($name, false);
|
||||
}
|
||||
|
||||
// if no extension is passed, make sure to maintain current one
|
||||
@@ -139,8 +141,8 @@ trait FileActions
|
||||
|
||||
// rename and/or resize the file if configured by new blueprint
|
||||
$create = $file->blueprint()->create();
|
||||
$file = $file->manipulate($create);
|
||||
$file = $file->changeExtension($file, $create['format'] ?? null);
|
||||
$file->manipulate($create);
|
||||
|
||||
return $file;
|
||||
});
|
||||
@@ -266,7 +268,6 @@ trait FileActions
|
||||
// we need to already rename it so that the correct file rules
|
||||
// are applied
|
||||
$create = $file->blueprint()->create();
|
||||
$file = $file->changeExtension($file, $create['format'] ?? null);
|
||||
|
||||
// run the hook
|
||||
$arguments = compact('file', 'upload');
|
||||
@@ -284,6 +285,7 @@ trait FileActions
|
||||
|
||||
// resize the file on upload if configured
|
||||
$file = $file->manipulate($create);
|
||||
$file = $file->changeExtension($file, $create['format'] ?? null);
|
||||
|
||||
// store the content if necessary
|
||||
// (always create files in the default language)
|
||||
@@ -384,8 +386,8 @@ trait FileActions
|
||||
|
||||
// apply the resizing/crop options from the blueprint
|
||||
$create = $file->blueprint()->create();
|
||||
$file = $file->changeExtension($file, $create['format'] ?? null);
|
||||
$file = $file->manipulate($create);
|
||||
$file = $file->changeExtension($file, $create['format'] ?? null);
|
||||
|
||||
// return a fresh clone
|
||||
return $file->clone();
|
||||
|
||||
@@ -50,7 +50,7 @@ class FileVersion
|
||||
|
||||
// content fields
|
||||
if ($this->original() instanceof File) {
|
||||
return $this->original()->content()->get($method, $arguments);
|
||||
return $this->original()->content()->get($method);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,14 +29,20 @@ class Helpers
|
||||
* ```
|
||||
*/
|
||||
public static $deprecations = [
|
||||
// The internal `$model->contentFile*()` methods have been deprecated
|
||||
'model-content-file' => true,
|
||||
|
||||
// Passing an `info` array inside the `extends` array
|
||||
// has been deprecated. Pass the individual entries (e.g. root, version)
|
||||
// directly as named arguments.
|
||||
// TODO: switch to true in v6
|
||||
'plugin-extends-root' => false,
|
||||
|
||||
// Passing a single space as value to `Xml::attr()` has been
|
||||
// deprecated. In a future version, passing a single space won't
|
||||
// render an empty value anymore but a single space.
|
||||
// To render an empty value, please pass an empty string.
|
||||
'xml-attr-single-space' => true,
|
||||
|
||||
// The internal `$model->contentFile*()` methods have been deprecated
|
||||
'model-content-file' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,7 +42,7 @@ class LanguageRules
|
||||
/**
|
||||
* Validates if the language can be updated
|
||||
*/
|
||||
public static function update(Language $language)
|
||||
public static function update(Language $language): void
|
||||
{
|
||||
static::validLanguageCode($language);
|
||||
static::validLanguageName($language);
|
||||
|
||||
@@ -57,7 +57,12 @@ class Media
|
||||
}
|
||||
|
||||
// try to generate a thumb for the file
|
||||
return static::thumb($model, $hash, $filename);
|
||||
try {
|
||||
return static::thumb($model, $hash, $filename);
|
||||
} catch (NotFoundException) {
|
||||
// render the error page if there is no job for this filename
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,7 +46,7 @@ abstract class ModelWithContent implements Identifiable
|
||||
public static App $kirby;
|
||||
protected Site|null $site;
|
||||
protected ContentStorage $storage;
|
||||
public Collection|null $translations;
|
||||
public Collection|null $translations = null;
|
||||
|
||||
/**
|
||||
* Store values used to initilaize object
|
||||
|
||||
@@ -22,7 +22,7 @@ class PagePicker extends Picker
|
||||
// remove once our implementation is better
|
||||
protected Pages|null $items = null;
|
||||
protected Pages|null $itemsForQuery = null;
|
||||
protected Page|Site|null $parent;
|
||||
protected Page|Site|null $parent = null;
|
||||
|
||||
/**
|
||||
* Extends the basic defaults
|
||||
|
||||
@@ -26,12 +26,6 @@ use Throwable;
|
||||
class Plugin
|
||||
{
|
||||
protected PluginAssets $assets;
|
||||
protected array $extends;
|
||||
protected string $name;
|
||||
protected string $root;
|
||||
|
||||
// caches
|
||||
protected array|null $info = null;
|
||||
protected UpdateStatus|null $updateStatus = null;
|
||||
|
||||
/**
|
||||
@@ -40,16 +34,44 @@ class Plugin
|
||||
*
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the plugin name has an invalid format
|
||||
*/
|
||||
public function __construct(string $name, array $extends = [])
|
||||
{
|
||||
public function __construct(
|
||||
protected string $name,
|
||||
protected array $extends = [],
|
||||
protected array $info = [],
|
||||
protected string|null $root = null,
|
||||
protected string|null $version = null,
|
||||
) {
|
||||
static::validateName($name);
|
||||
|
||||
$this->name = $name;
|
||||
$this->extends = $extends;
|
||||
$this->root = $extends['root'] ?? dirname(debug_backtrace()[0]['file']);
|
||||
$this->info = empty($extends['info']) === false && is_array($extends['info']) ? $extends['info'] : null;
|
||||
// TODO: Remove in v7
|
||||
if ($root = $extends['root'] ?? null) {
|
||||
Helpers::deprecated('Plugin "' . $name . '": Passing the `root` inside the `extends` array has been deprecated. Pass it directly as named argument `root`.', 'plugin-extends-root');
|
||||
$this->root ??= $root;
|
||||
unset($this->extends['root']);
|
||||
}
|
||||
|
||||
unset($this->extends['root'], $this->extends['info']);
|
||||
$this->root ??= dirname(debug_backtrace()[0]['file']);
|
||||
|
||||
// TODO: Remove in v7
|
||||
if ($info = $extends['info'] ?? null) {
|
||||
Helpers::deprecated('Plugin "' . $name . '": Passing an `info` array inside the `extends` array has been deprecated. Pass the individual entries directly as named `info` argument.', 'plugin-extends-root');
|
||||
|
||||
if (empty($info) === false && is_array($info) === true) {
|
||||
$this->info = [...$info, ...$this->info];
|
||||
}
|
||||
|
||||
unset($this->extends['info']);
|
||||
}
|
||||
|
||||
// read composer.json and use as info fallback
|
||||
try {
|
||||
$info = Data::read($this->manifest());
|
||||
} catch (Exception) {
|
||||
// there is no manifest file or it is invalid
|
||||
$info = [];
|
||||
}
|
||||
|
||||
$this->info = [...$info, ...$this->info];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,22 +139,11 @@ class Plugin
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw data from composer.json
|
||||
* Returns the info data (from composer.json)
|
||||
*/
|
||||
public function info(): array
|
||||
{
|
||||
if (is_array($this->info) === true) {
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
try {
|
||||
$info = Data::read($this->manifest());
|
||||
} catch (Exception) {
|
||||
// there is no manifest file or it is invalid
|
||||
$info = [];
|
||||
}
|
||||
|
||||
return $this->info = $info;
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -295,17 +306,20 @@ class Plugin
|
||||
*/
|
||||
public function version(): string|null
|
||||
{
|
||||
$composerName = $this->info()['name'] ?? null;
|
||||
$version = $this->info()['version'] ?? null;
|
||||
$name = $this->info()['name'] ?? null;
|
||||
|
||||
try {
|
||||
// if plugin doesn't have version key in composer.json file
|
||||
// try to get version from "vendor/composer/installed.php"
|
||||
$version ??= InstalledVersions::getPrettyVersion($composerName);
|
||||
// try to get version from "vendor/composer/installed.php",
|
||||
// this is the most reliable source for the version
|
||||
$version = InstalledVersions::getPrettyVersion($name);
|
||||
} catch (Throwable) {
|
||||
return null;
|
||||
$version = null;
|
||||
}
|
||||
|
||||
// fallback to the version provided in the plugin's index.php: as named
|
||||
// argument, entry in the info array or from the composer.json file
|
||||
$version ??= $this->version ?? $this->info()['version'] ?? null;
|
||||
|
||||
if (
|
||||
is_string($version) !== true ||
|
||||
$version === '' ||
|
||||
|
||||
@@ -162,6 +162,24 @@ class System
|
||||
->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with relevant system information
|
||||
* used for debugging
|
||||
* @since 4.3.0
|
||||
*/
|
||||
public function info(): array
|
||||
{
|
||||
return [
|
||||
'kirby' => $this->app->version(),
|
||||
'php' => phpversion(),
|
||||
'server' => $this->serverSoftware(),
|
||||
'license' => $this->license()->label(),
|
||||
'languages' => $this->app->languages()->values(
|
||||
fn ($lang) => $lang->code()
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the most important folders
|
||||
* if they don't exist yet
|
||||
@@ -379,32 +397,12 @@ class System
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a valid server environment
|
||||
*/
|
||||
public function server(): bool
|
||||
{
|
||||
return $this->serverSoftware() !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the detected server software
|
||||
*/
|
||||
public function serverSoftware(): string|null
|
||||
public function serverSoftware(): string
|
||||
{
|
||||
$servers = $this->app->option('servers', [
|
||||
'apache',
|
||||
'caddy',
|
||||
'litespeed',
|
||||
'nginx',
|
||||
'php'
|
||||
]);
|
||||
|
||||
$software = $this->app->environment()->get('SERVER_SOFTWARE', '');
|
||||
|
||||
preg_match('!(' . implode('|', A::wrap($servers)) . ')!i', $software, $matches);
|
||||
|
||||
return $matches[0] ?? null;
|
||||
return $this->app->environment()->get('SERVER_SOFTWARE', '–');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -421,14 +419,13 @@ class System
|
||||
public function status(): array
|
||||
{
|
||||
return [
|
||||
'accounts' => $this->accounts(),
|
||||
'content' => $this->content(),
|
||||
'curl' => $this->curl(),
|
||||
'sessions' => $this->sessions(),
|
||||
'mbstring' => $this->mbstring(),
|
||||
'media' => $this->media(),
|
||||
'php' => $this->php(),
|
||||
'server' => $this->server(),
|
||||
'accounts' => $this->accounts(),
|
||||
'content' => $this->content(),
|
||||
'curl' => $this->curl(),
|
||||
'sessions' => $this->sessions(),
|
||||
'mbstring' => $this->mbstring(),
|
||||
'media' => $this->media(),
|
||||
'php' => $this->php()
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -392,6 +392,46 @@ class UpdateStatus
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the maximum possible major update
|
||||
* that is included with the current license
|
||||
*
|
||||
* @return string|null Version number of the update or
|
||||
* `null` if no free update is possible
|
||||
*/
|
||||
protected function findMaximumFreeUpdate(): string|null
|
||||
{
|
||||
// get the timestamp of included updates
|
||||
$renewal = $this->app->system()->license()->renewal();
|
||||
|
||||
if ($renewal === null || $this->data === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($this->data['versions'] ?? [] as $entry) {
|
||||
$initialRelease = $entry['initialRelease'] ?? null;
|
||||
$latest = $entry['latest'] ?? '';
|
||||
|
||||
// skip entries of irrelevant releases
|
||||
if (
|
||||
is_string($initialRelease) !== true ||
|
||||
version_compare($latest, $this->currentVersion, '<=') === true
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$timestamp = strtotime($initialRelease);
|
||||
|
||||
// update is free if the initial release was before the
|
||||
// license renewal date
|
||||
if (is_int($timestamp) === true && $timestamp < $renewal) {
|
||||
return $latest;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the minimum possible security update
|
||||
* to fix all known vulnerabilities
|
||||
@@ -655,7 +695,7 @@ class UpdateStatus
|
||||
];
|
||||
}
|
||||
|
||||
// check if free updates are possible from the current version
|
||||
// check if updates within the same major version are possible
|
||||
$latest = $versionEntry['latest'] ?? null;
|
||||
if (is_string($latest) === true && $latest !== $this->currentVersion) {
|
||||
return $this->targetData = [
|
||||
@@ -665,6 +705,19 @@ class UpdateStatus
|
||||
];
|
||||
}
|
||||
|
||||
// check if the license includes updates to a newer major version
|
||||
if ($version = $this->findMaximumFreeUpdate()) {
|
||||
// extract the part before the first dot
|
||||
// to find the major release page URL
|
||||
preg_match('/^(\w+)\./', $version, $matches);
|
||||
|
||||
return $this->targetData = [
|
||||
'status' => 'update',
|
||||
'url' => $this->urlFor($matches[1] . '.0', 'changes'),
|
||||
'version' => $version
|
||||
];
|
||||
}
|
||||
|
||||
// no free update is possible, but we are not on the latest version,
|
||||
// so the overall latest version must be an upgrade
|
||||
return $this->targetData = [
|
||||
|
||||
Reference in New Issue
Block a user