View source
<?php
namespace Drupal\ultimenu;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\Menu\MenuActiveTrailInterface;
use Drupal\Core\Menu\MenuLinkTreeInterface;
use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Unicode;
use Drupal\system\Entity\Menu;
class UltimenuManager extends DefaultPluginManager implements UltimenuManagerInterface {
use StringTranslationTrait;
protected $entityTypeManager;
protected $configFactory;
protected $moduleHandler;
protected $themeHandler;
protected $menuTree;
protected $menuActiveTrail;
protected $menuOptions;
protected $menuBlocks;
protected $regions;
protected $enabledRegions;
protected $themeRegions;
protected $skinPath;
protected $skins;
public function __construct(EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, MenuLinkTreeInterface $menu_tree, MenuActiveTrailInterface $menu_active_trail, CacheBackendInterface $cache_backend) {
$this->entityTypeManager = $entity_type_manager;
$this->configFactory = $config_factory;
$this->moduleHandler = $module_handler;
$this->themeHandler = $theme_handler;
$this->menuTree = $menu_tree;
$this->menuActiveTrail = $menu_active_trail;
$this
->setCacheBackend($cache_backend, 'ultimenu', [
'ultimenu',
]);
$this
->alterInfo('ultimenu_info');
}
public function getEntityTypeManager() {
return $this->entityTypeManager;
}
public function getThemeHandler() {
return $this->themeHandler;
}
public function getModuleHandler() {
return $this->moduleHandler;
}
public function getConfig($config = 'ultimenu.settings') {
return $this->configFactory
->get($config);
}
public function getSetting($setting_name = NULL) {
return $this
->getConfig()
->get($setting_name);
}
public function getMenus() {
if (!isset($this->menuOptions)) {
if ($custom_menus = Menu::loadMultiple()) {
foreach ($custom_menus as $menu_name => $menu) {
$custom_menus[$menu_name] = Html::escape($menu
->label());
}
}
$exclude_menus = [
'admin' => $this
->t('Administration'),
'devel' => $this
->t('Development'),
'tools' => $this
->t('Tools'),
];
$this->menuOptions = array_diff_key($custom_menus, $exclude_menus);
asort($this->menuOptions);
}
return $this->menuOptions;
}
public function getUltimenuBlocks() {
if (!isset($this->menuBlocks)) {
$this->menuBlocks = [];
$menus = $this
->getMenus();
foreach ($menus as $delta => $name) {
if ($this
->getEnabledBlocks($delta)) {
$this->menuBlocks[$delta] = $this
->t('@name', [
'@name' => $name,
]);
}
}
asort($this->menuBlocks);
}
return $this->menuBlocks;
}
public function getEnabledBlocks($delta) {
$blocks = $this
->getSetting('blocks');
if (!empty($blocks[$delta])) {
return TRUE;
}
return FALSE;
}
public function getRegionKey($menu_name, $key, $title, $max_length = 28) {
$goodies = $this
->getSetting('goodies');
$is_mlid = isset($goodies['ultimenu-mlid']) && $goodies['ultimenu-mlid'];
$is_hash = isset($goodies['ultimenu-mlid-hash']) && $goodies['ultimenu-mlid-hash'];
$menu_name = $this
->truncateRegionKey($menu_name, $max_length);
if ($is_hash) {
$menu_item = $this
->getShortenedHash($key);
}
elseif ($is_mlid) {
$menu_item = $this
->getShortenedUuid($key);
}
else {
$menu_item = $this
->truncateRegionKey(strip_tags($title), $max_length);
}
return 'ultimenu_' . $menu_name . '_' . $menu_item;
}
protected function getShortenedHash($key) {
return substr(sha1($key), 0, 8);
}
protected function getShortenedUuid($key) {
list(, $uuid) = array_pad(array_map('trim', explode(":", $key, 2)), 2, NULL);
$uuid = str_replace('.', '__', $uuid ?: $key);
list($shortened_uuid, ) = array_pad(array_map('trim', explode("-", $uuid, 2)), 2, NULL);
return $shortened_uuid;
}
public function truncateRegionKey($string, $max_length = 28) {
$langcode = \Drupal::languageManager()
->getCurrentLanguage()
->getId();
$trans = \Drupal::transliteration();
$transformed = $trans
->transliterate($string, $langcode);
$transformed = Html::decodeEntities($transformed);
$transformed = Unicode::strtolower(str_replace(array(
'menu-',
'-menu',
), '', $transformed));
$transformed = preg_replace('/[\\W\\s]+/', '_', $transformed);
$transformed = trim($transformed, '_');
$transformed = Unicode::truncate($transformed, $max_length, TRUE, FALSE);
return $transformed;
}
public function loadMenuTree($menu_name) {
$menu_tree = $this->menuTree;
$parameters = new MenuTreeParameters();
$parameters
->setTopLevelOnly()
->onlyEnabledLinks();
$tree = $menu_tree
->load($menu_name, $parameters);
$manipulators = [
[
'callable' => 'menu.default_tree_manipulators:checkAccess',
],
[
'callable' => 'menu.default_tree_manipulators:generateIndexAndSort',
],
[
'callable' => 'menu.default_tree_manipulators:flatten',
],
];
$tree = $menu_tree
->transform($tree, $manipulators);
return $tree;
}
public function loadSubMenuTree($menu_name, $link_id = '', $title = '') {
$build = [];
if (empty($link_id)) {
return $build;
}
$level = 1;
$depth = 4;
$menu_tree = $this->menuTree;
$parameters = $menu_tree
->getCurrentRouteMenuTreeParameters($menu_name);
$parameters
->setRoot($link_id)
->excludeRoot()
->onlyEnabledLinks();
$parameters
->setMaxDepth(min($level + $depth - 1, $menu_tree
->maxDepth()));
$tree = $menu_tree
->load($menu_name, $parameters);
$manipulators = [
[
'callable' => 'menu.default_tree_manipulators:checkAccess',
],
[
'callable' => 'menu.default_tree_manipulators:generateIndexAndSort',
],
];
$tree = $menu_tree
->transform($tree, $manipulators);
if ($tree) {
$build['content'] = $menu_tree
->build($tree);
$css_name = Html::cleanCssIdentifier(Unicode::strtolower($menu_name . '-' . $title));
$build['#attributes']['class'] = [
'ultimenusub',
'ultimenusub--' . $css_name,
];
$build['#theme_wrappers'][] = 'container';
}
return $build;
}
public function buildMenuTree($menu_name, array $config) {
$tree = $this
->loadMenuTree($menu_name);
$submenu_enabled = !empty($config['submenu']);
$ultimenu = [];
foreach ($tree as $key => $data) {
if (!$data->link
->isEnabled()) {
continue;
}
if ($data->access !== NULL && !$data->access instanceof AccessResultInterface) {
throw new \DomainException('MenuLinkTreeElement::access must be either NULL or an AccessResultInterface object.');
}
if ($data->access instanceof AccessResultInterface && !$data->access
->isAllowed()) {
continue;
}
$class = [];
$element = [];
$link = $data->link;
$url = $link
->getUrlObject();
$title = $link
->getTitle();
$has_children = isset($data->hasChildren) && $data->hasChildren == TRUE;
$expanded = $link
->isExpanded();
$plugin_id = $link
->getPluginId();
$show_submenu = $submenu_enabled && $expanded && $has_children;
$children = $show_submenu ? $this
->loadSubMenuTree($menu_name, $plugin_id, $title) : [];
$definition = $link
->getPluginDefinition();
if ($url
->isExternal()) {
$class[] = 'external';
}
$region['region'] = $this
->getRegionKey($config['menu_name'], $plugin_id, $title);
$flyout = '';
if ($regions = $this
->getSetting('regions')) {
if (!empty($regions[$region['region']])) {
$region['config'] = $config;
$flyout = $this
->buildDataRegion($region, $children);
}
}
$element['attributes']['class'] = $class;
$element['title'] = $title;
$element['url'] = $url;
$element['options'] = $link
->getOptions();
$element['description'] = $link
->getDescription();
$element['flyout'] = $flyout;
$element['config'] = $config;
if ($definition['route_name'] == '<front>') {
$shortened_uuid = 'front_page';
}
else {
$shortened_uuid = $this
->getShortenedUuid($definition['id']);
}
$element['uuid'] = $definition['id'];
$element['shortened_uuid'] = $shortened_uuid;
$element['shortened_hash'] = $this
->getShortenedHash($definition['id']);
$ultimenu[$link
->getPluginId()] = $element;
unset($link);
}
return $ultimenu;
}
public function getRegions() {
if (!isset($this->regions)) {
$blocks = $this
->getSetting('blocks');
$menu_blocks = is_array($blocks) ? array_filter($blocks) : [
$blocks,
];
$menus = [];
foreach ($menu_blocks as $delta => $title) {
$menus[$delta] = $this
->loadMenuTree($delta);
}
$this->moduleHandler
->alter('ultimenu_menus_info', $menus);
$this->regions = [];
foreach ($menus as $menu_name => $tree) {
$tree = $this->menuTree
->build($tree);
if (isset($tree['#items'])) {
foreach ($tree['#items'] as $key => $item) {
$name_id = $this
->truncateRegionKey($menu_name);
$name_id_nice = str_replace("_", " ", $name_id);
if ($item['title']) {
$menu_title = strip_tags($item['title']);
$region_key = $this
->getRegionKey($menu_name, $key, $menu_title);
$this->regions[$region_key] = "Ultimenu:{$name_id_nice}: {$menu_title}";
$this->regions = array_unique($this->regions);
}
}
}
}
$this->moduleHandler
->alter('ultimenu_regions_info', $this->regions);
}
return $this->regions;
}
public function getEnabledRegions() {
if (!isset($this->enabledRegions)) {
$this->enabledRegions = [];
$regions_all = $this
->getRegions();
if (($regions_enabled = $this
->getSetting('regions')) !== NULL) {
foreach (array_filter($regions_enabled) as $enabled) {
if (array_key_exists($enabled, $regions_all)) {
$this->enabledRegions[$enabled] = $regions_all[$enabled];
}
}
}
}
return $this->enabledRegions;
}
public function buildDataRegion(&$region, $children = []) {
$build = [];
$blocks = [];
$config = $region['config'];
$reverse = FALSE;
$content = [];
if ($children) {
$reverse = !empty($config['submenu_position']) && $config['submenu_position'] == 'bottom';
$content[] = $children;
}
if ($blocks = $this
->getBlocksByRegion($region['region'])) {
$content[] = $blocks;
}
if ($content = array_filter($content)) {
$build['content'] = $reverse ? array_reverse($content, TRUE) : $content;
$build['#config'] = $config;
$build['#region'] = $region['region'];
$build['#sorted'] = TRUE;
$build['#theme_wrappers'][] = 'region';
}
return $build;
}
public function getBlocksByRegion($region) {
$build = [];
$theme = $this
->getConfig('system.theme')
->get('default');
$blocks = $this->entityTypeManager
->getStorage('block')
->loadByProperties([
'theme' => $theme,
'region' => $region,
]);
if ($blocks) {
uasort($blocks, 'Drupal\\block\\Entity\\Block::sort');
foreach ($blocks as $key => $block) {
if ($block
->access('view')) {
$build[$key] = $this->entityTypeManager
->getViewBuilder($block
->getEntityTypeId())
->view($block, 'block');
}
}
}
return $build;
}
public function parseThemeInfo() {
if (!isset($this->themeRegions)) {
$theme = $this
->getConfig('system.theme')
->get('default');
$file = drupal_get_path('theme', $theme) . '/' . $theme . '.info.yml';
$info = \Drupal::service('info_parser')
->parse($file);
$this->themeRegions = [];
foreach ($info['regions'] as $key => $region) {
if (array_key_exists($key, $this
->getRegions())) {
$this->themeRegions[$key] = $region;
}
}
}
return $this->themeRegions;
}
public function removeRegions() {
$goodies = $this
->getSetting('goodies');
if (!empty($goodies['force-remove-region']) && ($theme_regions = $this
->parseThemeInfo())) {
return $theme_regions;
}
return FALSE;
}
public function getSkinPath($path) {
if (!isset($this->skinPath)) {
$this->skinPath = '';
list(, $skin_name) = array_pad(array_map('trim', explode("|", $path, 2)), 2, NULL);
if (strpos($path, "module|") !== FALSE) {
$skin = drupal_get_path('module', 'ultimenu') . '/skins';
$this->skinPath = $skin . '/' . $skin_name . '.css';
}
elseif (strpos($path, "custom|") !== FALSE) {
$skin = $this
->getSetting('skins');
$this->skinPath = $skin . '/' . $skin_name . '.css';
}
elseif (strpos($path, "theme|") !== FALSE) {
$theme_default = $this
->getConfig('system.theme')
->get('default');
$skin = drupal_get_path('theme', $theme_default) . '/css/ultimenu';
$this->skinPath = $skin . '/' . $skin_name . '.css';
}
if ($this->skinPath) {
$this->skinPath = '/' . $this->skinPath;
}
}
return $this->skinPath;
}
public function getSkinName($path) {
$skin_name = \Drupal::service('file_system')
->basename($path, '.css');
$skin_basename = str_replace("ultimenu--", "", $skin_name);
$skin_basename = str_replace("-", "_", $skin_basename);
return $skin_basename;
}
public function getSkins() {
if (!isset($this->skins)) {
if ($cache = $this->cacheBackend
->get($this->cacheKey . ':skin')) {
$this->skins = $cache->data;
}
else {
$theme_default = $this
->getConfig('system.theme')
->get('default');
$theme_skin = drupal_get_path('theme', $theme_default) . '/css/ultimenu';
$custom_skin = $this
->getSetting('skins');
$module_skin = drupal_get_path('module', 'ultimenu') . '/skins';
$mask = '/.css$/';
$files = [];
if (is_dir($module_skin)) {
foreach (file_scan_directory($module_skin, $mask) as $filename => $file) {
$files[$filename] = $file;
}
}
if (!empty($custom_skin) && is_dir($custom_skin)) {
foreach (file_scan_directory($custom_skin, $mask) as $filename => $file) {
$files[$filename] = $file;
}
}
if (is_dir($theme_skin)) {
foreach (file_scan_directory($theme_skin, $mask) as $filename => $file) {
$files[$filename] = $file;
}
}
if ($files) {
$skins = [];
foreach ($files as $file) {
$uri = $file->uri;
$name = $file->name;
if (strpos($uri, $module_skin) !== FALSE) {
$uri = "module|" . $name;
}
elseif (!empty($custom_skin) && strpos($uri, $custom_skin) !== FALSE) {
$uri = "custom|" . $name;
}
elseif (is_dir($theme_skin) && strpos($uri, $theme_skin) !== FALSE) {
$uri = "theme|" . $name;
}
$skins[$uri] = Html::cleanCssIdentifier(Unicode::strtolower($name));
}
$this->cacheBackend
->set($this->cacheKey . ':skin', $skins, Cache::PERMANENT, array(
'skin',
));
$this->skins = $skins;
}
}
}
return $this->skins;
}
public function clearCachedDefinitions($all = FALSE) {
$this->themeHandler
->refreshInfo();
if ($all) {
$this->skins = NULL;
if ($this->moduleHandler
->moduleExists('block')) {
\Drupal::service('plugin.manager.block')
->clearCachedDefinitions();
}
}
}
}