33namespace Drupal \oit \Hook ;
44
55use Drupal \Core \Hook \Attribute \Hook ;
6+ use Drupal \Core \Routing \RedirectDestinationInterface ;
7+ use Drupal \Core \Url ;
68
79/**
8- * Hook implementations for responsive_menu performance tuning.
10+ * Hook implementations for menu performance tuning and link alterations .
911 *
1012 * The responsive_menu off-canvas and horizontal blocks force the entire menu
1113 * tree to expand and then run the generic "checkAccess" manipulator on every
@@ -30,6 +32,21 @@ class MenuHooks {
3032 'callable ' => 'menu.default_tree_manipulators:checkNodeAccess ' ,
3133 ];
3234
35+ /**
36+ * The SAML login path that carries a destination back to the current page.
37+ */
38+ protected const SAML_LOGIN_PATH = '/saml/login ' ;
39+
40+ /**
41+ * Constructs a new MenuHooks object.
42+ *
43+ * @param \Drupal\Core\Routing\RedirectDestinationInterface $redirectDestination
44+ * The redirect destination helper.
45+ */
46+ public function __construct (
47+ protected RedirectDestinationInterface $ redirectDestination ,
48+ ) {}
49+
3350 /**
3451 * Prepends the node-access manipulator if it is not already present.
3552 *
@@ -61,4 +78,79 @@ public function horizontalManipulatorsAlter(array &$manipulators): void {
6178 $ this ->addNodeAccessManipulator ($ manipulators );
6279 }
6380
81+ /**
82+ * Implements hook_preprocess_menu().
83+ *
84+ * Adds a "destination" query parameter to the SAML login link so that users
85+ * are returned to the page they were on instead of the front page. samlauth
86+ * honours the parameter and rejects external values.
87+ *
88+ * @see \Drupal\samlauth\Controller\SamlController::getDestinationUrl()
89+ */
90+ #[Hook('preprocess_menu ' )]
91+ public function preprocessMenu (array &$ variables ): void {
92+ if (($ variables ['menu_name ' ] ?? NULL ) !== 'account ' ) {
93+ return ;
94+ }
95+ $ destination = $ this ->redirectDestination ->get ();
96+ // Never send the user back to the login flow itself.
97+ if (!$ destination || str_starts_with ($ destination , self ::SAML_LOGIN_PATH )) {
98+ return ;
99+ }
100+ $ this ->addLoginDestination ($ variables ['items ' ], $ destination );
101+ }
102+
103+ /**
104+ * Implements hook_block_build_alter().
105+ *
106+ * The account menu now renders a per-page destination, so its render cache
107+ * must vary by the current URL.
108+ */
109+ #[Hook('block_build_alter ' )]
110+ public function blockBuildAlter (array &$ build , $ block ): void {
111+ if (!str_starts_with ($ block ->getPluginId (), 'system_menu_block:account ' )) {
112+ return ;
113+ }
114+ $ build ['#cache ' ]['contexts ' ][] = 'url.path ' ;
115+ $ build ['#cache ' ]['contexts ' ][] = 'url.query_args ' ;
116+ }
117+
118+ /**
119+ * Recursively adds the destination query to any SAML login link.
120+ *
121+ * @param array $items
122+ * The menu items, passed by reference.
123+ * @param string $destination
124+ * The destination path to return to after login.
125+ */
126+ protected function addLoginDestination (array &$ items , string $ destination ): void {
127+ foreach ($ items as &$ item ) {
128+ $ url = $ item ['url ' ] ?? NULL ;
129+ if ($ url instanceof Url && $ this ->isSamlLoginUrl ($ url )) {
130+ $ query = $ url ->getOption ('query ' ) ?: [];
131+ $ query ['destination ' ] = $ destination ;
132+ $ url ->setOption ('query ' , $ query );
133+ }
134+ if (!empty ($ item ['below ' ])) {
135+ $ this ->addLoginDestination ($ item ['below ' ], $ destination );
136+ }
137+ }
138+ }
139+
140+ /**
141+ * Determines whether a URL points at the SAML login route.
142+ *
143+ * @param \Drupal\Core\Url $url
144+ * The menu link URL.
145+ *
146+ * @return bool
147+ * TRUE if the URL is the SAML login link.
148+ */
149+ protected function isSamlLoginUrl (Url $ url ): bool {
150+ if ($ url ->isRouted ()) {
151+ return $ url ->getRouteName () === 'samlauth.saml_controller_login ' ;
152+ }
153+ return $ url ->isExternal () === FALSE && $ url ->toString () === self ::SAML_LOGIN_PATH ;
154+ }
155+
64156}
0 commit comments