2014-05-14 23:24:20 +10:00
|
|
|
<?php namespace Cms\Classes;
|
|
|
|
|
|
|
|
use Str;
|
|
|
|
use System\Classes\PluginManager;
|
2015-01-28 18:03:35 +11:00
|
|
|
use SystemException;
|
2016-02-09 11:42:14 +01:00
|
|
|
use Illuminate\Support\Facades\App;
|
2014-05-14 23:24:20 +10:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Component manager
|
|
|
|
*
|
|
|
|
* @package october\cms
|
|
|
|
* @author Alexey Bobkov, Samuel Georges
|
|
|
|
*/
|
|
|
|
class ComponentManager
|
|
|
|
{
|
|
|
|
use \October\Rain\Support\Traits\Singleton;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array Cache of registration callbacks.
|
|
|
|
*/
|
2014-08-01 18:20:55 +10:00
|
|
|
protected $callbacks = [];
|
2014-05-14 23:24:20 +10:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array An array where keys are codes and values are class names.
|
|
|
|
*/
|
|
|
|
protected $codeMap;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array An array where keys are class names and values are codes.
|
|
|
|
*/
|
|
|
|
protected $classMap;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array An array containing references to a corresponding plugin for each component class.
|
|
|
|
*/
|
|
|
|
protected $pluginMap;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array A cached array of component details.
|
|
|
|
*/
|
|
|
|
protected $detailsCache;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scans each plugin an loads it's components.
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
protected function loadComponents()
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Load module components
|
|
|
|
*/
|
|
|
|
foreach ($this->callbacks as $callback) {
|
|
|
|
$callback($this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load plugin components
|
|
|
|
*/
|
|
|
|
$pluginManager = PluginManager::instance();
|
|
|
|
$plugins = $pluginManager->getPlugins();
|
|
|
|
|
|
|
|
foreach ($plugins as $plugin) {
|
|
|
|
$components = $plugin->registerComponents();
|
2014-10-11 01:22:03 +02:00
|
|
|
if (!is_array($components)) {
|
2014-05-14 23:24:20 +10:00
|
|
|
continue;
|
2014-10-11 01:22:03 +02:00
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
|
|
|
|
foreach ($components as $className => $code) {
|
|
|
|
$this->registerComponent($className, $code, $plugin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-08-15 16:14:54 +01:00
|
|
|
* Manually registers a component for consideration. Usage:
|
2017-03-16 17:08:20 +11:00
|
|
|
*
|
2019-08-15 16:14:54 +01:00
|
|
|
* ComponentManager::registerComponents(function ($manager) {
|
2017-03-16 17:08:20 +11:00
|
|
|
* $manager->registerComponent('October\Demo\Components\Test', 'testComponent');
|
|
|
|
* });
|
2014-05-14 23:24:20 +10:00
|
|
|
*
|
2016-03-04 16:50:58 +01:00
|
|
|
* @param callable $definitions
|
2014-05-14 23:24:20 +10:00
|
|
|
* @return array Array values are class names.
|
|
|
|
*/
|
|
|
|
public function registerComponents(callable $definitions)
|
|
|
|
{
|
|
|
|
$this->callbacks[] = $definitions;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Registers a single component.
|
|
|
|
*/
|
|
|
|
public function registerComponent($className, $code = null, $plugin = null)
|
|
|
|
{
|
2014-10-11 01:22:03 +02:00
|
|
|
if (!$this->classMap) {
|
2014-05-14 23:24:20 +10:00
|
|
|
$this->classMap = [];
|
2014-10-11 01:22:03 +02:00
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
|
2014-10-11 01:22:03 +02:00
|
|
|
if (!$this->codeMap) {
|
2014-05-14 23:24:20 +10:00
|
|
|
$this->codeMap = [];
|
2014-10-11 01:22:03 +02:00
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
|
2014-10-11 01:22:03 +02:00
|
|
|
if (!$code) {
|
2014-05-14 23:24:20 +10:00
|
|
|
$code = Str::getClassId($className);
|
2014-10-11 01:22:03 +02:00
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
|
2017-03-16 07:00:39 +11:00
|
|
|
if ($code == 'viewBag' && $className != 'Cms\Components\ViewBag') {
|
2014-10-11 01:22:03 +02:00
|
|
|
throw new SystemException(sprintf(
|
|
|
|
'The component code viewBag is reserved. Please use another code for the component class %s.',
|
|
|
|
$className
|
|
|
|
));
|
|
|
|
}
|
2014-08-06 22:42:09 +11:00
|
|
|
|
2014-05-14 23:24:20 +10:00
|
|
|
$className = Str::normalizeClassName($className);
|
|
|
|
$this->codeMap[$code] = $className;
|
|
|
|
$this->classMap[$className] = $code;
|
2014-10-11 01:22:03 +02:00
|
|
|
if ($plugin !== null) {
|
2014-05-14 23:24:20 +10:00
|
|
|
$this->pluginMap[$className] = $plugin;
|
2014-10-11 01:22:03 +02:00
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a list of registered components.
|
|
|
|
* @return array Array keys are codes, values are class names.
|
|
|
|
*/
|
|
|
|
public function listComponents()
|
|
|
|
{
|
2014-10-11 01:22:03 +02:00
|
|
|
if ($this->codeMap === null) {
|
2014-05-14 23:24:20 +10:00
|
|
|
$this->loadComponents();
|
2014-10-11 01:22:03 +02:00
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
|
|
|
|
return $this->codeMap;
|
|
|
|
}
|
|
|
|
|
2016-02-09 11:42:14 +01:00
|
|
|
/**
|
2014-05-14 23:24:20 +10:00
|
|
|
* Returns an array of all component detail definitions.
|
|
|
|
* @return array Array keys are component codes, values are the details defined in the component.
|
|
|
|
*/
|
|
|
|
public function listComponentDetails()
|
|
|
|
{
|
2014-10-11 01:22:03 +02:00
|
|
|
if ($this->detailsCache !== null) {
|
2014-05-14 23:24:20 +10:00
|
|
|
return $this->detailsCache;
|
2014-10-11 01:22:03 +02:00
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
|
|
|
|
$details = [];
|
|
|
|
foreach ($this->listComponents() as $componentAlias => $componentClass) {
|
|
|
|
$details[$componentAlias] = $this->makeComponent($componentClass)->componentDetails();
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->detailsCache = $details;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a class name from a component code
|
|
|
|
* Normalizes a class name or converts an code to it's class name.
|
|
|
|
* @return string The class name resolved, or null.
|
|
|
|
*/
|
|
|
|
public function resolve($name)
|
|
|
|
{
|
|
|
|
$codes = $this->listComponents();
|
|
|
|
|
2014-10-11 01:22:03 +02:00
|
|
|
if (isset($codes[$name])) {
|
2014-05-14 23:24:20 +10:00
|
|
|
return $codes[$name];
|
2014-10-11 01:22:03 +02:00
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
|
|
|
|
$name = Str::normalizeClassName($name);
|
2014-10-11 01:22:03 +02:00
|
|
|
if (isset($this->classMap[$name])) {
|
2014-05-14 23:24:20 +10:00
|
|
|
return $name;
|
2014-10-11 01:22:03 +02:00
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks to see if a component has been registered.
|
|
|
|
* @param string $name A component class name or code.
|
|
|
|
* @return bool Returns true if the component is registered, otherwise false.
|
|
|
|
*/
|
|
|
|
public function hasComponent($name)
|
|
|
|
{
|
|
|
|
$className = $this->resolve($name);
|
2014-10-11 01:22:03 +02:00
|
|
|
if (!$className) {
|
2014-05-14 23:24:20 +10:00
|
|
|
return false;
|
2014-10-11 01:22:03 +02:00
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
|
|
|
|
return isset($this->classMap[$className]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Makes a component object with properties set.
|
2020-04-04 18:02:43 +01:00
|
|
|
*
|
2016-03-04 16:50:58 +01:00
|
|
|
* @param string $name A component class name or code.
|
2014-05-14 23:24:20 +10:00
|
|
|
* @param CmsObject $cmsObject The Cms object that spawned this component.
|
|
|
|
* @param array $properties The properties set by the Page or Layout.
|
2020-04-04 18:02:43 +01:00
|
|
|
* @param bool $isSoftComponent Defines if this is a soft component.
|
|
|
|
*
|
2014-05-14 23:24:20 +10:00
|
|
|
* @return ComponentBase The component object.
|
2020-04-04 18:02:43 +01:00
|
|
|
* @throws SystemException If the (hard) component cannot be found or is not registered.
|
2014-05-14 23:24:20 +10:00
|
|
|
*/
|
2020-04-04 18:02:43 +01:00
|
|
|
public function makeComponent($name, $cmsObject = null, $properties = [], $isSoftComponent = false)
|
2014-05-14 23:24:20 +10:00
|
|
|
{
|
2020-04-04 18:02:43 +01:00
|
|
|
$className = $this->resolve(ltrim($name, '@'));
|
|
|
|
|
|
|
|
if (!$className && !$isSoftComponent) {
|
2014-10-11 01:22:03 +02:00
|
|
|
throw new SystemException(sprintf(
|
2015-11-01 13:16:43 +11:00
|
|
|
'Class name is not registered for the component "%s". Check the component plugin.',
|
2014-10-11 01:22:03 +02:00
|
|
|
$name
|
|
|
|
));
|
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
|
2020-04-04 18:02:43 +01:00
|
|
|
if (!class_exists($className) && !$isSoftComponent) {
|
2014-10-11 01:22:03 +02:00
|
|
|
throw new SystemException(sprintf(
|
2015-11-01 13:16:43 +11:00
|
|
|
'Component class not found "%s". Check the component plugin.',
|
2014-10-11 01:22:03 +02:00
|
|
|
$className
|
|
|
|
));
|
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
|
2020-04-04 18:02:43 +01:00
|
|
|
if (class_exists($className)) {
|
|
|
|
$component = App::make($className, [$cmsObject, $properties]);
|
|
|
|
$component->name = $name;
|
2014-05-14 23:24:20 +10:00
|
|
|
|
2020-04-04 18:02:43 +01:00
|
|
|
return $component;
|
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a parent plugin for a specific component object.
|
|
|
|
* @param mixed $component A component to find the plugin for.
|
|
|
|
* @return mixed Returns the plugin object or null.
|
|
|
|
*/
|
|
|
|
public function findComponentPlugin($component)
|
|
|
|
{
|
|
|
|
$className = Str::normalizeClassName(get_class($component));
|
2014-10-11 01:22:03 +02:00
|
|
|
if (isset($this->pluginMap[$className])) {
|
2014-05-14 23:24:20 +10:00
|
|
|
return $this->pluginMap[$className];
|
2014-10-11 01:22:03 +02:00
|
|
|
}
|
2014-05-14 23:24:20 +10:00
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
2014-10-11 01:22:03 +02:00
|
|
|
}
|