View source
<?php
namespace Drupal\ultimenu;
use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Security\TrustedCallbackInterface;
use Drupal\Core\Theme\Registry;
use Symfony\Component\DependencyInjection\ContainerInterface;
class UltimenuManager extends UltimenuBase implements UltimenuManagerInterface, TrustedCallbackInterface {
protected $moduleHandler;
protected $renderer;
protected $menuBlocks;
protected $blocks;
protected $regions;
protected $enabledRegions;
protected $regionsByMenu;
protected $menuOptions;
protected $tree;
protected $tool;
public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, BlockManagerInterface $block_manager, ModuleHandlerInterface $module_handler, RendererInterface $renderer, UltimenuTreeInterface $tree, UltimenuToolInterface $tool) {
parent::__construct($config_factory, $entity_type_manager, $block_manager);
$this->moduleHandler = $module_handler;
$this->renderer = $renderer;
$this->tree = $tree;
$this->tool = $tool;
}
public static function create(ContainerInterface $container) {
return new static($container
->get('config.factory'), $container
->get('entity_type.manager'), $container
->get('plugin.manager.block'), $container
->get('module_handler'), $container
->get('renderer'), $container
->get('ultimenu.tree'), $container
->get('ultimenu.tool'));
}
public static function trustedCallbacks() {
return [
'preRenderBuild',
];
}
public function getModuleHandler() {
return $this->moduleHandler;
}
public function getRenderer() {
return $this->renderer;
}
public function getTool() {
return $this->tool;
}
public function getTree() {
return $this->tree;
}
public function getMenus() {
if (!isset($this->menuOptions)) {
$this->menuOptions = $this->tree
->getMenus();
}
return $this->menuOptions;
}
public function getUltimenuBlocks() {
if (!isset($this->menuBlocks)) {
$this->menuBlocks = [];
$blocks = $this
->getSetting('blocks');
foreach ($this
->getMenus() as $delta => $nice_name) {
if (!empty($blocks[$delta])) {
$this->menuBlocks[$delta] = $this
->t('@name', [
'@name' => $nice_name,
]);
}
}
asort($this->menuBlocks);
}
return $this->menuBlocks;
}
public function build(array $build = []) {
$build = [
'#theme' => 'ultimenu',
'#items' => [],
'#build' => $build,
'#pre_render' => [
[
$this,
'preRenderBuild',
],
],
];
$this->moduleHandler
->alter('ultimenu_build', $build);
return $build;
}
public function preRenderBuild(array $element) {
$build = $element['#build'];
$config = $build['config'];
unset($build, $element['#build']);
$config['current_path'] = Url::fromRoute('<current>')
->toString();
$goodies = $this
->getSetting('goodies');
$tree_access_cacheability = new CacheableMetadata();
$tree_link_cacheability = new CacheableMetadata();
$items = $this
->buildMenuTree($config, $tree_access_cacheability, $tree_link_cacheability);
$tree_cacheability = $tree_access_cacheability
->merge($tree_link_cacheability);
$tree_cacheability
->applyTo($element);
$element['#config'] = $config;
$element['#items'] = $items;
$element['#attached'] = $this
->attach($config);
$element['#cache']['tags'][] = 'config:ultimenu.' . $config['menu_name'];
return $element;
}
public function attach(array $config = []) {
$load = [];
$load['library'][] = 'ultimenu/ultimenu';
if (!empty($config['skin_basename'])) {
$load['library'][] = 'ultimenu/skin.' . $config['skin_basename'];
}
if (!empty($config['orientation']) && strpos($config['orientation'], 'v') !== FALSE) {
$load['library'][] = 'ultimenu/vertical';
}
if (!empty($config['ajaxify'])) {
$load['library'][] = 'ultimenu/ajax';
}
if (empty($goodies['no-extras'])) {
$load['library'][] = 'ultimenu/extras';
}
if ($config['menu_name'] == 'main') {
$canvas_skin = empty($config['canvas_skin']) ? 'scalein' : $config['canvas_skin'];
$load['library'][] = 'ultimenu/hamburger';
$load['library'][] = 'ultimenu/offcanvas.' . $canvas_skin;
if (!empty($config['canvas_off']) && !empty($config['canvas_on'])) {
$js_config = [
'canvasOff' => trim(strip_tags($config['canvas_off'])),
'canvasOn' => trim(strip_tags($config['canvas_on'])),
];
$load['drupalSettings']['ultimenu'] = $js_config;
}
}
if ($mw = $this
->getSetting('ajaxmw')) {
$load['drupalSettings']['ultimenu']['ajaxmw'] = $mw;
}
$this->moduleHandler
->alter('ultimenu_attach', $load, $attach);
return $load;
}
public function buildMenuTree(array $config, CacheableMetadata &$tree_access_cacheability, CacheableMetadata &$tree_link_cacheability) {
$menu_name = $config['menu_name'];
$active_trails = $this->tree
->getMenuActiveTrail()
->getActiveTrailIds($menu_name);
$tree = $this->tree
->loadMenuTree($menu_name);
if (empty($tree)) {
return [];
}
$ultimenu = [];
foreach ($tree as $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) {
$tree_access_cacheability = $tree_access_cacheability
->merge(CacheableMetadata::createFromObject($data->access));
}
$tree_link_cacheability = $tree_link_cacheability
->merge(CacheableMetadata::createFromObject($data->link));
if ($data->access instanceof AccessResultInterface && !$data->access
->isAllowed()) {
continue;
}
$ultimenu[$data->link
->getPluginId()] = $this
->buildMenuItem($data, $active_trails, $config);
}
return $ultimenu;
}
public function buildMenuItem($data, array $active_trails, array $config) {
$goodies = $this
->getSetting('goodies');
$link = $data->link;
$url = $link
->getUrlObject();
$mlid = $link
->getPluginId();
$titles = $this->tool
->extractTitleHtml($link);
$title = $titles['title'];
$title_html = $titles['title_html'];
$li_classes = $li_attributes = $li_options = [];
$flyout = '';
$region = $this->tool
->getRegionKey($link);
$config['has_submenu'] = !empty($config['submenu']) && $link
->isExpanded() && $data->hasChildren;
$config['is_ajax_region'] = FALSE;
$config['is_active'] = array_key_exists($mlid, $active_trails);
$config['title'] = $title;
$config['mlid'] = $mlid;
$li_options['title-class'] = $title;
$li_options['mlid-hash-class'] = $this->tool
->getShortenedHash($mlid);
if (!empty($goodies['mlid-class'])) {
$li_options['mlid-class'] = $link
->getRouteName() == '<front>' ? 'front_page' : $this->tool
->getShortenedUuid($mlid);
}
$link_options = $link
->getOptions();
if ($url
->isRouted()) {
if ($config['is_active']) {
$li_classes[] = 'is-active-trail';
}
if ($link
->getRouteName() == '<front>') {
if ($this->tool
->getPathMatcher()
->isFrontPage()) {
$li_classes[] = 'is-active-trail';
}
}
$link_options['set_active_class'] = TRUE;
$system_path = $url
->getInternalPath();
if ($url
->getRouteName() === '<front>') {
$system_path = '<front>';
}
$link_options['attributes']['data-drupal-link-system-path'] = $system_path;
$config['system_path'] = $system_path;
}
if (!empty($goodies['no-tooltip'])) {
$link_options['attributes']['title'] = '';
}
foreach ($li_options as $li_key => $li_value) {
if (!empty($goodies[$li_key])) {
$li_classes[] = Html::cleanCssIdentifier(mb_strtolower('uitem--' . str_replace('_', '-', $li_value)));
}
}
if ($url
->isExternal()) {
$link_options['attributes']['class'][] = 'is-external';
}
if (!empty($goodies['counter-class'])) {
static $item_id = 0;
$li_classes[] = 'uitem--' . ++$item_id;
}
$li_attributes['class'] = array_merge([
'ultimenu__item',
'uitem',
], $li_classes);
$flyout = $this
->getFlyout($region, $config);
$orientation = 'is-' . str_replace('ultimenu--', '', $config['orientation']);
$flyout_attributes['class'] = [
'ultimenu__flyout',
$orientation,
];
if (!empty($flyout)) {
if ($config['is_ajax_region']) {
$flyout_attributes['data-ultiajax-region'] = $region;
$link_options['attributes']['data-ultiajax-trigger'] = TRUE;
}
$title_html .= '<span class="caret" aria-hidden="true"></span>';
}
$extra_classes = isset($link_options['attributes']['class']) ? $link_options['attributes']['class'] : [];
if (!is_array($extra_classes)) {
$extra_classes = [
$extra_classes,
];
}
$link_options['attributes']['class'] = $extra_classes ? array_merge([
'ultimenu__link',
], $extra_classes) : [
'ultimenu__link',
];
$link_element = [
'#type' => 'link',
'#options' => $link_options,
'#url' => $url,
'#title' => [
'#markup' => $title_html,
'#allowed_tags' => [
'b',
'em',
'i',
'small',
'span',
'strong',
],
],
];
return [
'link' => $link_element,
'flyout' => $flyout,
'attributes' => new Attribute($li_attributes),
'flyout_attributes' => new Attribute($flyout_attributes),
'config' => $config,
];
}
public function buildAjaxLink(array $config = []) {
return [
'#type' => 'link',
'#title' => strip_tags($this
->getFallbackText()),
'#attributes' => [
'class' => [
'ultimenu__ajax',
'use-ajax',
],
'rel' => 'nofollow',
'id' => Html::getUniqueId('ultiajax-' . $this->tool
->getShortenedHash($config['mlid'])),
],
'#url' => Url::fromRoute('ultimenu.ajax', [
'mlid' => $config['mlid'],
'sub' => $config['has_submenu'] ? 1 : 0,
]),
];
}
public function getFallbackText() {
return $this
->t('@text', [
'@text' => $this
->getSetting('fallback_text') ?: 'Loading... Click here if it takes longer.',
]);
}
public function getFlyout($region, array &$config) {
$flyout = [];
if ($regions = $this
->getSetting('regions')) {
if (!empty($regions[$region])) {
if (empty($config['ajaxify'])) {
$flyout = $this
->buildFlyout($region, $config);
}
else {
$ajax_regions = isset($config['regions']) ? array_filter($config['regions']) : [];
$config['is_ajax_region'] = $ajax_regions && in_array($region, $ajax_regions);
$flyout = $config['is_ajax_region'] ? $this
->buildAjaxLink($config) : $this
->buildFlyout($region, $config);
}
}
}
return $flyout;
}
public function buildFlyout($region, array $config) {
$build = $content = [];
$reverse = FALSE;
$count = 0;
if (!empty($config['has_submenu'])) {
$reverse = !empty($config['submenu_position']) && $config['submenu_position'] == 'bottom';
$content[] = $this->tree
->loadSubMenuTree($config['menu_name'], $config['mlid'], $config['title']);
}
if ($blocks = $this
->getBlocksByRegion($region, $config)) {
$content[] = $blocks;
$count = count($blocks);
}
if ($content = array_filter($content)) {
$config['count'] = $count;
$build['content'] = $reverse ? array_reverse($content, TRUE) : $content;
$build['#config'] = $config;
$build['#region'] = $region;
$build['#sorted'] = TRUE;
$attributes['class'][] = 'ultimenu__region';
if ($count) {
$attributes['class'][] = 'region';
$attributes['class'][] = 'region--count-' . $count;
}
$build['#attributes'] = $attributes;
$build['#theme_wrappers'][] = 'region';
}
return $build;
}
public function getBlocksByRegion($region, array $config) {
if (!isset($this->blocks[$region])) {
$build = [];
$blocks = $this->entityTypeManager
->getStorage('block')
->loadByProperties([
'theme' => $this
->getThemeDefault(),
'region' => $region,
]);
if ($blocks) {
uasort($blocks, 'Drupal\\block\\Entity\\Block::sort');
if (empty($config['ajaxify'])) {
foreach ($blocks as $key => $block) {
if ($block
->access('view')) {
$build[$key] = $this->entityTypeManager
->getViewBuilder($block
->getEntityTypeId())
->view($block, 'block');
}
}
}
else {
foreach ($blocks as $key => $block) {
if ($this->tool
->isAllowedBlock($block, $config)) {
$build[$key] = $this->entityTypeManager
->getViewBuilder($block
->getEntityTypeId())
->view($block, 'block');
}
}
}
}
$this->blocks[$region] = $build;
}
return $this->blocks[$region];
}
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->tree
->loadMenuTree($delta);
}
$regions = [];
foreach ($menus as $menu_name => $tree) {
foreach ($tree as $item) {
$name_id = $this->tool
->truncateRegionKey($menu_name);
$name_id_nice = str_replace("_", " ", $name_id);
$link = $item->link;
$menu_title = $this->tool
->getTitle($link);
$region_key = $this->tool
->getRegionKey($link);
$regions[$region_key] = "Ultimenu:{$name_id_nice}: {$menu_title}";
}
}
$this->regions = $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 getRegionsByMenu($menu_name) {
if (!isset($this->regionsByMenu[$menu_name])) {
$regions = [];
foreach ($this
->getEnabledRegions() as $key => $region_name) {
if (strpos($key, 'ultimenu_' . $menu_name . '_') === FALSE) {
continue;
}
$regions[$key] = $region_name;
}
$this->regionsByMenu[$menu_name] = $regions;
}
return $this->regionsByMenu[$menu_name];
}
public function removeRegions() {
$goodies = $this
->getSetting('goodies');
if (empty($goodies['force-remove-region'])) {
return FALSE;
}
return $this->tool
->parseThemeInfo($this
->getRegions());
}
public function libraryInfoAlter(&$libraries, $extension) {
if ($extension === 'ultimenu') {
if ($this
->getSetting('goodies.vanilla') && $this->moduleHandler
->moduleExists('blazy')) {
$dependencies = [
'core/drupal',
'blazy/dblazy',
'ultimenu/base',
];
$libraries['ultimenu']['dependencies'] = $dependencies;
$libraries['ultimenu']['js'] = [
'js/ultimenu.vanilla.min.js' => [],
];
$libraries['ajax']['js'] = [
'js/ultimenu.ajax.vanilla.min.js' => [],
];
}
}
}
}