' . t('About') . ''; $output .= '

' . t('The Admin Toolbar Links Access Filter module provides a workaround for the common problem that users with Use the administration pages and help permission see menu links they done not have access permission for.') . '

'; return $output; } } /** * Implements hook_preprocess_menu(). * * Hides links from admin menu, if user doesn't have access rights. */ function admin_toolbar_links_access_filter_preprocess_menu(&$variables) { if (empty($variables['items'])) { // Additional empty check to prevent exotic situations, where the preprocess // function is entered even without items. // @see https://www.drupal.org/node/2833885 return; } // Ensure that menu_name exists. if (!isset($variables['menu_name'])) { // In rare cases (for unknown reasons) menu_name may not be set. // As fallback, we can fetch it from the first menu item. $first_link = reset($variables['items']); /** @var Drupal\Core\Menu\MenuLinkDefault $original_link */ // Fetch the menu_name from the original link. $original_link = $first_link['original_link']; $variables['menu_name'] = $original_link->getMenuName(); } if ($variables['menu_name'] == 'admin') { if (!admin_toolbar_links_access_filter_user_has_admin_role($variables['user'])) { admin_toolbar_links_access_filter_filter_non_accessible_links($variables['items']); } } } /** * Hides links from admin menu, if user doesn't have access rights. */ function admin_toolbar_links_access_filter_filter_non_accessible_links(array &$items) { foreach ($items as $route => &$item) { $route_name = $route; $route_params = []; if (!empty($item['original_link'])) { /** @var \Drupal\Core\Menu\MenuLinkBase $original_link */ $original_link = $item['original_link']; if ($original_link->getUrlObject()->isExternal()) { // Do not filter external URL at all. continue; } $route_name = $original_link->getRouteName(); $route_params = $original_link->getRouteParameters(); } // Check, if user has access rights to the route. if (!\Drupal::accessManager()->checkNamedRoute($route_name, $route_params)) { unset($items[$route]); } else { if (!empty($items[$route]['below'])) { // Recursively call this function for the child items. admin_toolbar_links_access_filter_filter_non_accessible_links($items[$route]['below']); } if (empty($items[$route]['below']) && \Drupal::moduleHandler()->moduleExists('admin_toolbar')) { // Every child item has been cleared out. // Now check, if the given route represents an overview page only, // without having functionality on its own. In this case, we can safely // unset this item, as there aren't any children left. // This assumption is only valid, when the admin_toolbar module is // installed because otherwise we won't have child items at all. if (admin_toolbar_links_access_filter_is_overview_page($route)) { unset($items[$route]); } else { // Let's remove the expanded flag. $items[$route]['is_expanded'] = FALSE; } } } } } /** * Implements template_preprocess_admin_block_content(). */ function admin_toolbar_links_access_filter_admin_block_content(&$variables) { if (!admin_toolbar_links_access_filter_user_has_admin_role($variables['user'])) { foreach ($variables['content'] as $key => &$item) { if (isset($item['url']) && $item['url'] instanceof Url) { /* @var \Drupal\Core\Url $url */ $url = $item['url']; if ($url->access()) { continue; } unset($variables['content'][$key]); } // The key is structured in the form: "ID title route", // concatenated with spaces. $key_parts = explode(' ', $key); $route = end($key_parts); // Special handling for Views pages, as they are not defined // system routes. // @TODO check the permission for Views + find a generic way for similar // cases. Best way would be to get the link entity somehow to properly // check permissions. if (strpos($route, 'views_view:') === 0) { continue; } // Check, if user has access rights to the route. if (!\Drupal::accessManager()->checkNamedRoute($route)) { unset($variables['content'][$key]); } } } } /** * Checks if the given route name is an overview page. * * Checks if the given route name matches a pure (admin) overview page that can * be skipped, if there are no child items set. The typical example are routes * having the SystemController::systemAdminMenuBlockPage() function as their * controller callback set. * * @param string $route_name * The route name to check. * * @return bool * TRUE, if the given route name matches a pure admin overview page route, * FALSE otherwise. */ function admin_toolbar_links_access_filter_is_overview_page($route_name) { // @var \Drupal\Core\Routing\RouteProviderInterface $route_provider. $route_provider = \Drupal::service('router.route_provider'); $overview_page_controllers = [ '\Drupal\system\Controller\AdminController::index', '\Drupal\system\Controller\SystemController::overview', '\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage', ]; try { $route = $route_provider->getRouteByName($route_name); $controller = $route->getDefault('_controller'); return !empty($controller) && in_array($controller, $overview_page_controllers); } catch (RouteNotFoundException $ex) { } return FALSE; } /** * Checks, if the given user has admin rights. * * @param \Drupal\Core\Session\AccountInterface $account * The account to check. * * @return bool * TRUE, if the given user account has at least one role with admin rights * assigned, FALSE otherwise. */ function admin_toolbar_links_access_filter_user_has_admin_role(AccountInterface $account) { static $user_has_admin_role = []; $uid = $account->id(); if (!isset($user_has_admin_role[$uid])) { $roles = Role::loadMultiple($account->getRoles()); foreach ($roles as $role) { if ($role->isAdmin()) { $user_has_admin_role[$uid] = TRUE; break; } $user_has_admin_role[$uid] = FALSE; } } return $user_has_admin_role[$uid]; }