123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- <?php
- /**
- * @file
- * This module don't show menu links that you don't have access permission for.
- */
- use Drupal\Core\Session\AccountInterface;
- use Drupal\Core\Url;
- use Drupal\user\Entity\Role;
- use Symfony\Component\Routing\Exception\RouteNotFoundException;
- use Drupal\Core\Routing\RouteMatchInterface;
- /**
- * Implements hook_help().
- */
- function admin_toolbar_links_access_filter_help($route_name, RouteMatchInterface $route_match) {
- switch ($route_name) {
- // Main module help.
- case 'help.page.admin_toolbar_links_access_filter':
- $output = '';
- $output .= '<h3>' . t('About') . '</h3>';
- $output .= '<p>' . t('The Admin Toolbar Links Access Filter module provides a workaround for the common problem that users with <em>Use the administration pages and help</em> permission see menu links they done not have access permission for.') . '</p>';
- 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];
- }
|