297 lines
8.6 KiB
PHP
297 lines
8.6 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace Kirby\Panel;
|
||
|
|
||
|
use Kirby\Exception\Exception;
|
||
|
use Kirby\Exception\InvalidArgumentException;
|
||
|
use Kirby\Filesystem\Dir;
|
||
|
use Kirby\Filesystem\F;
|
||
|
use Kirby\Http\Response;
|
||
|
use Kirby\Http\Uri;
|
||
|
use Kirby\Toolkit\Tpl;
|
||
|
use Throwable;
|
||
|
|
||
|
/**
|
||
|
* The Document is used by the View class to render
|
||
|
* the full Panel HTML document in Fiber calls that
|
||
|
* should not return just JSON objects
|
||
|
* @since 3.6.0
|
||
|
*
|
||
|
* @package Kirby Panel
|
||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||
|
* @link https://getkirby.com
|
||
|
* @copyright Bastian Allgeier
|
||
|
* @license https://getkirby.com/license
|
||
|
*/
|
||
|
class Document
|
||
|
{
|
||
|
/**
|
||
|
* Generates an array with all assets
|
||
|
* that need to be loaded for the panel (js, css, icons)
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public static function assets(): array
|
||
|
{
|
||
|
$kirby = kirby();
|
||
|
$nonce = $kirby->nonce();
|
||
|
|
||
|
// get the assets from the Vite dev server in dev mode;
|
||
|
// dev mode = explicitly enabled in the config AND Vite is running
|
||
|
$dev = $kirby->option('panel.dev', false);
|
||
|
$isDev = $dev !== false && is_file($kirby->roots()->panel() . '/.vite-running') === true;
|
||
|
|
||
|
if ($isDev === true) {
|
||
|
// vite on explicitly configured base URL or port 3000
|
||
|
// of the current Kirby request
|
||
|
if (is_string($dev) === true) {
|
||
|
$url = $dev;
|
||
|
} else {
|
||
|
$url = rtrim($kirby->request()->url([
|
||
|
'port' => 3000,
|
||
|
'path' => null,
|
||
|
'params' => null,
|
||
|
'query' => null
|
||
|
])->toString(), '/');
|
||
|
}
|
||
|
} else {
|
||
|
// vite is not running, use production assets
|
||
|
$url = $kirby->url('media') . '/panel/' . $kirby->versionHash();
|
||
|
}
|
||
|
|
||
|
// fetch all plugins
|
||
|
$plugins = new Plugins();
|
||
|
|
||
|
$assets = [
|
||
|
'css' => [
|
||
|
'index' => $url . '/css/style.css',
|
||
|
'plugins' => $plugins->url('css'),
|
||
|
'custom' => static::customAsset('panel.css'),
|
||
|
],
|
||
|
'icons' => static::favicon($url),
|
||
|
'js' => [
|
||
|
'vendor' => [
|
||
|
'nonce' => $nonce,
|
||
|
'src' => $url . '/js/vendor.js',
|
||
|
'type' => 'module'
|
||
|
],
|
||
|
'pluginloader' => [
|
||
|
'nonce' => $nonce,
|
||
|
'src' => $url . '/js/plugins.js',
|
||
|
'type' => 'module'
|
||
|
],
|
||
|
'plugins' => [
|
||
|
'nonce' => $nonce,
|
||
|
'src' => $plugins->url('js'),
|
||
|
'defer' => true
|
||
|
],
|
||
|
'custom' => [
|
||
|
'nonce' => $nonce,
|
||
|
'src' => static::customAsset('panel.js'),
|
||
|
'type' => 'module'
|
||
|
],
|
||
|
'index' => [
|
||
|
'nonce' => $nonce,
|
||
|
'src' => $url . '/js/index.js',
|
||
|
'type' => 'module'
|
||
|
],
|
||
|
]
|
||
|
];
|
||
|
|
||
|
// during dev mode, add vite client and adapt
|
||
|
// path to `index.js` - vendor and stylesheet
|
||
|
// don't need to be loaded in dev mode
|
||
|
if ($isDev === true) {
|
||
|
$assets['js']['vite'] = [
|
||
|
'nonce' => $nonce,
|
||
|
'src' => $url . '/@vite/client',
|
||
|
'type' => 'module'
|
||
|
];
|
||
|
$assets['js']['index'] = [
|
||
|
'nonce' => $nonce,
|
||
|
'src' => $url . '/src/index.js',
|
||
|
'type' => 'module'
|
||
|
];
|
||
|
|
||
|
unset($assets['css']['index'], $assets['js']['vendor']);
|
||
|
}
|
||
|
|
||
|
// remove missing files
|
||
|
$assets['css'] = array_filter($assets['css']);
|
||
|
$assets['js'] = array_filter(
|
||
|
$assets['js'],
|
||
|
fn ($js) => empty($js['src']) === false
|
||
|
);
|
||
|
|
||
|
return $assets;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check for a custom asset file from the
|
||
|
* config (e.g. panel.css or panel.js)
|
||
|
* @since 3.6.2
|
||
|
*
|
||
|
* @param string $option asset option name
|
||
|
* @return string|null
|
||
|
*/
|
||
|
public static function customAsset(string $option): ?string
|
||
|
{
|
||
|
if ($path = kirby()->option($option)) {
|
||
|
$asset = asset($path);
|
||
|
|
||
|
if ($asset->exists() === true) {
|
||
|
return $asset->url() . '?' . $asset->modified();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @deprecated 3.7.0 Use `Document::customAsset('panel.css)` instead
|
||
|
* @todo add deprecation warning in 3.7.0, remove in 3.8.0
|
||
|
*/
|
||
|
public static function customCss(): ?string
|
||
|
{
|
||
|
return static::customAsset('panel.css');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @deprecated 3.7.0 Use `Document::customAsset('panel.js)` instead
|
||
|
* @todo add deprecation warning in 3.7.0, remove in 3.8.0
|
||
|
*/
|
||
|
public static function customJs(): ?string
|
||
|
{
|
||
|
return static::customAsset('panel.js');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns array of favion icons
|
||
|
* based on config option
|
||
|
* @since 3.6.2
|
||
|
*
|
||
|
* @param string $url URL prefix for default icons
|
||
|
* @return array
|
||
|
*/
|
||
|
public static function favicon(string $url = ''): array
|
||
|
{
|
||
|
$kirby = kirby();
|
||
|
$icons = $kirby->option('panel.favicon', [
|
||
|
'apple-touch-icon' => [
|
||
|
'type' => 'image/png',
|
||
|
'url' => $url . '/apple-touch-icon.png',
|
||
|
],
|
||
|
'shortcut icon' => [
|
||
|
'type' => 'image/svg+xml',
|
||
|
'url' => $url . '/favicon.svg',
|
||
|
],
|
||
|
'alternate icon' => [
|
||
|
'type' => 'image/png',
|
||
|
'url' => $url . '/favicon.png',
|
||
|
]
|
||
|
]);
|
||
|
|
||
|
if (is_array($icons) === true) {
|
||
|
return $icons;
|
||
|
}
|
||
|
|
||
|
// make sure to convert favicon string to array
|
||
|
if (is_string($icons) === true) {
|
||
|
return [
|
||
|
'shortcut icon' => [
|
||
|
'type' => F::mime($icons),
|
||
|
'url' => $icons,
|
||
|
]
|
||
|
];
|
||
|
}
|
||
|
|
||
|
throw new InvalidArgumentException('Invalid panel.favicon option');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Load the SVG icon sprite
|
||
|
* This will be injected in the
|
||
|
* initial HTML document for the Panel
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function icons(): string
|
||
|
{
|
||
|
return F::read(kirby()->root('kirby') . '/panel/dist/img/icons.svg');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Links all dist files in the media folder
|
||
|
* and returns the link to the requested asset
|
||
|
*
|
||
|
* @return bool
|
||
|
* @throws \Kirby\Exception\Exception If Panel assets could not be moved to the public directory
|
||
|
*/
|
||
|
public static function link(): bool
|
||
|
{
|
||
|
$kirby = kirby();
|
||
|
$mediaRoot = $kirby->root('media') . '/panel';
|
||
|
$panelRoot = $kirby->root('panel') . '/dist';
|
||
|
$versionHash = $kirby->versionHash();
|
||
|
$versionRoot = $mediaRoot . '/' . $versionHash;
|
||
|
|
||
|
// check if the version already exists
|
||
|
if (is_dir($versionRoot) === true) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// delete the panel folder and all previous versions
|
||
|
Dir::remove($mediaRoot);
|
||
|
|
||
|
// recreate the panel folder
|
||
|
Dir::make($mediaRoot, true);
|
||
|
|
||
|
// copy assets to the dist folder
|
||
|
if (Dir::copy($panelRoot, $versionRoot) !== true) {
|
||
|
throw new Exception('Panel assets could not be linked');
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Renders the panel document
|
||
|
*
|
||
|
* @param array $fiber
|
||
|
* @return \Kirby\Http\Response
|
||
|
*/
|
||
|
public static function response(array $fiber)
|
||
|
{
|
||
|
$kirby = kirby();
|
||
|
|
||
|
// Full HTML response
|
||
|
// @codeCoverageIgnoreStart
|
||
|
try {
|
||
|
if (static::link() === true) {
|
||
|
usleep(1);
|
||
|
go($kirby->url('index') . '/' . $kirby->path());
|
||
|
}
|
||
|
} catch (Throwable $e) {
|
||
|
die('The Panel assets cannot be installed properly. ' . $e->getMessage());
|
||
|
}
|
||
|
// @codeCoverageIgnoreEnd
|
||
|
|
||
|
// get the uri object for the panel url
|
||
|
$uri = new Uri($url = $kirby->url('panel'));
|
||
|
|
||
|
// proper response code
|
||
|
$code = $fiber['$view']['code'] ?? 200;
|
||
|
|
||
|
// load the main Panel view template
|
||
|
$body = Tpl::load($kirby->root('kirby') . '/views/panel.php', [
|
||
|
'assets' => static::assets(),
|
||
|
'icons' => static::icons(),
|
||
|
'nonce' => $kirby->nonce(),
|
||
|
'fiber' => $fiber,
|
||
|
'panelUrl' => $uri->path()->toString(true) . '/',
|
||
|
]);
|
||
|
|
||
|
return new Response($body, 'text/html', $code);
|
||
|
}
|
||
|
}
|