%PDF- %PDF-
| Direktori : /home/vacivi36/intranet.vacivitta.com.br/protected/humhub/components/ |
| Current File : /home/vacivi36/intranet.vacivitta.com.br/protected/humhub/components/ModuleManager.php |
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\components;
use humhub\components\bootstrap\ModuleAutoLoader;
use humhub\components\console\Application as ConsoleApplication;
use humhub\libs\BaseSettingsManager;
use humhub\models\ModuleEnabled;
use humhub\modules\admin\events\ModulesEvent;
use humhub\modules\marketplace\Module as ModuleMarketplace;
use Yii;
use yii\base\Component;
use yii\base\Event;
use yii\base\Exception;
use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper;
use yii\helpers\FileHelper;
/**
* ModuleManager handles all installed modules.
*
* @author luke
*/
class ModuleManager extends Component
{
/**
* @event triggered before a module is enabled
* @since 1.3
*/
const EVENT_BEFORE_MODULE_ENABLE = 'beforeModuleEnabled';
/**
* @event triggered after a module is enabled
* @since 1.3
*/
const EVENT_AFTER_MODULE_ENABLE = 'afterModuleEnabled';
/**
* @event triggered before a module is disabled
* @since 1.3
*/
const EVENT_BEFORE_MODULE_DISABLE = 'beforeModuleDisabled';
/**
* @event triggered after a module is disabled
* @since 1.3
*/
const EVENT_AFTER_MODULE_DISABLE = 'afterModuleDisabled';
/**
* @event triggered after filter modules
* @since 1.11
*/
const EVENT_AFTER_FILTER_MODULES = 'afterFilterModules';
/**
* Create a backup on module folder deletion
*
* @var boolean
*/
public $createBackup = true;
/**
* List of all modules
* This also contains installed but not enabled modules.
*
* @param array $config moduleId-class pairs
*/
protected $modules;
/**
* List of all enabled module ids
*
* @var array
*/
protected $enabledModules = [];
/**
* List of core module classes.
*
* @var array the core module class names
*/
protected $coreModules = [];
/**
* @var bool Prevent registration of several different modules with the same id.
*/
public $preventDuplicatedModules = true;
/**
* List of module paths that should be overwritten
* Key - module id, Value - absolute path to module folder
*
* @var array
*/
public $overwriteModuleBasePath = [];
/**
* Module Manager init
*
* Loads all enabled moduleId's from database
*/
public function init()
{
parent::init();
// Either database installed and not in installed state
if (!Yii::$app->params['databaseInstalled'] && !Yii::$app->params['installed']) {
return;
}
if (!BaseSettingsManager::isDatabaseInstalled()) {
$this->enabledModules = [];
} else {
$this->enabledModules = ModuleEnabled::getEnabledIds();
}
}
/**
* Registers a module to the manager
* This is usually done by config.php in modules root folder.
* @param array $configs
* @throws InvalidConfigException
* @see \humhub\components\bootstrap\ModuleAutoLoader::bootstrap
*
*/
public function registerBulk(array $configs)
{
foreach ($configs as $basePath => $config) {
$this->register($basePath, $config);
}
}
/**
* Registers a module
*
* @param string $basePath the modules base path
* @param array $config the module configuration (config.php)
* @throws InvalidConfigException
*/
public function register($basePath, $config = null)
{
$filename = $basePath . '/config.php';
if ($config === null && is_file($filename)) {
$config = include $filename;
}
// Check mandatory config options
if (!isset($config['class']) || !isset($config['id'])) {
throw new InvalidConfigException('Module configuration requires an id and class attribute: ' . $basePath);
}
$isCoreModule = (isset($config['isCoreModule']) && $config['isCoreModule']);
$isInstallerModule = (isset($config['isInstallerModule']) && $config['isInstallerModule']);
$this->modules[$config['id']] = $config['class'];
if (isset($config['namespace'])) {
Yii::setAlias('@' . str_replace('\\', '/', $config['namespace']), $basePath);
}
// Check if alias is not in use yet (e.g. don't register "web" module alias)
if (Yii::getAlias('@' . $config['id'], false) === false) {
Yii::setAlias('@' . $config['id'], $basePath);
}
if (isset($config['aliases']) && is_array($config['aliases'])) {
foreach ($config['aliases'] as $name => $value) {
Yii::setAlias($name, $value);
}
}
if (!Yii::$app->params['installed'] && $isInstallerModule) {
$this->enabledModules[] = $config['id'];
}
// Not enabled and no core/installer module
if (!$isCoreModule && !in_array($config['id'], $this->enabledModules)) {
return;
}
// Handle Submodules
if (!isset($config['modules'])) {
$config['modules'] = [];
}
if ($isCoreModule) {
$this->coreModules[] = $config['class'];
}
// Append URL Rules
if (isset($config['urlManagerRules'])) {
Yii::$app->urlManager->addRules($config['urlManagerRules'], false);
}
$moduleConfig = [
'class' => $config['class'],
'modules' => $config['modules'],
];
// Add config file values to module
if (isset(Yii::$app->modules[$config['id']]) && is_array(Yii::$app->modules[$config['id']])) {
$moduleConfig = ArrayHelper::merge($moduleConfig, Yii::$app->modules[$config['id']]);
}
// Register Yii Module
Yii::$app->setModule($config['id'], $moduleConfig);
// Register Event Handlers
if (isset($config['events'])) {
foreach ($config['events'] as $event) {
$eventClass = $event['class'] ?? $event[0];
$eventName = $event['event'] ?? $event[1];
$eventHandler = $event['callback'] ?? $event[2];
if (method_exists($eventHandler[0], $eventHandler[1])) {
Event::on($eventClass, $eventName, $eventHandler);
}
}
}
// Register Console ControllerMap
if (Yii::$app instanceof ConsoleApplication && !(empty($config['consoleControllerMap']))) {
Yii::$app->controllerMap = ArrayHelper::merge(Yii::$app->controllerMap, $config['consoleControllerMap']);
}
}
/**
* Returns all modules (also disabled modules).
*
* Note: Only modules which extends \humhub\components\Module will be returned.
*
* @param array $options options (name => config)
* The following options are available:
*
* - includeCoreModules: boolean, return also core modules (default: false)
* - returnClass: boolean, return classname instead of module object (default: false)
* - enabled: boolean, returns only enabled modules (core modules only when combined with `includeCoreModules`)
*
* @return array
* @throws Exception
*/
public function getModules($options = [])
{
$options = array_merge([
'includeCoreModules' => false,
'enabled' => false,
'returnClass' => false,
], $options);
$modules = [];
foreach ($this->modules as $id => $class) {
if (!$options['includeCoreModules'] && in_array($class, $this->coreModules)) {
// Skip core modules
continue;
}
if ($options['enabled'] && !in_array($class, $this->coreModules) && !in_array($id, $this->enabledModules)) {
// Skip disabled modules
continue;
}
if ($options['returnClass']) {
$modules[$id] = $class;
} else {
$module = $this->getModule($id);
if ($module instanceof Module) {
$modules[$id] = $module;
}
}
}
return $modules;
}
/**
* Filter modules by keyword and by additional filters from module event
*
* @param Module[] $modules
* @param array $filters
* @return Module[]
*/
public function filterModules(array $modules, $filters = []): array
{
$filters = array_merge([
'keyword' => null,
], $filters);
$modules = $this->filterModulesByKeyword($modules, $filters['keyword']);
$modulesEvent = new ModulesEvent(['modules' => $modules]);
$this->trigger(static::EVENT_AFTER_FILTER_MODULES, $modulesEvent);
return $modulesEvent->modules;
}
/**
* Filter modules by keyword
*
* @param Module[] $modules
* @param null|string $keyword
* @return Module[]
*/
public function filterModulesByKeyword(array $modules, $keyword = null): array
{
if ($keyword === null) {
$keyword = Yii::$app->request->get('keyword', '');
}
if (!is_scalar($keyword) || $keyword === '') {
return $modules;
}
foreach ($modules as $id => $module) {
/* @var Module $module */
$searchFields = [$id];
if (isset($module->name)) {
$searchFields[] = $module->name;
}
if (isset($module->description)) {
$searchFields[] = $module->description;
}
$keywordFound = false;
foreach ($searchFields as $searchField) {
if (stripos($searchField, $keyword) !== false) {
$keywordFound = true;
continue;
}
}
if (!$keywordFound) {
unset($modules[$id]);
}
}
return $modules;
}
/**
* Returns all enabled modules and supportes further options as [[getModules()]].
*
* @param array $options
* @return array
* @throws Exception
* @since 1.3.10
*/
public function getEnabledModules($options = [])
{
$options['enabled'] = true;
return $this->getModules($options);
}
/**
* Checks if a moduleId exists, regardless it's activated or not
*
* @param string $id
* @return boolean
*/
public function hasModule($id)
{
return (array_key_exists($id, $this->modules));
}
/**
* Returns weather or not the given module id belongs to an core module.
*
* @return bool
* @throws Exception
* @since 1.3.8
*/
public function isCoreModule($id)
{
if (!$this->hasModule($id)) {
return false;
}
return (in_array(get_class($this->getModule($id)), $this->coreModules));
}
/**
* Returns a module instance by id
*
* @param string $id Module Id
* @return Module|object
* @throws Exception
* @throws InvalidConfigException
*/
public function getModule($id)
{
// Enabled Module
if (Yii::$app->hasModule($id)) {
return Yii::$app->getModule($id, true);
}
// Disabled Module
if (isset($this->modules[$id])) {
$class = $this->modules[$id];
return Yii::createObject($class, [$id, Yii::$app]);
}
throw new Exception('Could not find/load requested module: ' . $id);
}
/**
* Flushes module manager cache
*/
public function flushCache()
{
Yii::$app->cache->delete(ModuleAutoLoader::CACHE_ID);
}
/**
* Checks the module can removed
*
* @param string $moduleId
* @return bool
* @throws Exception
*/
public function canRemoveModule($moduleId)
{
$module = $this->getModule($moduleId);
if ($module === null) {
return false;
}
// Check is in dynamic/marketplace module folder
/** @var ModuleMarketplace $marketplaceModule */
$marketplaceModule = Yii::$app->getModule('marketplace');
if ($marketplaceModule !== null) {
if (strpos($module->getBasePath(), Yii::getAlias($marketplaceModule->modulesPath)) !== false) {
return true;
}
}
return false;
}
/**
* Removes a module
*
* @param string $moduleId
* @param bool $disableBeforeRemove
* @throws Exception
* @throws \yii\base\ErrorException
*/
public function removeModule($moduleId, $disableBeforeRemove = true)
{
$module = $this->getModule($moduleId);
if ($module == null) {
throw new Exception('Could not load module to remove!');
}
/**
* Disable Module
*/
if ($disableBeforeRemove && Yii::$app->hasModule($moduleId)) {
$module->disable();
}
/**
* Remove Folder
*/
if ($this->createBackup) {
$moduleBackupFolder = Yii::getAlias('@runtime/module_backups');
FileHelper::createDirectory($moduleBackupFolder);
$backupFolderName = $moduleBackupFolder . DIRECTORY_SEPARATOR . $moduleId . '_' . time();
$moduleBasePath = $module->getBasePath();
FileHelper::copyDirectory($moduleBasePath, $backupFolderName);
FileHelper::removeDirectory($moduleBasePath);
} else {
//TODO: Delete directory
}
$this->flushCache();
}
/**
* Enables a module
*
* @param Module $module
* @throws InvalidConfigException
* @since 1.1
*/
public function enable(Module $module)
{
$this->trigger(static::EVENT_BEFORE_MODULE_ENABLE, new ModuleEvent(['module' => $module]));
if (!ModuleEnabled::findOne(['module_id' => $module->id])) {
(new ModuleEnabled(['module_id' => $module->id]))->save();
}
$this->enabledModules[] = $module->id;
$this->register($module->getBasePath());
$this->trigger(static::EVENT_AFTER_MODULE_ENABLE, new ModuleEvent(['module' => $module]));
}
public function enableModules($modules = [])
{
foreach ($modules as $module) {
$module = ($module instanceof Module) ? $module : $this->getModule($module);
if ($module != null) {
$module->enable();
}
}
}
/**
* Disables a module
*
* @param Module $module
* @throws \Throwable
* @throws \yii\db\StaleObjectException
* @since 1.1
*/
public function disable(Module $module)
{
$this->trigger(static::EVENT_BEFORE_MODULE_DISABLE, new ModuleEvent(['module' => $module]));
$moduleEnabled = ModuleEnabled::findOne(['module_id' => $module->id]);
if ($moduleEnabled != null) {
$moduleEnabled->delete();
}
if (($key = array_search($module->id, $this->enabledModules)) !== false) {
unset($this->enabledModules[$key]);
}
Yii::$app->setModule($module->id, null);
$this->trigger(static::EVENT_AFTER_MODULE_DISABLE, new ModuleEvent(['module' => $module]));
}
/**
* @param array $modules
* @throws Exception
*/
public function disableModules($modules = [])
{
foreach ($modules as $module) {
$module = ($module instanceof Module) ? $module : $this->getModule($module);
if ($module != null) {
$module->disable();
}
}
}
}