916 lines
21 KiB
PHP
916 lines
21 KiB
PHP
<?php
|
|
|
|
namespace Kirby\Cms;
|
|
|
|
use Closure;
|
|
use Kirby\Exception\DuplicateException;
|
|
use Kirby\Filesystem\Dir;
|
|
use Kirby\Filesystem\F;
|
|
use Kirby\Filesystem\Mime;
|
|
use Kirby\Form\Field as FormField;
|
|
use Kirby\Image\Image;
|
|
use Kirby\Panel\Panel;
|
|
use Kirby\Text\KirbyTag;
|
|
use Kirby\Toolkit\A;
|
|
use Kirby\Toolkit\Collection as ToolkitCollection;
|
|
use Kirby\Toolkit\V;
|
|
|
|
/**
|
|
* AppPlugins
|
|
*
|
|
* @package Kirby Cms
|
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
|
* @link https://getkirby.com
|
|
* @copyright Bastian Allgeier
|
|
* @license https://getkirby.com/license
|
|
*/
|
|
trait AppPlugins
|
|
{
|
|
/**
|
|
* A list of all registered plugins
|
|
*
|
|
* @var array
|
|
*/
|
|
protected static $plugins = [];
|
|
|
|
/**
|
|
* The extension registry
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $extensions = [
|
|
// load options first to make them available for the rest
|
|
'options' => [],
|
|
|
|
// other plugin types
|
|
'api' => [],
|
|
'areas' => [],
|
|
'authChallenges' => [],
|
|
'blockMethods' => [],
|
|
'blockModels' => [],
|
|
'blocksMethods' => [],
|
|
'blueprints' => [],
|
|
'cacheTypes' => [],
|
|
'collections' => [],
|
|
'components' => [],
|
|
'controllers' => [],
|
|
'collectionFilters' => [],
|
|
'collectionMethods' => [],
|
|
'fieldMethods' => [],
|
|
'fileMethods' => [],
|
|
'fileTypes' => [],
|
|
'filesMethods' => [],
|
|
'fields' => [],
|
|
'hooks' => [],
|
|
'layoutMethods' => [],
|
|
'layoutColumnMethods' => [],
|
|
'layoutsMethods' => [],
|
|
'pages' => [],
|
|
'pageMethods' => [],
|
|
'pagesMethods' => [],
|
|
'pageModels' => [],
|
|
'permissions' => [],
|
|
'routes' => [],
|
|
'sections' => [],
|
|
'siteMethods' => [],
|
|
'snippets' => [],
|
|
'tags' => [],
|
|
'templates' => [],
|
|
'thirdParty' => [],
|
|
'translations' => [],
|
|
'userMethods' => [],
|
|
'userModels' => [],
|
|
'usersMethods' => [],
|
|
'validators' => [],
|
|
];
|
|
|
|
/**
|
|
* Flag when plugins have been loaded
|
|
* to not load them again
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $pluginsAreLoaded = false;
|
|
|
|
/**
|
|
* Register all given extensions
|
|
*
|
|
* @internal
|
|
* @param array $extensions
|
|
* @param \Kirby\Cms\Plugin $plugin|null The plugin which defined those extensions
|
|
* @return array
|
|
*/
|
|
public function extend(array $extensions, Plugin $plugin = null): array
|
|
{
|
|
foreach ($this->extensions as $type => $registered) {
|
|
if (isset($extensions[$type]) === true) {
|
|
$this->{'extend' . $type}($extensions[$type], $plugin);
|
|
}
|
|
}
|
|
|
|
return $this->extensions;
|
|
}
|
|
|
|
/**
|
|
* Registers API extensions
|
|
*
|
|
* @param array|bool $api
|
|
* @return array
|
|
*/
|
|
protected function extendApi($api): array
|
|
{
|
|
if (is_array($api) === true) {
|
|
if (is_a($api['routes'] ?? [], 'Closure') === true) {
|
|
$api['routes'] = $api['routes']($this);
|
|
}
|
|
|
|
return $this->extensions['api'] = A::merge($this->extensions['api'], $api, A::MERGE_APPEND);
|
|
} else {
|
|
return $this->extensions['api'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers additional custom Panel areas
|
|
*
|
|
* @param array $areas
|
|
* @return array
|
|
*/
|
|
protected function extendAreas(array $areas): array
|
|
{
|
|
foreach ($areas as $id => $area) {
|
|
if (isset($this->extensions['areas'][$id]) === false) {
|
|
$this->extensions['areas'][$id] = [];
|
|
}
|
|
|
|
$this->extensions['areas'][$id][] = $area;
|
|
}
|
|
|
|
return $this->extensions['areas'];
|
|
}
|
|
|
|
/**
|
|
* Registers additional authentication challenges
|
|
*
|
|
* @param array $challenges
|
|
* @return array
|
|
*/
|
|
protected function extendAuthChallenges(array $challenges): array
|
|
{
|
|
return $this->extensions['authChallenges'] = Auth::$challenges = array_merge(Auth::$challenges, $challenges);
|
|
}
|
|
|
|
/**
|
|
* Registers additional block methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendBlockMethods(array $methods): array
|
|
{
|
|
return $this->extensions['blockMethods'] = Block::$methods = array_merge(Block::$methods, $methods);
|
|
}
|
|
|
|
/**
|
|
* Registers additional block models
|
|
*
|
|
* @param array $models
|
|
* @return array
|
|
*/
|
|
protected function extendBlockModels(array $models): array
|
|
{
|
|
return $this->extensions['blockModels'] = Block::$models = array_merge(Block::$models, $models);
|
|
}
|
|
|
|
/**
|
|
* Registers additional blocks methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendBlocksMethods(array $methods): array
|
|
{
|
|
return $this->extensions['blockMethods'] = Blocks::$methods = array_merge(Blocks::$methods, $methods);
|
|
}
|
|
|
|
/**
|
|
* Registers additional blueprints
|
|
*
|
|
* @param array $blueprints
|
|
* @return array
|
|
*/
|
|
protected function extendBlueprints(array $blueprints): array
|
|
{
|
|
return $this->extensions['blueprints'] = array_merge($this->extensions['blueprints'], $blueprints);
|
|
}
|
|
|
|
/**
|
|
* Registers additional cache types
|
|
*
|
|
* @param array $cacheTypes
|
|
* @return array
|
|
*/
|
|
protected function extendCacheTypes(array $cacheTypes): array
|
|
{
|
|
return $this->extensions['cacheTypes'] = array_merge($this->extensions['cacheTypes'], $cacheTypes);
|
|
}
|
|
|
|
/**
|
|
* Registers additional collection filters
|
|
*
|
|
* @param array $filters
|
|
* @return array
|
|
*/
|
|
protected function extendCollectionFilters(array $filters): array
|
|
{
|
|
return $this->extensions['collectionFilters'] = ToolkitCollection::$filters = array_merge(ToolkitCollection::$filters, $filters);
|
|
}
|
|
|
|
/**
|
|
* Registers additional collection methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendCollectionMethods(array $methods): array
|
|
{
|
|
return $this->extensions['collectionMethods'] = Collection::$methods = array_merge(Collection::$methods, $methods);
|
|
}
|
|
|
|
/**
|
|
* Registers additional collections
|
|
*
|
|
* @param array $collections
|
|
* @return array
|
|
*/
|
|
protected function extendCollections(array $collections): array
|
|
{
|
|
return $this->extensions['collections'] = array_merge($this->extensions['collections'], $collections);
|
|
}
|
|
|
|
/**
|
|
* Registers core components
|
|
*
|
|
* @param array $components
|
|
* @return array
|
|
*/
|
|
protected function extendComponents(array $components): array
|
|
{
|
|
return $this->extensions['components'] = array_merge($this->extensions['components'], $components);
|
|
}
|
|
|
|
/**
|
|
* Registers additional controllers
|
|
*
|
|
* @param array $controllers
|
|
* @return array
|
|
*/
|
|
protected function extendControllers(array $controllers): array
|
|
{
|
|
return $this->extensions['controllers'] = array_merge($this->extensions['controllers'], $controllers);
|
|
}
|
|
|
|
/**
|
|
* Registers additional file methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendFileMethods(array $methods): array
|
|
{
|
|
return $this->extensions['fileMethods'] = File::$methods = array_merge(File::$methods, $methods);
|
|
}
|
|
|
|
/**
|
|
* Registers additional custom file types and mimes
|
|
*
|
|
* @param array $fileTypes
|
|
* @return array
|
|
*/
|
|
protected function extendFileTypes(array $fileTypes): array
|
|
{
|
|
// normalize array
|
|
foreach ($fileTypes as $ext => $file) {
|
|
$extension = $file['extension'] ?? $ext;
|
|
$type = $file['type'] ?? null;
|
|
$mime = $file['mime'] ?? null;
|
|
$resizable = $file['resizable'] ?? false;
|
|
$viewable = $file['viewable'] ?? false;
|
|
|
|
if (is_string($type) === true) {
|
|
if (isset(F::$types[$type]) === false) {
|
|
F::$types[$type] = [];
|
|
}
|
|
|
|
if (in_array($extension, F::$types[$type]) === false) {
|
|
F::$types[$type][] = $extension;
|
|
}
|
|
}
|
|
|
|
if ($mime !== null) {
|
|
if (array_key_exists($extension, Mime::$types) === true) {
|
|
// if `Mime::$types[$extension]` is not already an array, make it one
|
|
// and append the new MIME type unless it's already in the list
|
|
Mime::$types[$extension] = array_unique(array_merge((array)Mime::$types[$extension], (array)$mime));
|
|
} else {
|
|
Mime::$types[$extension] = $mime;
|
|
}
|
|
}
|
|
|
|
if ($resizable === true && in_array($extension, Image::$resizableTypes) === false) {
|
|
Image::$resizableTypes[] = $extension;
|
|
}
|
|
|
|
if ($viewable === true && in_array($extension, Image::$viewableTypes) === false) {
|
|
Image::$viewableTypes[] = $extension;
|
|
}
|
|
}
|
|
|
|
return $this->extensions['fileTypes'] = [
|
|
'type' => F::$types,
|
|
'mime' => Mime::$types,
|
|
'resizable' => Image::$resizableTypes,
|
|
'viewable' => Image::$viewableTypes
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Registers additional files methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendFilesMethods(array $methods): array
|
|
{
|
|
return $this->extensions['filesMethods'] = Files::$methods = array_merge(Files::$methods, $methods);
|
|
}
|
|
|
|
/**
|
|
* Registers additional field methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendFieldMethods(array $methods): array
|
|
{
|
|
return $this->extensions['fieldMethods'] = Field::$methods = array_merge(Field::$methods, array_change_key_case($methods));
|
|
}
|
|
|
|
/**
|
|
* Registers Panel fields
|
|
*
|
|
* @param array $fields
|
|
* @return array
|
|
*/
|
|
protected function extendFields(array $fields): array
|
|
{
|
|
return $this->extensions['fields'] = FormField::$types = array_merge(FormField::$types, $fields);
|
|
}
|
|
|
|
/**
|
|
* Registers hooks
|
|
*
|
|
* @param array $hooks
|
|
* @return array
|
|
*/
|
|
protected function extendHooks(array $hooks): array
|
|
{
|
|
foreach ($hooks as $name => $callbacks) {
|
|
if (isset($this->extensions['hooks'][$name]) === false) {
|
|
$this->extensions['hooks'][$name] = [];
|
|
}
|
|
|
|
if (is_array($callbacks) === false) {
|
|
$callbacks = [$callbacks];
|
|
}
|
|
|
|
foreach ($callbacks as $callback) {
|
|
$this->extensions['hooks'][$name][] = $callback;
|
|
}
|
|
}
|
|
|
|
return $this->extensions['hooks'];
|
|
}
|
|
|
|
/**
|
|
* Registers markdown component
|
|
*
|
|
* @param Closure $markdown
|
|
* @return Closure
|
|
*/
|
|
protected function extendMarkdown(Closure $markdown)
|
|
{
|
|
return $this->extensions['markdown'] = $markdown;
|
|
}
|
|
|
|
/**
|
|
* Registers additional layout methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendLayoutMethods(array $methods): array
|
|
{
|
|
return $this->extensions['layoutMethods'] = Layout::$methods = array_merge(Layout::$methods, $methods);
|
|
}
|
|
|
|
/**
|
|
* Registers additional layout column methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendLayoutColumnMethods(array $methods): array
|
|
{
|
|
return $this->extensions['layoutColumnMethods'] = LayoutColumn::$methods = array_merge(LayoutColumn::$methods, $methods);
|
|
}
|
|
|
|
/**
|
|
* Registers additional layouts methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendLayoutsMethods(array $methods): array
|
|
{
|
|
return $this->extensions['layoutsMethods'] = Layouts::$methods = array_merge(Layouts::$methods, $methods);
|
|
}
|
|
|
|
/**
|
|
* Registers additional options
|
|
*
|
|
* @param array $options
|
|
* @param \Kirby\Cms\Plugin|null $plugin
|
|
* @return array
|
|
*/
|
|
protected function extendOptions(array $options, Plugin $plugin = null): array
|
|
{
|
|
if ($plugin !== null) {
|
|
$options = [$plugin->prefix() => $options];
|
|
}
|
|
|
|
return $this->extensions['options'] = $this->options = A::merge($options, $this->options, A::MERGE_REPLACE);
|
|
}
|
|
|
|
/**
|
|
* Registers additional page methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendPageMethods(array $methods): array
|
|
{
|
|
return $this->extensions['pageMethods'] = Page::$methods = array_merge(Page::$methods, $methods);
|
|
}
|
|
|
|
/**
|
|
* Registers additional pages methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendPagesMethods(array $methods): array
|
|
{
|
|
return $this->extensions['pagesMethods'] = Pages::$methods = array_merge(Pages::$methods, $methods);
|
|
}
|
|
|
|
/**
|
|
* Registers additional page models
|
|
*
|
|
* @param array $models
|
|
* @return array
|
|
*/
|
|
protected function extendPageModels(array $models): array
|
|
{
|
|
return $this->extensions['pageModels'] = Page::$models = array_merge(Page::$models, $models);
|
|
}
|
|
|
|
/**
|
|
* Registers pages
|
|
*
|
|
* @param array $pages
|
|
* @return array
|
|
*/
|
|
protected function extendPages(array $pages): array
|
|
{
|
|
return $this->extensions['pages'] = array_merge($this->extensions['pages'], $pages);
|
|
}
|
|
|
|
/**
|
|
* Registers additional permissions
|
|
*
|
|
* @param array $permissions
|
|
* @param \Kirby\Cms\Plugin|null $plugin
|
|
* @return array
|
|
*/
|
|
protected function extendPermissions(array $permissions, Plugin $plugin = null): array
|
|
{
|
|
if ($plugin !== null) {
|
|
$permissions = [$plugin->prefix() => $permissions];
|
|
}
|
|
|
|
return $this->extensions['permissions'] = Permissions::$extendedActions = array_merge(Permissions::$extendedActions, $permissions);
|
|
}
|
|
|
|
/**
|
|
* Registers additional routes
|
|
*
|
|
* @param array|\Closure $routes
|
|
* @return array
|
|
*/
|
|
protected function extendRoutes($routes): array
|
|
{
|
|
if (is_a($routes, 'Closure') === true) {
|
|
$routes = $routes($this);
|
|
}
|
|
|
|
return $this->extensions['routes'] = array_merge($this->extensions['routes'], $routes);
|
|
}
|
|
|
|
/**
|
|
* Registers Panel sections
|
|
*
|
|
* @param array $sections
|
|
* @return array
|
|
*/
|
|
protected function extendSections(array $sections): array
|
|
{
|
|
return $this->extensions['sections'] = Section::$types = array_merge(Section::$types, $sections);
|
|
}
|
|
|
|
/**
|
|
* Registers additional site methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendSiteMethods(array $methods): array
|
|
{
|
|
return $this->extensions['siteMethods'] = Site::$methods = array_merge(Site::$methods, $methods);
|
|
}
|
|
|
|
/**
|
|
* Registers SmartyPants component
|
|
*
|
|
* @param \Closure $smartypants
|
|
* @return \Closure
|
|
*/
|
|
protected function extendSmartypants(Closure $smartypants)
|
|
{
|
|
return $this->extensions['smartypants'] = $smartypants;
|
|
}
|
|
|
|
/**
|
|
* Registers additional snippets
|
|
*
|
|
* @param array $snippets
|
|
* @return array
|
|
*/
|
|
protected function extendSnippets(array $snippets): array
|
|
{
|
|
return $this->extensions['snippets'] = array_merge($this->extensions['snippets'], $snippets);
|
|
}
|
|
|
|
/**
|
|
* Registers additional KirbyTags
|
|
*
|
|
* @param array $tags
|
|
* @return array
|
|
*/
|
|
protected function extendTags(array $tags): array
|
|
{
|
|
return $this->extensions['tags'] = KirbyTag::$types = array_merge(KirbyTag::$types, array_change_key_case($tags));
|
|
}
|
|
|
|
/**
|
|
* Registers additional templates
|
|
*
|
|
* @param array $templates
|
|
* @return array
|
|
*/
|
|
protected function extendTemplates(array $templates): array
|
|
{
|
|
return $this->extensions['templates'] = array_merge($this->extensions['templates'], $templates);
|
|
}
|
|
|
|
/**
|
|
* Registers translations
|
|
*
|
|
* @param array $translations
|
|
* @return array
|
|
*/
|
|
protected function extendTranslations(array $translations): array
|
|
{
|
|
return $this->extensions['translations'] = array_replace_recursive($this->extensions['translations'], $translations);
|
|
}
|
|
|
|
/**
|
|
* Add third party extensions to the registry
|
|
* so they can be used as plugins for plugins
|
|
* for example.
|
|
*
|
|
* @param array $extensions
|
|
* @return array
|
|
*/
|
|
protected function extendThirdParty(array $extensions): array
|
|
{
|
|
return $this->extensions['thirdParty'] = array_replace_recursive($this->extensions['thirdParty'], $extensions);
|
|
}
|
|
|
|
/**
|
|
* Registers additional user methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendUserMethods(array $methods): array
|
|
{
|
|
return $this->extensions['userMethods'] = User::$methods = array_merge(User::$methods, $methods);
|
|
}
|
|
|
|
/**
|
|
* Registers additional user models
|
|
*
|
|
* @param array $models
|
|
* @return array
|
|
*/
|
|
protected function extendUserModels(array $models): array
|
|
{
|
|
return $this->extensions['userModels'] = User::$models = array_merge(User::$models, $models);
|
|
}
|
|
|
|
/**
|
|
* Registers additional users methods
|
|
*
|
|
* @param array $methods
|
|
* @return array
|
|
*/
|
|
protected function extendUsersMethods(array $methods): array
|
|
{
|
|
return $this->extensions['usersMethods'] = Users::$methods = array_merge(Users::$methods, $methods);
|
|
}
|
|
|
|
/**
|
|
* Registers additional custom validators
|
|
*
|
|
* @param array $validators
|
|
* @return array
|
|
*/
|
|
protected function extendValidators(array $validators): array
|
|
{
|
|
return $this->extensions['validators'] = V::$validators = array_merge(V::$validators, $validators);
|
|
}
|
|
|
|
/**
|
|
* Returns a given extension by type and name
|
|
*
|
|
* @internal
|
|
* @param string $type i.e. `'hooks'`
|
|
* @param string $name i.e. `'page.delete:before'`
|
|
* @param mixed $fallback
|
|
* @return mixed
|
|
*/
|
|
public function extension(string $type, string $name, $fallback = null)
|
|
{
|
|
return $this->extensions($type)[$name] ?? $fallback;
|
|
}
|
|
|
|
/**
|
|
* Returns the extensions registry
|
|
*
|
|
* @internal
|
|
* @param string|null $type
|
|
* @return array
|
|
*/
|
|
public function extensions(string $type = null)
|
|
{
|
|
if ($type === null) {
|
|
return $this->extensions;
|
|
}
|
|
|
|
return $this->extensions[$type] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Load extensions from site folders.
|
|
* This is only used for models for now, but
|
|
* could be extended later
|
|
*/
|
|
protected function extensionsFromFolders()
|
|
{
|
|
$models = [];
|
|
|
|
foreach (glob($this->root('models') . '/*.php') as $model) {
|
|
$name = F::name($model);
|
|
$class = str_replace(['.', '-', '_'], '', $name) . 'Page';
|
|
|
|
// load the model class
|
|
F::loadOnce($model);
|
|
|
|
if (class_exists($class) === true) {
|
|
$models[$name] = $class;
|
|
}
|
|
}
|
|
|
|
$this->extendPageModels($models);
|
|
}
|
|
|
|
/**
|
|
* Register extensions that could be located in
|
|
* the options array. I.e. hooks and routes can be
|
|
* setup from the config.
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function extensionsFromOptions()
|
|
{
|
|
// register routes and hooks from options
|
|
$this->extend([
|
|
'api' => $this->options['api'] ?? [],
|
|
'routes' => $this->options['routes'] ?? [],
|
|
'hooks' => $this->options['hooks'] ?? []
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Apply all plugin extensions
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function extensionsFromPlugins()
|
|
{
|
|
// register all their extensions
|
|
foreach ($this->plugins() as $plugin) {
|
|
$extends = $plugin->extends();
|
|
|
|
if (empty($extends) === false) {
|
|
$this->extend($extends, $plugin);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply all passed extensions
|
|
*
|
|
* @param array $props
|
|
* @return void
|
|
*/
|
|
protected function extensionsFromProps(array $props)
|
|
{
|
|
$this->extend($props);
|
|
}
|
|
|
|
/**
|
|
* Apply all default extensions
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function extensionsFromSystem()
|
|
{
|
|
// mixins
|
|
FormField::$mixins = $this->core->fieldMixins();
|
|
Section::$mixins = $this->core->sectionMixins();
|
|
|
|
// aliases
|
|
KirbyTag::$aliases = $this->core->kirbyTagAliases();
|
|
Field::$aliases = $this->core->fieldMethodAliases();
|
|
|
|
// blueprint presets
|
|
PageBlueprint::$presets = $this->core->blueprintPresets();
|
|
|
|
$this->extendAuthChallenges($this->core->authChallenges());
|
|
$this->extendCacheTypes($this->core->cacheTypes());
|
|
$this->extendComponents($this->core->components());
|
|
$this->extendBlueprints($this->core->blueprints());
|
|
$this->extendFields($this->core->fields());
|
|
$this->extendFieldMethods($this->core->fieldMethods());
|
|
$this->extendSections($this->core->sections());
|
|
$this->extendSnippets($this->core->snippets());
|
|
$this->extendTags($this->core->kirbyTags());
|
|
$this->extendTemplates($this->core->templates());
|
|
}
|
|
|
|
/**
|
|
* Checks if a native component was extended
|
|
* @since 3.7.0
|
|
*
|
|
* @param string $component
|
|
* @return bool
|
|
*/
|
|
public function isNativeComponent(string $component): bool
|
|
{
|
|
return $this->component($component) === $this->nativeComponent($component);
|
|
}
|
|
|
|
/**
|
|
* Returns the native implementation
|
|
* of a core component
|
|
*
|
|
* @param string $component
|
|
* @return \Closure|false
|
|
*/
|
|
public function nativeComponent(string $component)
|
|
{
|
|
return $this->core->components()[$component] ?? false;
|
|
}
|
|
|
|
/**
|
|
* Kirby plugin factory and getter
|
|
*
|
|
* @param string $name
|
|
* @param array|null $extends If null is passed it will be used as getter. Otherwise as factory.
|
|
* @return \Kirby\Cms\Plugin|null
|
|
* @throws \Kirby\Exception\DuplicateException
|
|
*/
|
|
public static function plugin(string $name, array $extends = 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, $extends);
|
|
$name = $plugin->name();
|
|
|
|
if (isset(static::$plugins[$name]) === true) {
|
|
throw new DuplicateException('The plugin "' . $name . '" has already been registered');
|
|
}
|
|
|
|
return static::$plugins[$name] = $plugin;
|
|
}
|
|
|
|
/**
|
|
* Loads and returns all plugins in the site/plugins directory
|
|
* Loading only happens on the first call.
|
|
*
|
|
* @internal
|
|
* @param array|null $plugins Can be used to overwrite the plugins registry
|
|
* @return array
|
|
*/
|
|
public function plugins(array $plugins = null): array
|
|
{
|
|
// overwrite the existing plugins registry
|
|
if ($plugins !== null) {
|
|
$this->pluginsAreLoaded = true;
|
|
return static::$plugins = $plugins;
|
|
}
|
|
|
|
// don't load plugins twice
|
|
if ($this->pluginsAreLoaded === true) {
|
|
return static::$plugins;
|
|
}
|
|
|
|
// load all plugins from site/plugins
|
|
$this->pluginsLoader();
|
|
|
|
// mark plugins as loaded to stop doing it twice
|
|
$this->pluginsAreLoaded = true;
|
|
return static::$plugins;
|
|
}
|
|
|
|
/**
|
|
* Loads all plugins from site/plugins
|
|
*
|
|
* @return array Array of loaded directories
|
|
*/
|
|
protected function pluginsLoader(): array
|
|
{
|
|
$root = $this->root('plugins');
|
|
$loaded = [];
|
|
|
|
foreach (Dir::read($root) as $dirname) {
|
|
if (in_array(substr($dirname, 0, 1), ['.', '_']) === true) {
|
|
continue;
|
|
}
|
|
|
|
$dir = $root . '/' . $dirname;
|
|
|
|
if (is_dir($dir) !== true) {
|
|
continue;
|
|
}
|
|
|
|
$entry = $dir . '/index.php';
|
|
$script = $dir . '/index.js';
|
|
$styles = $dir . '/index.css';
|
|
|
|
if (is_file($entry) === true) {
|
|
F::loadOnce($entry);
|
|
} elseif (is_file($script) === true || is_file($styles) === true) {
|
|
// if no PHP file is present but an index.js or index.css,
|
|
// 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]);
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
$loaded[] = $dir;
|
|
}
|
|
|
|
return $loaded;
|
|
}
|
|
}
|