You are here

domain_menus.module in Domain Menus for Domains 3.x

Same filename and directory in other branches
  1. 9.x domain_menus.module
  2. 9.1.x domain_menus.module

Domain menus implementation through entity create, delete, and access customizations.

File

domain_menus.module
View source
<?php

/**
 * @file
 * Domain menus implementation through entity create, delete, and access customizations.
 */
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\system\Entity\Menu;
const DOMAIN_MENUS_MENU_ID_PATTERN = 'dm@domain_domainid-@menu_name';

# eg. dm12345678-alt, menu machine names 27 chars max
const DOMAIN_MENUS_MENU_LABEL_PATTERN = 'Domain menu for @domain_label (@menu_name)';

# pattern helps group domain menus together, a-z sort
const DOMAIN_MENUS_SETTINGS = 'domain_menus.settings';

/**
 * Implements hook_help().
 *
 * {@inheritdoc}
 */
function domain_menus_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.domain_menus':
      $text = file_get_contents(dirname(__FILE__) . "/README.txt");
      $output = nl2br($text);
      return $output;
      break;
  }
}

/**
 * Helper function to tell if a menu is a domain menu.
 * If a menu has the domain_menus third party setting, it's considered a domain menu.
 */
function _domain_menus_is_domain_menu(EntityInterface $menu) {
  $menu_domains = $menu
    ->getThirdPartySetting("domain_menus", "domains");
  if ($menu_domains !== NULL) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Helper function to get domain_menus settings by name.
 */
function _domain_menus_get_setting($key) {
  return \Drupal::config(DOMAIN_MENUS_SETTINGS)
    ->get($key);
}

/**
 * Helper function to get list of domains as form options.
 */
function _domain_menus_domain_options() {
  $options = [];
  $domains = \Drupal::entityTypeManager()
    ->getStorage('domain')
    ->loadMultiple();
  foreach ($domains as $domain) {
    $options[$domain
      ->id()] = $domain
      ->label();
  }
  return $options;
}

/**
 * Helper function to save third party settings on form submit.
 */
function _domain_menus_menu_form_submit($form, FormStateInterface &$form_state) {
  $menu = $form_state
    ->getFormObject()
    ->getEntity();

  // filter out unchecked options
  $values = array_diff($form_state
    ->getValue('domain_menus_menu_domains'), array(
    '0',
  ));
  if (!empty($values)) {
    $menu
      ->setThirdPartySetting('domain_menus', 'domains', $values);
  }
  else {
    $menu
      ->unsetThirdPartySetting('domain_menus', 'domains');
  }
  $menu
    ->save();
}

/**
 * Implements hook_form_alter().
 */
function domain_menus_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if ($form_id == 'menu_add_form' || $form_id == 'menu_edit_form') {
    if (\Drupal::currentUser()
      ->hasPermission('administer menu')) {
      $menu = $form_state
        ->getFormObject()
        ->getEntity();
      $menu_auto_created = $menu
        ->getThirdPartySetting('domain_menus', 'auto-created', 0);
      if ($menu_auto_created != 1) {
        $options = _domain_menus_domain_options();
        $default_value = $menu
          ->getThirdPartySetting('domain_menus', 'domains', []);
        $form['domain_menus_menu_domains'] = array(
          '#type' => 'checkboxes',
          '#title' => t('Domain(s)'),
          '#description' => t('Select the domain assignment of this menu. Leave empty if menu should not be considered a domain menu.'),
          '#options' => $options,
          '#default_value' => $default_value,
        );
        $form['actions']['submit']['#submit'][] = "_domain_menus_menu_form_submit";
      }
    }
  }

  // Make add menu link use Domain Menus autocomplete selection handler that filters by domain.
  if (!empty(_domain_menus_get_setting('domain_menus_filter_node_autocomplete'))) {
    if ($form_id == 'menu_link_content_menu_link_content_form') {
      $menu_link_content = $form_state
        ->getFormObject()
        ->getEntity();
      $menu_name = $menu_link_content
        ->getMenuName();
      $menu = Menu::load($menu_name);
      if (_domain_menus_is_domain_menu($menu)) {
        $type = $form['link']['widget'][0]['uri']['#type'] ?? null;
        if ($type == 'entity_autocomplete') {
          $domain_id = $menu
            ->getThirdPartySetting('domain_menus', 'domains');

          // Use Drupal\domain_menus\Plugin\EntityReferenceSelection\DomainMenusSelection.
          $form['link']['widget'][0]['uri']['#selection_handler'] = 'domain_menus:node';
          $form['link']['widget'][0]['uri']['#selection_settings']['domain_id'] = $domain_id;
        }
      }
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter() for menu_link_content_menu_link_content_form.
 *
 * Unset all parent links that are not on the default or current domain menu.
 */
function domain_menus_form_menu_link_content_menu_link_content_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if (!empty($form['menu_parent']['#default_value'])) {
    list($default_menu_id) = explode(':', $form['menu_parent']['#default_value']);
    $menu = Menu::load($default_menu_id);
    if (_domain_menus_is_domain_menu($menu)) {
      foreach ($form['menu_parent']['#options'] as $key => $name) {
        if (strpos($key, $default_menu_id . ':') !== 0) {
          unset($form['menu_parent']['#options'][$key]);
        }
      }
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_insert().
 *
 * Create domain menus for the domain being created.
 */
function domain_menus_domain_insert(EntityInterface $entity) {
  global $install_state;
  if (isset($install_state['parameters']['existing_config'])) {
    return;
  }
  $domain = $entity;
  $domain_id = $domain
    ->id();
  $domain_domainid = $domain
    ->getDomainId();
  $domain_label = Markup::create($domain
    ->label());
  $domain_menus = _domain_menus_get_setting('domain_menus_menu_names');
  if (!empty($domain_menus)) {
    $menu_names = explode("\r\n", $domain_menus);
    if (!empty($menu_names)) {
      foreach ($menu_names as $menu_name) {
        if (!empty($menu_name)) {
          $menu_name = Markup::create($menu_name);
          $menu = Menu::create(array(
            'id' => trim(t(DOMAIN_MENUS_MENU_ID_PATTERN, [
              '@domain_domainid' => $domain_domainid,
              '@menu_name' => $menu_name,
            ])),
            'label' => trim(t(DOMAIN_MENUS_MENU_LABEL_PATTERN, [
              '@domain_label' => $domain_label,
              '@menu_name' => $menu_name,
            ])),
          ));
          $menu
            ->setThirdPartySetting('domain_menus', 'domains', array(
            $domain_id => $domain_id,
          ));
          $menu
            ->setThirdPartySetting('domain_menus', 'auto-created', 1);
          $menu
            ->save();
        }
      }
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_delete().
 *
 * Delete domain menus of the domain being deleted.
 * If a domain menu has assignment to multiple domains, remove the deleted domain from assignment instead of deleting the menu.
 */
function domain_menus_domain_delete(EntityInterface $entity) {
  $domain = $entity;
  $domain_id = $domain
    ->id();
  $domain_domainid = $domain
    ->getDomainId();
  $menus = \Drupal::entityTypeManager()
    ->getStorage('menu')
    ->loadMultiple();
  if (!empty($menus)) {
    foreach ($menus as $menu) {
      $menu_domains = $menu
        ->getThirdPartySetting("domain_menus", "domains", []);
      if (array_key_exists($domain_id, $menu_domains)) {
        unset($menu_domains[$domain_id]);
        if (empty($menu_domains)) {
          $menu
            ->delete();
        }
        else {
          $menu
            ->setThirdPartySetting('domain_menus', 'domains', $menu_domains);
          $menu
            ->save();
        }
      }
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_create_access().
 *
 * Allow menu link create access to domain menus based on domain_menus permissions
 */
function domain_menus_menu_link_content_create_access(AccountInterface $account, array $context, $entity_bundle) {
  $route = Drupal::routeMatch();
  if ($menu = $route
    ->getParameter('menu')) {
    if (_domain_menus_is_domain_menu($menu)) {
      $menu_id = $menu
        ->id();
      $menu_domains = $menu
        ->getThirdPartySetting("domain_menus", "domains", []);
      $user = \Drupal::entityTypeManager()
        ->getStorage('user')
        ->load($account
        ->id());

      //$user_domains = \Drupal::service('domain_access.manager')->getAccessValues($user);
      $user_domains_string = $user
        ->get(DOMAIN_ACCESS_FIELD)
        ->getString();
      $user_domains = explode(', ', $user_domains_string);
      $menu_and_user_domains = array_intersect($menu_domains, $user_domains);
      $active_domain = \Drupal::service('domain.negotiator')
        ->getActiveDomain();
      $active_domain_id = $active_domain
        ->id();

      // permission check order matters, do "edit assigned domain menus" first
      if ($account
        ->hasPermission('edit assigned domain menus')) {
        if (!empty($menu_and_user_domains)) {
          return AccessResult::allowed();
        }
      }
      elseif ($account
        ->hasPermission('edit active domain menus')) {
        if (in_array($active_domain_id, $menu_and_user_domains)) {
          return AccessResult::allowed();
        }
      }
    }
  }
  return AccessResult::neutral();
}

/**
 * Implements hook_ENTITY_TYPE_access().
 *
 * Allow menu link edit access in domain menus based on domain_menus permissions
 */
function domain_menus_menu_link_content_access(EntityInterface $entity, $operation, AccountInterface $account) {
  $menu_link = $entity;
  $menu_id = $menu_link
    ->getMenuName();
  $menu = Menu::load($menu_id);
  $menu_domains = $menu
    ->getThirdPartySetting("domain_menus", "domains", []);
  $user = \Drupal::entityTypeManager()
    ->getStorage('user')
    ->load($account
    ->id());

  //$user_domains = \Drupal::service('domain_access.manager')->getAccessValues($user);
  $user_domains_string = $user
    ->get(DOMAIN_ACCESS_FIELD)
    ->getString();
  $user_domains = explode(', ', $user_domains_string);
  $menu_and_user_domains = array_intersect($menu_domains, $user_domains);
  $active_domain = \Drupal::service('domain.negotiator')
    ->getActiveDomain();
  $active_domain_id = $active_domain
    ->id();

  // permission check order matters, do "edit assigned domain menus" first
  if ($account
    ->hasPermission('edit assigned domain menus')) {
    if (!empty($menu_and_user_domains)) {
      return AccessResult::allowed();
    }
  }
  elseif ($account
    ->hasPermission('edit active domain menus')) {
    if (in_array($active_domain_id, $menu_and_user_domains)) {
      return AccessResult::allowed();
    }
  }
  return AccessResult::neutral();
}

/**
 * Implements hook_ENTITY_TYPE_access().
 *
 * Allow domain menus edit access based on domain_menus permissions
 */
function domain_menus_menu_access(EntityInterface $entity, $operation, AccountInterface $account) {
  $menu = $entity;
  $menu_domains = $menu
    ->getThirdPartySetting("domain_menus", "domains", []);
  $user = \Drupal::entityTypeManager()
    ->getStorage('user')
    ->load($account
    ->id());

  //$user_domains = \Drupal::service('domain_access.manager')->getAccessValues($user);
  $user_domains_string = $user
    ->get(DOMAIN_ACCESS_FIELD)
    ->getString();
  $user_domains = explode(', ', $user_domains_string);
  $menu_and_user_domains = array_intersect($menu_domains, $user_domains);
  $active_domain = \Drupal::service('domain.negotiator')
    ->getActiveDomain();
  $active_domain_id = $active_domain
    ->id();

  // permission check order matters, do "edit assigned domain menus" first
  if ($account
    ->hasPermission('edit assigned domain menus')) {
    if (!empty($menu_and_user_domains)) {
      return AccessResult::allowed();
    }
  }
  elseif ($account
    ->hasPermission('edit active domain menus')) {
    if (in_array($active_domain_id, $menu_and_user_domains)) {
      return AccessResult::allowed();
    }
  }
  return AccessResult::neutral();
}

/**
 * Implements hook_theme_suggestions_HOOK_alter() for menu.
 */
function domain_menus_theme_suggestions_menu_alter(array &$suggestions, array $variables) {
  if (strpos($variables['theme_hook_original'], 'menu__dm') !== 0) {
    return;
  }
  $base_suggestion = 'menu__domain_menu';
  $suggestions[] = $base_suggestion;
  $parts = explode('_', $variables['theme_hook_original']);
  $suggestions[] = $base_suggestion . '__' . end($parts);
}

Functions

Namesort descending Description
domain_menus_domain_delete Implements hook_ENTITY_TYPE_delete().
domain_menus_domain_insert Implements hook_ENTITY_TYPE_insert().
domain_menus_form_alter Implements hook_form_alter().
domain_menus_form_menu_link_content_menu_link_content_form_alter Implements hook_form_FORM_ID_alter() for menu_link_content_menu_link_content_form.
domain_menus_help Implements hook_help().
domain_menus_menu_access Implements hook_ENTITY_TYPE_access().
domain_menus_menu_link_content_access Implements hook_ENTITY_TYPE_access().
domain_menus_menu_link_content_create_access Implements hook_ENTITY_TYPE_create_access().
domain_menus_theme_suggestions_menu_alter Implements hook_theme_suggestions_HOOK_alter() for menu.
_domain_menus_domain_options Helper function to get list of domains as form options.
_domain_menus_get_setting Helper function to get domain_menus settings by name.
_domain_menus_is_domain_menu Helper function to tell if a menu is a domain menu. If a menu has the domain_menus third party setting, it's considered a domain menu.
_domain_menus_menu_form_submit Helper function to save third party settings on form submit.

Constants