Pathologic was missing because of a .git folder inside.
[yaffs-website] / web / modules / contrib / redirect / redirect.module
1 <?php
2
3 /**
4  * @file
5  * The redirect module.
6  */
7
8 /**
9  * @defgroup redirect_api Redirection API
10  * @{
11  * Functions related to URL redirects.
12  *
13  * @} End of "defgroup redirect_api".
14  */
15 use Drupal\Component\Utility\UrlHelper;
16 use Drupal\Core\Cache\Cache;
17 use Drupal\Core\Entity\EntityInterface;
18 use Drupal\Core\Field\FieldItemList;
19 use Drupal\Core\Form\FormStateInterface;
20 use Drupal\Core\Language\Language;
21 use Drupal\Core\Routing\RouteMatchInterface;
22 use Drupal\Core\Url;
23 use Drupal\Core\Site\Settings;
24 use Drupal\redirect\Entity\Redirect;
25
26 /**
27  * Implements hook_hook_info().
28  */
29 function redirect_hook_info() {
30   $hooks = array(
31     'redirect_load',
32     'redirect_load_by_source_alter',
33     'redirect_access',
34     'redirect_prepare',
35     'redirect_validate',
36     'redirect_presave',
37     'redirect_insert',
38     'redirect_update',
39     'redirect_delete',
40     'redirect_alter',
41   );
42
43   return array_fill_keys($hooks, array('group' => 'redirect'));
44 }
45
46 /**
47  * Implements hook_help().
48  */
49 function redirect_help($route_name, RouteMatchInterface $route_match) {
50   $output = '';
51   switch ($route_name) {
52     case 'help.page.redirect':
53       $output = '<h3>' . t('About') . '</h3>';
54       $output .= '<p>' . t('The Redirect module allows users to redirect from old URLs to new URLs.   For more information, see the <a href=":online">online documentation for Redirect</a>.', [':online' => 'https://www.drupal.org/documentation/modules/path-redirect']) . '</p>';
55       $output .= '<dl>';
56       $output .= '<h3>' . t('Uses') . '</h3>';
57       $output .= '<dd>' . t('Redirect is accessed from three tabs that help you manage <a href=":list">URL Redirects</a>.', [':list' => Url::fromRoute('redirect.list')->toString()]) . '</dd>';
58       $output .= '<dt>' . t('Manage URL Redirects') . '</dt>';
59       $output .= '<dd>' . t('The <a href=":redirect">"URL Redirects"</a> page is used to setup and manage URL Redirects.  New redirects are created here using the <a href=":add_form">Add redirect</a> button which presents a form to simplify the creation of redirects . The URL redirects page provides a list of all redirects on the site and allows you to edit them.', [':redirect' => Url::fromRoute('redirect.list')->toString(), ':add_form' => Url::fromRoute('redirect.add')->toString()]) . '</dd>';
60       if (\Drupal::moduleHandler()->moduleExists('redirect_404')) {
61         $output .= '<dt>' . t('Fix 404 pages') . '</dt>';
62         $output .= '<dd>' . t('<a href=":fix_404">"Fix 404 pages"</a> lists all paths that have resulted in 404 errors and do not yet have any redirects assigned to them. This 404 (or Not Found) error message is an HTTP standard response code indicating that the client was able to communicate with a given server, but the server could not find what was requested.', [':fix_404' => Url::fromRoute('redirect_404.fix_404')->toString()]) . '</dd>';
63       }
64       elseif (!\Drupal::moduleHandler()->moduleExists('redirect_404') && \Drupal::currentUser()->hasPermission('administer modules')) {
65         $output .= '<dt>' . t('Fix 404 pages') . '</dt>';
66         $output .= '<dd>' . t('404 (or Not Found) error message is an HTTP standard response code indicating that the client was able to communicate with a given server, but the server could not find what was requested. Please install the <a href=":extend">Redirect 404</a> submodule to be able to log all paths that have resulted in 404 errors.', [':extend' => Url::fromRoute('system.modules_list')->toString()]) . '</dd>';
67       }
68       $output .= '<dt>' . t('Configure Global Redirects') . '</dt>';
69       $output .= '<dd>' . t('The <a href=":settings">"Settings"</a> page presents you with a number of means to adjust redirect settings.', [':settings' => Url::fromRoute('redirect.settings')->toString()]) . '</dd>';
70       $output .= '</dl>';
71       return $output;
72       break;
73   }
74 }
75
76 /**
77  * Implements hook_entity_delete().
78  *
79  * Will delete redirects based on the entity URL.
80  */
81 function redirect_entity_delete(EntityInterface $entity) {
82   if (redirect_entity_has_path_field($entity)) {
83     redirect_delete_by_path('internal:/' . $entity->toUrl()->getInternalPath());
84     redirect_delete_by_path('entity:' . $entity->getEntityTypeId() . '/' . $entity->id());
85   }
86 }
87
88 /**
89  * Implements hook_path_update().
90  *
91  * Will create redirect from the old path alias to the new one.
92  */
93 function redirect_path_update(array $path) {
94   if (!\Drupal::config('redirect.settings')->get('auto_redirect')) {
95     return;
96   }
97   $original_path = $path['original'];
98
99   // Delete all redirects having the same source as this alias.
100   redirect_delete_by_path($path['alias'], $path['langcode'], FALSE);
101   if ($original_path['alias'] != $path['alias']) {
102     if (!redirect_repository()->findMatchingRedirect($original_path['alias'], array(), $original_path['langcode'])) {
103       $redirect = Redirect::create();
104       $redirect->setSource($original_path['alias']);
105       $redirect->setRedirect($path['source']);
106       $redirect->setLanguage($original_path['langcode']);
107       $redirect->setStatusCode(\Drupal::config('redirect.settings')->get('default_status_code'));
108       $redirect->save();
109     }
110   }
111 }
112
113 /**
114  * Implements hook_path_insert().
115  */
116 function redirect_path_insert(array $path) {
117   // Delete all redirects having the same source as this alias.
118   redirect_delete_by_path($path['alias'], $path['langcode'], FALSE);
119 }
120
121 /**
122  * Implements hook_path_delete().
123  */
124 function redirect_path_delete($path) {
125   if (!\Drupal::config('redirect.settings')->get('auto_redirect')) {
126     return;
127   }
128   elseif (isset($path['redirect']) && !$path['redirect']) {
129     return;
130   }
131   elseif (empty($path)) {
132     // @todo Remove this condition and allow $path to use an array type hint
133     // when http://drupal.org/node/1025904 is fixed.
134     return;
135   }
136
137   // Redirect from a deleted alias to the system path.
138   //if (!redirect_load_by_source($path['alias'], $path['language'])) {
139   //  $redirect = new stdClass();
140   //  redirect_create($redirect);
141   //  $redirect->source = $path['alias'];
142   //  $redirect->redirect = $path['source'];
143   //  $redirect->language = $path['language'];
144   //  redirect_save($redirect);
145   //}
146 }
147
148 /**
149  * Implements hook_page_build().
150  *
151  * Adds an action on 404 pages to create a redirect.
152  *
153  * @todo hook_page_build() can no longer be used for this. Find a different way.
154  */
155 function redirect_page_build(&$page) {
156   if (redirect_is_current_page_404() && \Drupal::currentUser()->hasPermission('administer redirects')) {
157     if (!isset($page['content']['system_main']['actions'])) {
158       $page['content']['system_main']['actions'] = array(
159         '#theme' => 'links',
160         '#links' => array(),
161         '#attributes' => array('class' => array('action-links')),
162         '#weight' => -100,
163       );
164     }
165     // We cannot simply use current_path() because if a 404 path is set, then
166     // that value overrides whatever is in $_GET['q']. The
167     // drupal_deliver_html_page() function thankfully puts the original current
168     // path into $_GET['destination'].
169     $destination = drupal_get_destination();
170     $page['content']['system_main']['actions']['#links']['add_redirect'] = array(
171       'title' => t('Add URL redirect from this page to another location'),
172       'href' => 'admin/config/search/redirect/add',
173       'query' => array('source' => $destination['destination']) + drupal_get_destination(),
174     );
175   }
176 }
177
178 /**
179  * Checks if the entity has path field.
180  *
181  * @param EntityInterface $entity
182  *   The entity to check.
183  *
184  * @return bool
185  *   TRUE if the entity has the path field.
186  */
187 function redirect_entity_has_path_field(EntityInterface $entity) {
188   if (isset($entity->path) && $entity->path instanceof FieldItemList) {
189     return TRUE;
190   }
191
192   return FALSE;
193 }
194
195 /**
196  * Gets the redirect repository service.
197  *
198  * @return \Drupal\redirect\RedirectRepository
199  *   The repository service.
200  */
201 function redirect_repository() {
202   return \Drupal::service('redirect.repository');
203 }
204
205 /**
206  * Delete any redirects associated with a path or any of its sub-paths.
207  *
208  * Given a source like 'node/1' this function will delete any redirects that
209  * have that specific source or any sources that match 'node/1/%'.
210  *
211  * @param string $path
212  *   An string with an internal Drupal path.
213  * @param string $langcode
214  *   (optional) If specified, limits deletion to redirects for the given
215  *   language. Defaults to all languages.
216  * @param bool $match_subpaths_and_redirect
217  *   (optional) Whether redirects with a destination to the given path and
218  *   sub-paths should also be deleted.
219  *
220  * @ingroup redirect_api
221  */
222 function redirect_delete_by_path($path, $langcode = NULL, $match_subpaths_and_redirect = TRUE) {
223   $path = ltrim($path, '/');
224   $query = \Drupal::database()->select('redirect');
225   $query->addField('redirect', 'rid');
226   $query_or = db_or();
227   $query_or->condition('redirect_source__path', db_like($path), 'LIKE');
228   if ($match_subpaths_and_redirect) {
229     $query_or->condition('redirect_source__path', db_like($path . '/') . '%', 'LIKE');
230     $query_or->condition('redirect_redirect__uri', db_like($path), 'LIKE');
231     $query_or->condition('redirect_redirect__uri', db_like($path . '/') . '%', 'LIKE');
232   }
233
234   if ($langcode) {
235     $query->condition('language', $langcode);
236   }
237
238   $query->condition($query_or);
239   $rids = $query->execute()->fetchCol();
240
241   if ($rids) {
242     foreach (redirect_repository()->loadMultiple($rids) as $redirect) {
243       $redirect->delete();
244     }
245   }
246 }
247
248 /**
249  * Sort an array recusively.
250  *
251  * @param $array
252  *   The array to sort, by reference.
253  * @param $callback
254  *   The sorting callback to use (e.g. 'sort', 'ksort', 'asort').
255  *
256  * @return
257  *   TRUE on success or FALSE on failure.
258  */
259 function redirect_sort_recursive(&$array, $callback = 'sort') {
260   $result = $callback($array);
261   foreach ($array as $key => $value) {
262     if (is_array($value)) {
263       $result &= redirect_sort_recursive($array[$key], $callback);
264     }
265   }
266   return $result;
267 }
268
269 /**
270  * Build the URL of a redirect for display purposes only.
271  */
272 function redirect_url($path, array $options = array(), $clean_url = NULL) {
273   // @todo - deal with removal of clean_url config. See
274   //    https://drupal.org/node/1659580
275   if (!isset($clean_url)) {
276     //$clean_url = variable_get('clean_url', 0);
277   }
278
279   if ($path == '') {
280     $path = '<front>';
281   }
282
283   if (!isset($options['alter']) || !empty($options['alter'])) {
284     \Drupal::moduleHandler()->alter('redirect_url', $path, $options);
285   }
286
287   // The base_url might be rewritten from the language rewrite in domain mode.
288   if (!isset($options['base_url'])) {
289     // @todo - is this correct? See https://drupal.org/node/1798832.
290     if (isset($options['https']) && Settings::get('mixed_mode_sessions', FALSE)) {
291       if ($options['https'] === TRUE) {
292         $options['base_url'] = $GLOBALS['base_secure_url'];
293         $options['absolute'] = TRUE;
294       }
295       elseif ($options['https'] === FALSE) {
296         $options['base_url'] = $GLOBALS['base_insecure_url'];
297         $options['absolute'] = TRUE;
298       }
299     }
300     else {
301       $options['base_url'] = $GLOBALS['base_url'];
302     }
303   }
304
305   if (empty($options['absolute']) || url_is_external($path)) {
306     $url = $path;
307   }
308   else {
309     $url = $options['base_url'] . base_path() . $path;
310   }
311
312   if (isset($options['query'])) {
313     $url .= $clean_url ? '?' : '&';
314     $url .= UrlHelper::buildQuery($options['query']);
315   }
316   if (isset($options['fragment'])) {
317     $url .= '#' . $options['fragment'];
318   }
319
320   return $url;
321 }
322
323 function redirect_status_code_options($code = NULL) {
324   $codes = array(
325     300 => t('300 Multiple Choices'),
326     301 => t('301 Moved Permanently'),
327     302 => t('302 Found'),
328     303 => t('303 See Other'),
329     304 => t('304 Not Modified'),
330     305 => t('305 Use Proxy'),
331     307 => t('307 Temporary Redirect'),
332   );
333   return isset($codes[$code]) ? $codes[$code] : $codes;
334 }
335
336 /**
337  * Returns if the current page request is a page not found (404 status error).
338  *
339  * Why the fuck do we have to do this? Why is there not an easier way???
340  *
341  * @return
342  *   TRUE if the current page is a 404, or FALSE otherwise.
343  */
344 function redirect_is_current_page_404() {
345   return drupal_get_http_header('Status') == '404 Not Found';
346 }
347
348 /**
349  * uasort callback; Compare redirects based on language neutrality and rids.
350  */
351 function _redirect_uasort($a, $b) {
352   $a_weight = isset($a->weight) ? $a->weight : 0;
353   $b_weight = isset($b->weight) ? $b->weight : 0;
354   if ($a_weight != $b_weight) {
355     // First sort by weight (case sensitivity).
356     return $a_weight > $b_weight;
357   }
358   elseif ($a->language != $b->language) {
359     // Then sort by language specific over language neutral.
360     return $a->language == Language::LANGCODE_NOT_SPECIFIED;
361   }
362   elseif (!empty($a->source_options['query']) != !empty($b->source_options['query'])) {
363     // Then sort by redirects that do not have query strings over ones that do.
364     return empty($a->source_options['query']);
365   }
366   else {
367     // Lastly sort by the highest redirect ID.
368     return $a->rid < $b->rid;
369   }
370 }
371
372 /**
373  * Implements hook_form_FORM_ID_alter() on behalf of locale.module.
374  */
375 function locale_form_redirect_edit_form_alter(array &$form, FormStateInterface $form_state) {
376   $form['language'] = array(
377     '#type' => 'select',
378     '#title' => t('Language'),
379     '#options' => array(Language::LANGCODE_NOT_SPECIFIED => t('All languages')) + \Drupal::languageManager()->getLanguages(),
380     '#default_value' => $form['language']['#value'],
381     '#description' => t('A redirect set for a specific language will always be used when requesting this page in that language, and takes precedence over redirects set for <em>All languages</em>.'),
382   );
383 }
384
385 /**
386  * Fetch an array of redirect bulk operations.
387  *
388  * @see hook_redirect_operations()
389  * @see hook_redirect_operations_alter()
390  */
391 function redirect_get_redirect_operations() {
392   $operations = &drupal_static(__FUNCTION__);
393
394   if (!isset($operations)) {
395     $operations = \Drupal::moduleHandler()->invokeAll('redirect_operations');
396     \Drupal::moduleHandler()->alter('redirect_operations', $operations);
397   }
398
399   return $operations;
400 }
401
402 /**
403  * Implements hook_redirect_operations().
404  */
405 function redirect_redirect_operations() {
406   $operations['delete'] = array(
407     'action' => t('Delete'),
408     'action_past' => t('Deleted'),
409     'callback' => 'redirect_delete_multiple',
410     'confirm' => TRUE,
411   );
412   return $operations;
413 }
414
415 /**
416  * Ajax callback for the redirect link widget.
417  */
418 function redirect_source_link_get_status_messages(array $form, FormStateInterface $form_state) {
419   return $form['redirect_source']['widget'][0]['status_box'];
420 }