5 * This module don't show menu links that you don't have access permission for.
8 use Drupal\Core\Session\AccountInterface;
10 use Drupal\user\Entity\Role;
11 use Symfony\Component\Routing\Exception\RouteNotFoundException;
12 use Drupal\Core\Routing\RouteMatchInterface;
15 * Implements hook_help().
17 function admin_toolbar_links_access_filter_help($route_name, RouteMatchInterface $route_match) {
18 switch ($route_name) {
20 case 'help.page.admin_toolbar_links_access_filter':
22 $output .= '<h3>' . t('About') . '</h3>';
23 $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>';
30 * Implements hook_preprocess_menu().
32 * Hides links from admin menu, if user doesn't have access rights.
34 function admin_toolbar_links_access_filter_preprocess_menu(&$variables) {
35 if (empty($variables['items'])) {
36 // Additional empty check to prevent exotic situations, where the preprocess
37 // function is entered even without items.
38 // @see https://www.drupal.org/node/2833885
41 // Ensure that menu_name exists.
42 if (!isset($variables['menu_name'])) {
43 // In rare cases (for unknown reasons) menu_name may not be set.
44 // As fallback, we can fetch it from the first menu item.
45 $first_link = reset($variables['items']);
46 /** @var Drupal\Core\Menu\MenuLinkDefault $original_link */
47 // Fetch the menu_name from the original link.
48 $original_link = $first_link['original_link'];
49 $variables['menu_name'] = $original_link->getMenuName();
51 if ($variables['menu_name'] == 'admin') {
52 if (!admin_toolbar_links_access_filter_user_has_admin_role($variables['user'])) {
53 admin_toolbar_links_access_filter_filter_non_accessible_links($variables['items']);
59 * Hides links from admin menu, if user doesn't have access rights.
61 function admin_toolbar_links_access_filter_filter_non_accessible_links(array &$items) {
62 foreach ($items as $route => &$item) {
65 if (!empty($item['original_link'])) {
66 /** @var \Drupal\Core\Menu\MenuLinkBase $original_link */
67 $original_link = $item['original_link'];
68 if ($original_link->getUrlObject()->isExternal()) {
69 // Do not filter external URL at all.
72 $route_name = $original_link->getRouteName();
73 $route_params = $original_link->getRouteParameters();
76 // Check, if user has access rights to the route.
77 if (!\Drupal::accessManager()->checkNamedRoute($route_name, $route_params)) {
78 unset($items[$route]);
81 if (!empty($items[$route]['below'])) {
82 // Recursively call this function for the child items.
83 admin_toolbar_links_access_filter_filter_non_accessible_links($items[$route]['below']);
85 if (empty($items[$route]['below']) && \Drupal::moduleHandler()->moduleExists('admin_toolbar')) {
87 // Every child item has been cleared out.
88 // Now check, if the given route represents an overview page only,
89 // without having functionality on its own. In this case, we can safely
90 // unset this item, as there aren't any children left.
91 // This assumption is only valid, when the admin_toolbar module is
92 // installed because otherwise we won't have child items at all.
93 if (admin_toolbar_links_access_filter_is_overview_page($route)) {
94 unset($items[$route]);
97 // Let's remove the expanded flag.
98 $items[$route]['is_expanded'] = FALSE;
106 * Implements template_preprocess_admin_block_content().
108 function admin_toolbar_links_access_filter_admin_block_content(&$variables) {
109 if (!admin_toolbar_links_access_filter_user_has_admin_role($variables['user'])) {
110 foreach ($variables['content'] as $key => &$item) {
111 if (isset($item['url']) && $item['url'] instanceof Url) {
112 /* @var \Drupal\Core\Url $url */
114 if ($url->access()) {
117 unset($variables['content'][$key]);
120 // The key is structured in the form: "ID title route",
121 // concatenated with spaces.
122 $key_parts = explode(' ', $key);
123 $route = end($key_parts);
125 // Special handling for Views pages, as they are not defined
127 // @TODO check the permission for Views + find a generic way for similar
128 // cases. Best way would be to get the link entity somehow to properly
129 // check permissions.
130 if (strpos($route, 'views_view:') === 0) {
134 // Check, if user has access rights to the route.
135 if (!\Drupal::accessManager()->checkNamedRoute($route)) {
136 unset($variables['content'][$key]);
143 * Checks if the given route name is an overview page.
145 * Checks if the given route name matches a pure (admin) overview page that can
146 * be skipped, if there are no child items set. The typical example are routes
147 * having the SystemController::systemAdminMenuBlockPage() function as their
148 * controller callback set.
150 * @param string $route_name
151 * The route name to check.
154 * TRUE, if the given route name matches a pure admin overview page route,
157 function admin_toolbar_links_access_filter_is_overview_page($route_name) {
158 // @var \Drupal\Core\Routing\RouteProviderInterface $route_provider.
159 $route_provider = \Drupal::service('router.route_provider');
160 $overview_page_controllers = [
161 '\Drupal\system\Controller\AdminController::index',
162 '\Drupal\system\Controller\SystemController::overview',
163 '\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage',
166 $route = $route_provider->getRouteByName($route_name);
167 $controller = $route->getDefault('_controller');
168 return !empty($controller) && in_array($controller, $overview_page_controllers);
170 catch (RouteNotFoundException $ex) {
177 * Checks, if the given user has admin rights.
179 * @param \Drupal\Core\Session\AccountInterface $account
180 * The account to check.
183 * TRUE, if the given user account has at least one role with admin rights
184 * assigned, FALSE otherwise.
186 function admin_toolbar_links_access_filter_user_has_admin_role(AccountInterface $account) {
187 static $user_has_admin_role = [];
188 $uid = $account->id();
189 if (!isset($user_has_admin_role[$uid])) {
190 $roles = Role::loadMultiple($account->getRoles());
191 foreach ($roles as $role) {
192 if ($role->isAdmin()) {
193 $user_has_admin_role[$uid] = TRUE;
196 $user_has_admin_role[$uid] = FALSE;
199 return $user_has_admin_role[$uid];