2013-06-07 11:44:37 +02:00
|
|
|
<?php
|
2016-02-08 15:41:00 +01:00
|
|
|
/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
|
2013-06-07 11:44:37 +02:00
|
|
|
|
|
|
|
namespace Icinga\Application\Modules;
|
|
|
|
|
2013-09-24 15:26:10 +02:00
|
|
|
use Icinga\Application\ApplicationBootstrap;
|
|
|
|
use Icinga\Application\Icinga;
|
2014-10-31 10:27:17 +01:00
|
|
|
use Icinga\Application\Logger;
|
2014-06-06 08:21:35 +02:00
|
|
|
use Icinga\Data\DataArray\ArrayDatasource;
|
|
|
|
use Icinga\Data\SimpleQuery;
|
2013-09-24 15:26:10 +02:00
|
|
|
use Icinga\Exception\ConfigurationError;
|
|
|
|
use Icinga\Exception\SystemPermissionException;
|
|
|
|
use Icinga\Exception\ProgrammingError;
|
2014-02-20 13:53:28 +01:00
|
|
|
use Icinga\Exception\NotReadableError;
|
2013-06-07 11:44:37 +02:00
|
|
|
|
2013-08-12 15:58:26 +02:00
|
|
|
/**
|
|
|
|
* Module manager that handles detecting, enabling and disabling of modules
|
|
|
|
*
|
|
|
|
* Modules can have 3 states:
|
2013-08-14 16:07:17 +02:00
|
|
|
* * installed, module exists but is disabled
|
|
|
|
* * enabled, module enabled and should be loaded
|
|
|
|
* * loaded, module enabled and loaded via the autoloader
|
2013-08-12 15:58:26 +02:00
|
|
|
*
|
|
|
|
*/
|
2013-06-07 11:44:37 +02:00
|
|
|
class Manager
|
|
|
|
{
|
2015-07-22 14:59:35 +02:00
|
|
|
/**
|
|
|
|
* Namespace for module permissions
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
const MODULE_PERMISSION_NS = 'module/';
|
|
|
|
|
2013-08-12 15:58:26 +02:00
|
|
|
/**
|
|
|
|
* Array of all installed module's base directories
|
|
|
|
*
|
2013-08-29 16:30:22 +02:00
|
|
|
* @var array
|
2013-08-12 15:58:26 +02:00
|
|
|
*/
|
2013-08-29 16:30:22 +02:00
|
|
|
private $installedBaseDirs = array();
|
2013-08-12 15:58:26 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of all enabled modules base dirs
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
2013-08-14 12:42:32 +02:00
|
|
|
private $enabledDirs = array();
|
2013-08-12 15:58:26 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of all module names that have been loaded
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
2013-08-14 12:42:32 +02:00
|
|
|
private $loadedModules = array();
|
2013-08-12 15:58:26 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Reference to Icinga::app
|
|
|
|
*
|
|
|
|
* @var Icinga
|
|
|
|
*/
|
2013-08-14 12:42:32 +02:00
|
|
|
private $app;
|
2013-08-12 15:58:26 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The directory that is used to detect enabled modules
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
2013-08-14 12:42:32 +02:00
|
|
|
private $enableDir;
|
2013-08-12 15:58:26 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* All paths to look for installed modules that can be enabled
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
2013-08-14 16:07:17 +02:00
|
|
|
private $modulePaths = array();
|
2013-08-12 15:58:26 +02:00
|
|
|
|
2015-11-12 17:14:23 +01:00
|
|
|
/**
|
|
|
|
* Whether we loaded all enabled modules
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
private $loadedAllEnabledModules = false;
|
|
|
|
|
2013-06-20 17:01:13 +02:00
|
|
|
/**
|
2013-08-14 12:42:32 +02:00
|
|
|
* Create a new instance of the module manager
|
2013-08-12 15:58:26 +02:00
|
|
|
*
|
2013-08-14 16:07:17 +02:00
|
|
|
* @param ApplicationBootstrap $app
|
|
|
|
* @param string $enabledDir Enabled modules location. The application maintains symlinks within
|
|
|
|
* the given path
|
|
|
|
* @param array $availableDirs Installed modules location
|
2013-08-14 12:42:32 +02:00
|
|
|
**/
|
2014-02-20 13:53:28 +01:00
|
|
|
public function __construct($app, $enabledDir, array $availableDirs)
|
2013-06-07 11:44:37 +02:00
|
|
|
{
|
|
|
|
$this->app = $app;
|
2013-06-20 17:01:13 +02:00
|
|
|
$this->modulePaths = $availableDirs;
|
2013-09-02 12:47:57 +02:00
|
|
|
$this->enableDir = $enabledDir;
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
|
|
|
* Query interface for the module manager
|
|
|
|
*
|
2014-06-06 08:21:35 +02:00
|
|
|
* @return SimpleQuery
|
2013-08-14 12:42:32 +02:00
|
|
|
*/
|
2013-06-20 17:01:13 +02:00
|
|
|
public function select()
|
|
|
|
{
|
2013-08-29 16:30:22 +02:00
|
|
|
$source = new ArrayDatasource($this->getModuleInfo());
|
2013-06-20 17:01:13 +02:00
|
|
|
return $source->select();
|
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
2013-09-02 12:47:57 +02:00
|
|
|
* Check for enabled modules
|
|
|
|
*
|
|
|
|
* Update the internal $enabledDirs property with the enabled modules.
|
|
|
|
*
|
2014-02-20 13:53:28 +01:00
|
|
|
* @throws ConfigurationError If module dir does not exist, is not a directory or not readable
|
2013-08-14 12:42:32 +02:00
|
|
|
*/
|
|
|
|
private function detectEnabledModules()
|
2013-06-07 11:44:37 +02:00
|
|
|
{
|
2015-04-22 17:04:31 +02:00
|
|
|
if (! file_exists($parent = dirname($this->enableDir))) {
|
2015-04-22 16:44:00 +02:00
|
|
|
return;
|
|
|
|
}
|
2015-04-22 17:04:31 +02:00
|
|
|
if (! is_readable($parent)) {
|
2015-04-22 16:44:00 +02:00
|
|
|
throw new NotReadableError(
|
2016-02-25 17:34:03 +01:00
|
|
|
'Cannot read enabled modules. Config directory "%s" is not readable',
|
2015-04-22 16:44:00 +02:00
|
|
|
$parent
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2015-01-30 16:16:12 +01:00
|
|
|
if (! file_exists($this->enableDir)) {
|
2014-04-17 23:16:51 +02:00
|
|
|
return;
|
2014-02-20 13:53:28 +01:00
|
|
|
}
|
2015-01-30 16:16:12 +01:00
|
|
|
if (! is_dir($this->enableDir)) {
|
2014-02-20 13:53:28 +01:00
|
|
|
throw new NotReadableError(
|
2014-08-25 12:38:34 +02:00
|
|
|
'Cannot read enabled modules. Module directory "%s" is not a directory',
|
|
|
|
$this->enableDir
|
2013-09-02 12:47:57 +02:00
|
|
|
);
|
|
|
|
}
|
2015-01-30 16:16:12 +01:00
|
|
|
if (! is_readable($this->enableDir)) {
|
2014-02-20 13:53:28 +01:00
|
|
|
throw new NotReadableError(
|
2014-08-25 12:38:34 +02:00
|
|
|
'Cannot read enabled modules. Module directory "%s" is not readable',
|
|
|
|
$this->enableDir
|
2013-09-02 12:47:57 +02:00
|
|
|
);
|
|
|
|
}
|
2015-01-30 16:16:12 +01:00
|
|
|
if (($dh = opendir($this->enableDir)) !== false) {
|
2017-11-21 12:12:07 +01:00
|
|
|
$isPhar = substr($this->enableDir, 0, 8) === 'phar:///';
|
2014-02-20 13:53:28 +01:00
|
|
|
$this->enabledDirs = array();
|
|
|
|
while (($file = readdir($dh)) !== false) {
|
|
|
|
if ($file[0] === '.' || $file === 'README') {
|
|
|
|
continue;
|
|
|
|
}
|
2013-06-07 11:44:37 +02:00
|
|
|
|
2015-01-30 16:16:12 +01:00
|
|
|
$link = $this->enableDir . DIRECTORY_SEPARATOR . $file;
|
2017-11-21 12:12:07 +01:00
|
|
|
if (! $isPhar && ! is_link($link)) {
|
2014-03-04 10:31:17 +01:00
|
|
|
Logger::warning(
|
2014-02-20 13:53:28 +01:00
|
|
|
'Found invalid module in enabledModule directory "%s": "%s" is not a symlink',
|
|
|
|
$this->enableDir,
|
|
|
|
$link
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
2013-06-07 11:44:37 +02:00
|
|
|
|
2017-11-21 12:12:07 +01:00
|
|
|
$dir = $isPhar ? $link : realpath($link);
|
2017-11-07 12:51:31 +01:00
|
|
|
if ($dir !== false && is_dir($dir)) {
|
|
|
|
$this->enabledDirs[$file] = $dir;
|
|
|
|
} else {
|
|
|
|
$this->enabledDirs[$file] = null;
|
|
|
|
|
2014-03-04 10:31:17 +01:00
|
|
|
Logger::warning(
|
2014-02-20 13:53:28 +01:00
|
|
|
'Found invalid module in enabledModule directory "%s": "%s" points to non existing path "%s"',
|
|
|
|
$this->enableDir,
|
|
|
|
$link,
|
|
|
|
$dir
|
|
|
|
);
|
|
|
|
}
|
2013-06-07 11:44:37 +02:00
|
|
|
|
2014-02-20 13:53:28 +01:00
|
|
|
ksort($this->enabledDirs);
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
2014-02-20 13:53:28 +01:00
|
|
|
closedir($dh);
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
2014-12-09 16:28:05 +01:00
|
|
|
* Try to set all enabled modules in loaded sate
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
2015-04-07 14:23:26 +02:00
|
|
|
* @return $this
|
2013-08-14 12:42:32 +02:00
|
|
|
* @see Manager::loadModule()
|
|
|
|
*/
|
2013-06-07 11:44:37 +02:00
|
|
|
public function loadEnabledModules()
|
|
|
|
{
|
2015-11-12 17:14:23 +01:00
|
|
|
if (! $this->loadedAllEnabledModules) {
|
|
|
|
foreach ($this->listEnabledModules() as $name) {
|
|
|
|
$this->loadModule($name);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->loadedAllEnabledModules = true;
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
2015-11-12 17:14:23 +01:00
|
|
|
|
2013-06-07 11:44:37 +02:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2015-11-12 17:14:23 +01:00
|
|
|
/**
|
|
|
|
* Whether we loaded all enabled modules
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function loadedAllEnabledModules()
|
|
|
|
{
|
|
|
|
return $this->loadedAllEnabledModules;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
|
|
|
* Try to load the module and register it in the application
|
|
|
|
*
|
2014-06-05 00:57:50 +02:00
|
|
|
* @param string $name The name of the module to load
|
|
|
|
* @param mixed $basedir Optional module base directory
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
2015-04-07 14:23:26 +02:00
|
|
|
* @return $this
|
2013-08-14 12:42:32 +02:00
|
|
|
*/
|
2014-06-05 00:57:50 +02:00
|
|
|
public function loadModule($name, $basedir = null)
|
2013-06-07 11:44:37 +02:00
|
|
|
{
|
|
|
|
if ($this->hasLoaded($name)) {
|
|
|
|
return $this;
|
|
|
|
}
|
2013-06-20 17:01:13 +02:00
|
|
|
|
|
|
|
$module = null;
|
2014-06-05 00:57:50 +02:00
|
|
|
if ($basedir === null) {
|
2013-06-20 17:01:13 +02:00
|
|
|
$module = new Module($this->app, $name, $this->getModuleDir($name));
|
|
|
|
} else {
|
2014-06-05 00:57:50 +02:00
|
|
|
$module = new Module($this->app, $name, $basedir);
|
2013-06-20 17:01:13 +02:00
|
|
|
}
|
2013-06-07 11:44:37 +02:00
|
|
|
$module->register();
|
|
|
|
$this->loadedModules[$name] = $module;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
|
|
|
* Set the given module to the enabled state
|
|
|
|
*
|
2013-08-29 17:44:02 +02:00
|
|
|
* @param string $name The module to enable
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
2015-04-07 14:23:26 +02:00
|
|
|
* @return $this
|
2013-08-29 17:44:02 +02:00
|
|
|
* @throws ConfigurationError When trying to enable a module that is not installed
|
|
|
|
* @throws SystemPermissionException When insufficient permissions for the application exist
|
2013-08-14 12:42:32 +02:00
|
|
|
*/
|
2013-06-07 11:44:37 +02:00
|
|
|
public function enableModule($name)
|
|
|
|
{
|
2015-01-30 16:16:12 +01:00
|
|
|
if (! $this->hasInstalled($name)) {
|
2013-06-07 11:44:37 +02:00
|
|
|
throw new ConfigurationError(
|
2014-08-22 11:46:11 +02:00
|
|
|
'Cannot enable module "%s". Module is not installed.',
|
|
|
|
$name
|
2013-06-07 11:44:37 +02:00
|
|
|
);
|
|
|
|
}
|
2014-02-11 13:40:29 +01:00
|
|
|
|
2016-11-16 16:21:35 +01:00
|
|
|
if (strtolower(substr($name, 0, 18)) === 'icingaweb2-module-') {
|
|
|
|
throw new ConfigurationError(
|
|
|
|
'Cannot enable module "%s": Directory name does not match the module\'s name.'
|
|
|
|
. ' Please rename the module to "%s" before enabling.',
|
|
|
|
$name,
|
|
|
|
substr($name, 18)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-06-20 17:01:13 +02:00
|
|
|
clearstatcache(true);
|
2013-06-07 11:44:37 +02:00
|
|
|
$target = $this->installedBaseDirs[$name];
|
2015-01-30 16:16:12 +01:00
|
|
|
$link = $this->enableDir . DIRECTORY_SEPARATOR . $name;
|
2014-02-11 13:40:29 +01:00
|
|
|
|
2015-01-30 16:16:12 +01:00
|
|
|
if (! is_dir($this->enableDir) && !@mkdir($this->enableDir, 02770, true)) {
|
|
|
|
$error = error_get_last();
|
|
|
|
throw new SystemPermissionException(
|
2015-04-20 10:09:33 +02:00
|
|
|
'Failed to create enabledModules directory "%s" (%s)',
|
2015-01-30 16:16:12 +01:00
|
|
|
$this->enableDir,
|
|
|
|
$error['message']
|
|
|
|
);
|
|
|
|
} elseif (! is_writable($this->enableDir)) {
|
2013-06-20 14:06:02 +02:00
|
|
|
throw new SystemPermissionException(
|
2015-04-20 10:09:33 +02:00
|
|
|
'Cannot enable module "%s". Check the permissions for the enabledModules directory: %s',
|
|
|
|
$name,
|
|
|
|
$this->enableDir
|
2013-06-20 14:06:02 +02:00
|
|
|
);
|
|
|
|
}
|
2014-02-11 13:40:29 +01:00
|
|
|
|
2015-11-12 17:14:23 +01:00
|
|
|
$this->loadedAllEnabledModules = false;
|
|
|
|
|
2013-06-20 14:06:02 +02:00
|
|
|
if (file_exists($link) && is_link($link)) {
|
2013-06-07 11:44:37 +02:00
|
|
|
return $this;
|
|
|
|
}
|
2014-02-11 13:40:29 +01:00
|
|
|
|
2015-01-30 16:16:12 +01:00
|
|
|
if (! @symlink($target, $link)) {
|
2013-06-20 14:06:02 +02:00
|
|
|
$error = error_get_last();
|
|
|
|
if (strstr($error["message"], "File exists") === false) {
|
2013-08-29 17:44:02 +02:00
|
|
|
throw new SystemPermissionException(
|
2015-04-20 10:09:33 +02:00
|
|
|
'Cannot enable module "%s" at %s due to file system errors. '
|
2013-08-29 17:44:02 +02:00
|
|
|
. 'Please check path and mounting points because this is not a permission error. '
|
2014-08-26 11:21:57 +02:00
|
|
|
. 'Primary error was: %s',
|
|
|
|
$name,
|
2015-04-20 10:09:33 +02:00
|
|
|
$this->enableDir,
|
2014-08-26 11:21:57 +02:00
|
|
|
$error['message']
|
2013-08-29 17:44:02 +02:00
|
|
|
);
|
2013-06-20 14:06:02 +02:00
|
|
|
}
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
2014-02-11 13:40:29 +01:00
|
|
|
|
2013-06-20 17:01:13 +02:00
|
|
|
$this->enabledDirs[$name] = $link;
|
2014-02-11 13:40:29 +01:00
|
|
|
$this->loadModule($name);
|
2013-06-07 11:44:37 +02:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
2015-03-08 14:39:53 +01:00
|
|
|
* Disable the given module and remove its enabled state
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
2013-08-29 17:44:02 +02:00
|
|
|
* @param string $name The name of the module to disable
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
2015-04-07 14:23:26 +02:00
|
|
|
* @return $this
|
2013-08-14 16:07:17 +02:00
|
|
|
*
|
2013-08-29 17:44:02 +02:00
|
|
|
* @throws ConfigurationError When the module is not installed or it's not a symlink
|
2015-04-20 10:09:33 +02:00
|
|
|
* @throws SystemPermissionException When insufficient permissions for the application exist
|
2013-08-14 12:42:32 +02:00
|
|
|
*/
|
2013-06-07 11:44:37 +02:00
|
|
|
public function disableModule($name)
|
|
|
|
{
|
2015-01-30 16:16:12 +01:00
|
|
|
if (! $this->hasEnabled($name)) {
|
2015-08-04 14:34:30 +02:00
|
|
|
throw new ConfigurationError(
|
|
|
|
'Cannot disable module "%s". Module is not installed.',
|
|
|
|
$name
|
|
|
|
);
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
2015-04-20 10:09:33 +02:00
|
|
|
|
2015-01-30 16:16:12 +01:00
|
|
|
if (! is_writable($this->enableDir)) {
|
2013-08-29 17:44:02 +02:00
|
|
|
throw new SystemPermissionException(
|
2015-04-20 10:09:33 +02:00
|
|
|
'Cannot disable module "%s". Check the permissions for the enabledModules directory: %s',
|
|
|
|
$name,
|
|
|
|
$this->enableDir
|
2013-08-29 17:44:02 +02:00
|
|
|
);
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
2015-04-20 10:09:33 +02:00
|
|
|
|
2015-01-30 16:16:12 +01:00
|
|
|
$link = $this->enableDir . DIRECTORY_SEPARATOR . $name;
|
|
|
|
if (! is_link($link)) {
|
2013-06-20 14:06:02 +02:00
|
|
|
throw new ConfigurationError(
|
2015-04-20 10:09:33 +02:00
|
|
|
'Cannot disable module %s at %s. '
|
2013-08-14 16:07:17 +02:00
|
|
|
. 'It looks like you have installed this module manually and moved it to your module folder. '
|
|
|
|
. 'In order to dynamically enable and disable modules, you have to create a symlink to '
|
2015-04-20 10:09:33 +02:00
|
|
|
. 'the enabledModules folder.',
|
|
|
|
$name,
|
|
|
|
$this->enableDir
|
2013-06-20 14:06:02 +02:00
|
|
|
);
|
|
|
|
}
|
2013-08-14 16:07:17 +02:00
|
|
|
|
2017-11-07 12:51:31 +01:00
|
|
|
if (is_link($link)) {
|
2015-01-30 16:16:12 +01:00
|
|
|
if (! @unlink($link)) {
|
2013-06-20 14:06:02 +02:00
|
|
|
$error = error_get_last();
|
2013-08-29 17:44:02 +02:00
|
|
|
throw new SystemPermissionException(
|
2015-04-20 10:09:33 +02:00
|
|
|
'Cannot enable module "%s" at %s due to file system errors. '
|
2013-08-29 17:44:02 +02:00
|
|
|
. 'Please check path and mounting points because this is not a permission error. '
|
2014-08-26 11:21:57 +02:00
|
|
|
. 'Primary error was: %s',
|
|
|
|
$name,
|
2015-04-20 10:09:33 +02:00
|
|
|
$this->enableDir,
|
2014-08-26 11:21:57 +02:00
|
|
|
$error['message']
|
2013-08-29 17:44:02 +02:00
|
|
|
);
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
|
|
|
}
|
2013-08-29 17:44:02 +02:00
|
|
|
|
2013-06-20 17:01:13 +02:00
|
|
|
unset($this->enabledDirs[$name]);
|
2013-06-07 11:44:37 +02:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
|
|
|
* Return the directory of the given module as a string, optionally with a given sub directoy
|
|
|
|
*
|
2013-08-14 16:07:17 +02:00
|
|
|
* @param string $name The module name to return the module directory of
|
|
|
|
* @param string $subdir The sub directory to append to the path
|
|
|
|
*
|
|
|
|
* @return string
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
2013-08-29 17:44:02 +02:00
|
|
|
* @throws ProgrammingError When the module is not installed or existing
|
2013-08-14 12:42:32 +02:00
|
|
|
*/
|
2013-06-07 11:44:37 +02:00
|
|
|
public function getModuleDir($name, $subdir = '')
|
|
|
|
{
|
2014-06-05 00:59:48 +02:00
|
|
|
if ($this->hasLoaded($name)) {
|
|
|
|
return $this->getModule($name)->getBaseDir() . $subdir;
|
|
|
|
}
|
|
|
|
|
2013-06-07 11:44:37 +02:00
|
|
|
if ($this->hasEnabled($name)) {
|
|
|
|
return $this->enabledDirs[$name]. $subdir;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->hasInstalled($name)) {
|
|
|
|
return $this->installedBaseDirs[$name] . $subdir;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new ProgrammingError(
|
2014-08-26 11:15:19 +02:00
|
|
|
'Trying to access uninstalled module dir: %s',
|
|
|
|
$name
|
2013-06-07 11:44:37 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
|
|
|
* Return true when the module with the given name is installed, otherwise false
|
|
|
|
*
|
2013-08-14 16:07:17 +02:00
|
|
|
* @param string $name The module to check for being installed
|
|
|
|
*
|
|
|
|
* @return bool
|
2013-08-14 12:42:32 +02:00
|
|
|
*/
|
2013-06-07 11:44:37 +02:00
|
|
|
public function hasInstalled($name)
|
|
|
|
{
|
2013-08-29 16:30:22 +02:00
|
|
|
if (!count($this->installedBaseDirs)) {
|
2013-06-07 11:44:37 +02:00
|
|
|
$this->detectInstalledModules();
|
|
|
|
}
|
|
|
|
return array_key_exists($name, $this->installedBaseDirs);
|
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
|
|
|
* Return true when the given module is in enabled state, otherwise false
|
|
|
|
*
|
2013-08-14 16:07:17 +02:00
|
|
|
* @param string $name The module to check for being enabled
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
2013-08-14 16:07:17 +02:00
|
|
|
* @return bool
|
2013-08-14 12:42:32 +02:00
|
|
|
*/
|
2013-06-07 11:44:37 +02:00
|
|
|
public function hasEnabled($name)
|
|
|
|
{
|
|
|
|
return array_key_exists($name, $this->enabledDirs);
|
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
|
|
|
* Return true when the module is in loaded state, otherwise false
|
|
|
|
*
|
2013-08-14 16:07:17 +02:00
|
|
|
* @param string $name The module to check for being loaded
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
2013-08-14 16:07:17 +02:00
|
|
|
* @return bool
|
2013-08-14 12:42:32 +02:00
|
|
|
*/
|
2013-06-07 11:44:37 +02:00
|
|
|
public function hasLoaded($name)
|
|
|
|
{
|
|
|
|
return array_key_exists($name, $this->loadedModules);
|
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
2014-10-31 11:22:27 +01:00
|
|
|
* Get the currently loaded modules
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
2014-10-31 11:22:27 +01:00
|
|
|
* @return Module[]
|
2013-08-14 12:42:32 +02:00
|
|
|
*/
|
2013-06-07 11:44:37 +02:00
|
|
|
public function getLoadedModules()
|
|
|
|
{
|
|
|
|
return $this->loadedModules;
|
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
2015-07-24 14:23:48 +02:00
|
|
|
* Get a module
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
2015-07-24 14:23:48 +02:00
|
|
|
* @param string $name Name of the module
|
|
|
|
* @param bool $assertLoaded Whether or not to throw an exception if the module hasn't been loaded
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
2013-08-29 17:44:02 +02:00
|
|
|
* @return Module
|
2015-07-24 14:23:48 +02:00
|
|
|
* @throws ProgrammingError If the module hasn't been loaded
|
2013-08-14 12:42:32 +02:00
|
|
|
*/
|
2015-07-24 14:23:48 +02:00
|
|
|
public function getModule($name, $assertLoaded = true)
|
2013-06-07 11:44:37 +02:00
|
|
|
{
|
2015-07-24 14:23:48 +02:00
|
|
|
if ($this->hasLoaded($name)) {
|
|
|
|
return $this->loadedModules[$name];
|
|
|
|
} elseif (! (bool) $assertLoaded) {
|
|
|
|
return new Module($this->app, $name, $this->getModuleDir($name));
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
2015-07-24 14:23:48 +02:00
|
|
|
throw new ProgrammingError(
|
|
|
|
'Can\'t access module %s because it hasn\'t been loaded',
|
|
|
|
$name
|
|
|
|
);
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
|
|
|
* Return an array containing information objects for each available module
|
|
|
|
*
|
|
|
|
* Each entry has the following fields
|
2013-08-14 16:07:17 +02:00
|
|
|
* * name, name of the module as a string
|
|
|
|
* * path, path where the module is located as a string
|
2017-11-07 12:51:31 +01:00
|
|
|
* * installed, whether the module is installed or not as a boolean
|
2013-08-14 16:07:17 +02:00
|
|
|
* * enabled, whether the module is enabled or not as a boolean
|
|
|
|
* * loaded, whether the module is loaded or not as a boolean
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
2013-06-07 11:44:37 +02:00
|
|
|
public function getModuleInfo()
|
|
|
|
{
|
|
|
|
$info = array();
|
2014-01-22 18:21:05 +01:00
|
|
|
|
2017-11-07 12:51:31 +01:00
|
|
|
$installed = $this->listInstalledModules();
|
|
|
|
foreach ($installed as $name) {
|
2014-01-22 18:21:05 +01:00
|
|
|
$info[$name] = (object) array(
|
2017-11-07 12:51:31 +01:00
|
|
|
'name' => $name,
|
|
|
|
'path' => $this->installedBaseDirs[$name],
|
|
|
|
'installed' => true,
|
|
|
|
'enabled' => $this->hasEnabled($name),
|
|
|
|
'loaded' => $this->hasLoaded($name)
|
2014-01-22 18:21:05 +01:00
|
|
|
);
|
2013-06-26 16:05:01 +02:00
|
|
|
}
|
2013-08-14 16:07:17 +02:00
|
|
|
|
2017-11-07 12:51:31 +01:00
|
|
|
$enabled = $this->listEnabledModules();
|
|
|
|
foreach ($enabled as $name) {
|
2014-01-22 18:21:05 +01:00
|
|
|
$info[$name] = (object) array(
|
2017-11-07 12:51:31 +01:00
|
|
|
'name' => $name,
|
|
|
|
'path' => $this->enabledDirs[$name],
|
|
|
|
'installed' => $this->enabledDirs[$name] !== null,
|
|
|
|
'enabled' => true,
|
|
|
|
'loaded' => $this->hasLoaded($name)
|
2013-06-07 11:44:37 +02:00
|
|
|
);
|
|
|
|
}
|
2017-11-07 12:51:31 +01:00
|
|
|
|
2013-06-07 11:44:37 +02:00
|
|
|
return $info;
|
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
|
|
|
* Return an array containing all enabled module names as strings
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
2013-06-07 11:44:37 +02:00
|
|
|
public function listEnabledModules()
|
|
|
|
{
|
2013-09-02 12:47:57 +02:00
|
|
|
if (count($this->enabledDirs) === 0) {
|
|
|
|
$this->detectEnabledModules();
|
|
|
|
}
|
|
|
|
|
2013-06-07 11:44:37 +02:00
|
|
|
return array_keys($this->enabledDirs);
|
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
|
|
|
* Return an array containing all loaded module names as strings
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
2013-06-07 11:44:37 +02:00
|
|
|
public function listLoadedModules()
|
|
|
|
{
|
|
|
|
return array_keys($this->loadedModules);
|
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
2013-08-29 17:44:02 +02:00
|
|
|
* Return an array of module names from installed modules
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
2013-08-14 16:07:17 +02:00
|
|
|
* Calls detectInstalledModules() if no module discovery has been performed yet
|
2013-08-14 12:42:32 +02:00
|
|
|
*
|
2013-08-14 16:07:17 +02:00
|
|
|
* @return array
|
|
|
|
*
|
|
|
|
* @see detectInstalledModules()
|
2013-08-14 12:42:32 +02:00
|
|
|
*/
|
2013-06-07 11:44:37 +02:00
|
|
|
public function listInstalledModules()
|
|
|
|
{
|
2013-08-29 16:30:22 +02:00
|
|
|
if (!count($this->installedBaseDirs)) {
|
2013-06-07 11:44:37 +02:00
|
|
|
$this->detectInstalledModules();
|
|
|
|
}
|
2013-08-14 16:07:17 +02:00
|
|
|
|
2013-08-29 16:30:22 +02:00
|
|
|
if (count($this->installedBaseDirs)) {
|
2013-06-26 16:05:01 +02:00
|
|
|
return array_keys($this->installedBaseDirs);
|
|
|
|
}
|
2013-08-29 17:44:02 +02:00
|
|
|
|
|
|
|
return array();
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
|
|
|
|
2013-08-14 12:42:32 +02:00
|
|
|
/**
|
|
|
|
* Detect installed modules from every path provided in modulePaths
|
|
|
|
*
|
2014-10-24 13:51:23 +02:00
|
|
|
* @param array $availableDirs Installed modules location
|
|
|
|
*
|
2015-04-07 14:23:26 +02:00
|
|
|
* @return $this
|
2013-08-14 12:42:32 +02:00
|
|
|
*/
|
2014-10-24 13:51:23 +02:00
|
|
|
public function detectInstalledModules(array $availableDirs = null)
|
2013-06-07 11:44:37 +02:00
|
|
|
{
|
2014-10-24 13:51:23 +02:00
|
|
|
$modulePaths = $availableDirs !== null ? $availableDirs : $this->modulePaths;
|
|
|
|
foreach ($modulePaths as $basedir) {
|
2014-02-20 13:53:28 +01:00
|
|
|
$canonical = realpath($basedir);
|
|
|
|
if ($canonical === false) {
|
2014-03-04 10:31:17 +01:00
|
|
|
Logger::warning('Module path "%s" does not exist', $basedir);
|
2014-01-22 18:21:05 +01:00
|
|
|
continue;
|
|
|
|
}
|
2014-02-20 13:53:28 +01:00
|
|
|
if (!is_dir($canonical)) {
|
2014-03-04 10:31:17 +01:00
|
|
|
Logger::error('Module path "%s" is not a directory', $canonical);
|
2014-02-20 13:53:28 +01:00
|
|
|
continue;
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
2014-02-20 13:53:28 +01:00
|
|
|
if (!is_readable($canonical)) {
|
2014-03-04 10:31:17 +01:00
|
|
|
Logger::error('Module path "%s" is not readable', $canonical);
|
2014-02-20 13:53:28 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (($dh = opendir($canonical)) !== false) {
|
|
|
|
while (($file = readdir($dh)) !== false) {
|
|
|
|
if ($file[0] === '.') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (is_dir($canonical . '/' . $file)) {
|
|
|
|
if (! array_key_exists($file, $this->installedBaseDirs)) {
|
|
|
|
$this->installedBaseDirs[$file] = $canonical . '/' . $file;
|
|
|
|
} else {
|
2014-05-21 00:39:32 +02:00
|
|
|
Logger::debug(
|
2014-02-20 13:53:28 +01:00
|
|
|
'Module "%s" already exists in installation path "%s" and is ignored.',
|
|
|
|
$canonical . '/' . $file,
|
|
|
|
$this->installedBaseDirs[$file]
|
|
|
|
);
|
|
|
|
}
|
2014-01-22 18:21:05 +01:00
|
|
|
}
|
2013-06-20 17:01:13 +02:00
|
|
|
}
|
2014-02-20 13:53:28 +01:00
|
|
|
closedir($dh);
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
|
|
|
}
|
2014-01-24 10:48:30 +01:00
|
|
|
ksort($this->installedBaseDirs);
|
2013-08-14 12:42:32 +02:00
|
|
|
return $this;
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
2014-11-13 09:33:31 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the directories where to look for installed modules
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getModuleDirs()
|
|
|
|
{
|
|
|
|
return $this->modulePaths;
|
|
|
|
}
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|