protected/modules/yupe/components/ModuleManager.php
<?php
/**
* Компонент для управления модулями
*
* @category YupeComponent
* @package yupe.modules.yupe.components
* @author A.Opeykin <support@yupe.ru>
* @license BSD http://ru.wikipedia.org/wiki/%D0%9B%D0%B8%D1%86%D0%B5%D0%BD%D0%B7%D0%B8%D1%8F_BSD
* @version 0.6
* @link https://yupe.ru
*
**/
namespace yupe\components;
use CChainedCacheDependency;
use CDirectoryCacheDependency;
use Exception;
use GlobIterator;
use TagsCache;
use Yii;
use yupe\widgets\YFlashMessages;
use yupe\helpers\YFile;
/**
* Class ModuleManager
* @package yupe\components
*/
class ModuleManager extends \CApplicationComponent
{
/**
*
*/
const CORE_MODULE = 'yupe';
/**
*
*/
const INSTALL_MODULE = 'install';
/**
* @var
*/
public $otherCategoryName;
/**
* @var
*/
public $category;
/**
* @var
*/
public $categoryIcon;
/**
* @var
*/
public $categorySort;
/**
* Возвращаем список модулей:
*
* @param bool $navigationOnly - только навигация
* @param bool $disableModule - отключённые модули
*
* @return mixed
**/
public function getModules($navigationOnly = false, $disableModule = false)
{
$this->otherCategoryName = Yii::t('YupeModule.yupe', 'Other');
$this->categoryIcon = [
Yii::t('YupeModule.yupe', 'Services') => 'fa fa-fw fa-briefcase',
Yii::t('YupeModule.yupe', 'Yupe!') => 'fa fa-fw fa-cog',
Yii::t('YupeModule.yupe', 'Content') => 'fa fa-fw fa-file',
$this->otherCategoryName => 'fa fa-fw fa-cog',
];
$this->categorySort = [
Yii::t('YupeModule.yupe', 'Users'),
Yii::t('YupeModule.yupe', 'Content'),
Yii::t('YupeModule.yupe', 'Structure'),
Yii::t('YupeModule.yupe', 'Users'),
Yii::t('YupeModule.yupe', 'Services'),
Yii::t('YupeModule.yupe', 'Store'),
Yii::t('YupeModule.yupe', 'Catalog'),
Yii::t('YupeModule.yupe', 'Yupe!'),
$this->otherCategoryName,
];
$modules = $yiiModules = $order = [];
$modulesExtendedNavigation = [];
if (count(Yii::app()->getModules())) {
/**
*
* Получаем модули и заполняем основные массивы
**/
foreach (Yii::app()->getModules() as $key => $value) {
$key = strtolower($key);
$module = Yii::app()->getModule($key);
if ($module !== null) {
if ($module instanceof WebModule) {
$category = (!$module->getCategory())
? $this->otherCategoryName
: $module->getCategory();
$modules[$key] = $module;
$order[$category][$key] = $module->adminMenuOrder;
$moduleExNav = (array)$module->getExtendedNavigation();
$modulesExtendedNavigation = array_merge($modulesExtendedNavigation, $moduleExNav);
} else {
$yiiModules[$key] = $module;
}
}
}
$modulesNavigation = Yii::app()->getCache()->get('YupeModulesNavigation-'.Yii::app()->getLanguage());
if ($modulesNavigation === false) {
// Формируем навигационное меню
$modulesNavigation = [];
// Сортируем категории модулей
if (count($order) > 1) {
$categorySort = array_reverse($this->categorySort);
foreach ($categorySort as $iValue) {
if (array_key_exists($iValue, $order)) {
$orderValue = $order[$iValue];
unset($order[$iValue]);
$order = array_merge([$iValue => $orderValue], $order);
}
}
}
$uniqueMenuId = 0;
$settings['items'] = [];
// Обходим категории модулей
foreach ($order as $keyCategory => $valueCategory) {
// Шаблон категорий
$modulesNavigation[$keyCategory] = [
'label' => $keyCategory,
//'url' => '#',
'items' => [],
'submenuOptions' => ["id" => "mainmenu_".$uniqueMenuId],
];
$uniqueMenuId++;
if (array_key_exists($keyCategory, $this->categoryIcon)) {
$modulesNavigation[$keyCategory]['icon'] = $this->categoryIcon[$keyCategory];
}
// Сортируем модули в категории
asort($valueCategory, SORT_NUMERIC);
// Обходим модули
foreach ($valueCategory as $key => $value) {
$modSettings = [];
// Собраются подпункты категории "Настройки модулей", кроме пункта Юпи
if ($key !== self::CORE_MODULE && $modules[$key]->editableParams) {
$modSettings = [
'---',
[
'icon' => 'fa fa-fw fa-cog',
'label' => Yii::t('YupeModule.yupe', 'Module settings'),
'url' => $modules[$key]->getSettingsUrl(),
],
];
}
// Проверка на вывод модуля в категориях, потребуется при отключении модуля
if (!$modules[$key]->getIsShowInAdminMenu()) {
continue;
}
// Если нет иконки для данной категории - подставляется иконка первого модуля
if (!isset($modulesNavigation[$keyCategory]['icon']) && $modules[$key]->icon) {
$modulesNavigation[$keyCategory]['icon'] = $modules[$key]->icon;
}
// Шаблон модулей
$data = [
'icon' => $modules[$key]->icon,
'label' => $modules[$key]->name,
'url' => $modules[$key]->adminPageLinkNormalize,
'submenuOptions' => ["id" => "submenu_".$key],
'items' => [],
];
// Добавляем подменю у модулей
$links = $modules[$key]->getNavigation();
if (!empty($links)) {
$data['items'] = $links;
} else {
unset($modSettings[0]);
}
if ($key !== self::CORE_MODULE) {
$data['items'] = array_merge(
$data['items'],
$key === self::CORE_MODULE ? [] : $modSettings
);
}
$modulesNavigation[$keyCategory]['items'][$modules[$key]->id] = $data;
}
}
foreach ($modulesNavigation as $key => $data) {
if (count($data['items']) === 1) {
$items = array_shift($modulesNavigation[$key]['items']);
$modulesNavigation[$key]['items'] = $items['items'];
}
}
// Цепочка зависимостей:
$chain = new CChainedCacheDependency();
// Зависимость на тег:
$chain->dependencies->add(
new TagsCache('yupe', 'navigation', 'installedModules')
);
// Зависимость на каталог 'application.config.modules':
$chain->dependencies->add(
new CDirectoryCacheDependency(
Yii::getPathOfAlias('application.config.modules')
)
);
Yii::app()->getCache()->set(
'YupeModulesNavigation-'.Yii::app()->getLanguage(),
$modulesNavigation,
0,
$chain
);
}
}
// Подгрузка отключенных модулей
if ($disableModule) {
$modules = array_merge((array)$this->getModulesDisabled($modules), $modules);
}
$modulesNavigation = array_merge($modulesNavigation, $modulesExtendedNavigation);
return ($navigationOnly === true) ? $modulesNavigation : [
'modules' => $modules,
'yiiModules' => $yiiModules,
'modulesNavigation' => $modulesNavigation,
];
}
/**
* Подгружает и выдает список отключенных модулей
*
* @param array $enableModule - список активных модулей, по умолчанию array()
*
* @since 0.5
*
* @return array список отключенных модулей
*/
public function getModulesDisabled($enableModule = [])
{
if (($imports = Yii::app()->getCache()->get('pathForImports')) !== false) {
Yii::app()->getModule('yupe')->setImport($imports);
}
try {
if ($imports === false || ($modules = Yii::app()->getCache()->get('modulesDisabled')) == false) {
$modConfigs = Yii::getPathOfAlias('application.config.modules');
$modPath = Yii::getPathOfAlias('application.modules');
$cacheFile = Yii::app()->configManager->cacheFileName;
foreach (new GlobIterator($modConfigs.'/*.php') as $item) {
if (is_dir(
$modPath.'/'.$item->getBaseName('.php')
) == false && $cacheFile != $item->getBaseName('.php')
) {
Yii::app()->getCache()->flush();
unlink($modConfigs.'/'.$item->getBaseName());
throw new Exception(
Yii::t(
'YupeModule.yupe',
'There is an error occurred when try get modules from the cache. It seems that module\'s folder was deleted. Module is {module}...',
[
'module' => $item->getBaseName(),
]
)
);
}
}
$path = $this->getModulesConfigDefault();
$enableModule = array_keys($enableModule);
$modules = [];
$imports = [];
if ($handler = opendir($path)) {
while ($dir = readdir($handler)) {
if (!$this->isValidModule($dir)) {
continue;
}
if ($dir != '.' && $dir != '..' && !is_file($dir) && !isset($enableModule[$dir])) {
$modules[$dir] = $this->getCreateModule($dir);
$imports[] = Yii::app()->getCache()->get('tmpImports');
}
}
closedir($handler);
}
$chain = new CChainedCacheDependency();
// Зависимость на тег:
$chain->dependencies->add(
new TagsCache('yupe', 'modulesDisabled', 'getModulesDisabled', 'installedModules', 'pathForImports')
);
// Зависимость на каталог 'application.config.modules':
$chain->dependencies->add(
new CDirectoryCacheDependency(
Yii::getPathOfAlias('application.config.modules')
)
);
Yii::app()->getCache()->set('modulesDisabled', $modules, 0, $chain);
Yii::app()->getCache()->set('pathForImports', $imports, 0, $chain);
}
} catch (Exception $e) {
Yii::app()->getCache()->flush();
Yii::app()->user->setFlash(
YFlashMessages::ERROR_MESSAGE,
$e->getMessage()
);
Yii::log($e->__toString(), \CLogger::LEVEL_ERROR);
return false;
}
return $modules;
}
/**
* Подгружает модуль
*
* @param array $name - имя модуля
*
* @since 0.5
* @return array класс модуля
*/
public function getCreateModule($name)
{
if (Yii::app()->hasModule($name)) {
return Yii::app()->getModule($name);
}
$path = $this->getModulesConfigDefault();
$module = null;
if ($path) {
//посмотреть внутри файл с окончанием Module.php
$files = glob($path.'/'.$name.'/'.'*Module.php');
if (count($files) === 1) {
$className = pathinfo($files[0], PATHINFO_FILENAME);
Yii::app()->getCache()->set('tmpImports', 'application.modules.'.$name.'.'.$className);
Yii::import('application.modules.'.$name.'.'.$className);
$module = Yii::createComponent($className, $name, null, false);
}
}
return $module;
}
/**
* Получаем путь к папке или файлу с конфигурацией модуля(-ей)
*
* @param bool $module - Имя модуля
*
* @since 0.5
* @return string путь к папке или файлу с конфигурацией модуля(-ей)
*/
public function getModulesConfig($module = false)
{
return Yii::app()->getBasePath().'/config/modules/'.($module ? $module.'.php' : '');
}
/**
* Получаем путь к папке или файлу с резервной конфигурацией модуля(-ей)
*
* @param string $module Имя модуля
*
* @since 0.5
* @return string путь к папке или файлу с резервной конфигурацией модуля(-ей)
*/
public function getModulesConfigBack($module = '')
{
$path = Yii::app()->getBasePath().'/config/modulesBack/';
return empty($module) ? $path : $path.$module.'.php';
}
/**
* Получаем путь к папке c дефолтной конфигурацией модуля
*
* @param string $module Имя модуля
*
* @since 0.5
* @return string путь к папке c дефолтной конфигурацией модуля или путь к модулям
*/
public function getModulesConfigDefault($module = '')
{
return empty($module) ? Yii::getPathOfAlias('application.modules') :
Yii::getPathOfAlias('application.modules.'.$module).'/install/'.$module.'.php';
}
/**
* Метод проверяет является ли каталог валидным модулем Yii/Yupe
*
* @param string $module - ID модуля
*
* @since 0.6
*
* @return boolean true - модуль валиде false - нет
*/
public function isValidModule($module)
{
if (!$module) {
return false;
}
$modulePath = Yii::app()->moduleManager->getModulesConfigDefault().DIRECTORY_SEPARATOR.$module;
if (!is_dir($modulePath)) {
return false;
}
$files = glob($modulePath.DIRECTORY_SEPARATOR.'*Module.php');
return empty($files) ? false : true;
}
/**
* Обновить конфигурационный файл модуля
*
* @param WebModule $module
* @return bool
* @since 0.8
*/
public function updateModuleConfig(WebModule $module)
{
$newConfig = $this->getModulesConfigDefault($module->getId());
$currentConfig = $this->getModulesConfig($module->getId());
if ((!file_exists($currentConfig) || YFile::rmFile($currentConfig)) && YFile::cpFile(
$newConfig,
$currentConfig
)
) {
Yii::app()->configManager->flushDump();
return true;
}
return false;
}
}