Permalink
Browse files

Merge branch 'develop'

  • Loading branch information...
2 parents 6331abe + 1131d69 commit a34805b0bc8ac947cdfde770aa3d975093aa112c @mackh mackh committed Mar 5, 2014
Showing with 5,671 additions and 4 deletions.
  1. +1 −0 .gitignore
  2. +339 −0 amani/modules/contrib/redirect/LICENSE.txt
  3. +3 −0 amani/modules/contrib/redirect/README.txt
  4. +892 −0 amani/modules/contrib/redirect/redirect.admin.inc
  5. +28 −0 amani/modules/contrib/redirect/redirect.admin.js
  6. +295 −0 amani/modules/contrib/redirect/redirect.api.php
  7. +72 −0 amani/modules/contrib/redirect/redirect.drush.inc
  8. +182 −0 amani/modules/contrib/redirect/redirect.generate.inc
  9. +23 −0 amani/modules/contrib/redirect/redirect.info
  10. +358 −0 amani/modules/contrib/redirect/redirect.install
  11. +18 −0 amani/modules/contrib/redirect/redirect.js
  12. +1,568 −0 amani/modules/contrib/redirect/redirect.module
  13. +244 −0 amani/modules/contrib/redirect/redirect.test
  14. +219 −0 amani/modules/contrib/redirect/views/redirect.views.inc
  15. +16 −0 amani/modules/contrib/redirect/views/redirect.views_default.inc
  16. +41 −0 amani/modules/contrib/redirect/views/redirect_handler_field_redirect_link_delete.inc
  17. +41 −0 amani/modules/contrib/redirect/views/redirect_handler_field_redirect_link_edit.inc
  18. +68 −0 amani/modules/contrib/redirect/views/redirect_handler_field_redirect_operations.inc
  19. +71 −0 amani/modules/contrib/redirect/views/redirect_handler_field_redirect_redirect.inc
  20. +71 −0 amani/modules/contrib/redirect/views/redirect_handler_field_redirect_source.inc
  21. +20 −0 amani/modules/contrib/redirect/views/redirect_handler_filter_redirect_type.inc
  22. +177 −0 amani/modules/contrib/redirect/views/redirects.view
  23. +339 −0 amani/modules/contrib/search404/LICENSE.txt
  24. +27 −0 amani/modules/contrib/search404/README.txt
  25. +7 −0 amani/modules/contrib/search404/TODO
  26. +17 −0 amani/modules/contrib/search404/search404.info
  27. +36 −0 amani/modules/contrib/search404/search404.install
  28. +404 −0 amani/modules/contrib/search404/search404.module
  29. +0 −1 amani/modules/features/amani_features/amani_about/amani_about.views_default.inc
  30. +4 −0 amani/modules/features/amani_features/amani_global/amani_global.features.wysiwyg.inc
  31. +86 −1 amani/modules/features/amani_features/amani_partners/amani_partners.features.field.inc
  32. +2 −0 amani/modules/features/amani_features/amani_partners/amani_partners.info
  33. +2 −2 amani/modules/features/amani_features/amani_partners/amani_partners.strongarm.inc
View
1 .gitignore
@@ -35,3 +35,4 @@ web.config
# /profiles
# /scripts
# /themes
+.idea
View
339 amani/modules/contrib/redirect/LICENSE.txt
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
View
3 amani/modules/contrib/redirect/README.txt
@@ -0,0 +1,3 @@
+
+This is the new module home for a unified redirection API (also replaces
+path_redirect and globalredirect).
View
892 amani/modules/contrib/redirect/redirect.admin.inc
@@ -0,0 +1,892 @@
+<?php
+
+/**
+ * @file
+ * Administrative page callbacks for the redirect module.
+ */
+
+function redirect_list_form($form, &$form_state) {
+ $form['#operations'] = redirect_get_redirect_operations();
+ if (isset($form_state['values']['operation']) && empty($form_state['values']['confirm'])) {
+ return redirect_list_form_operations_confirm_form($form, $form_state, $form_state['values']['operation'], array_filter($form_state['values']['rids']));
+ }
+
+ $destination = drupal_get_destination();
+ $default_status_code = variable_get('redirect_default_status_code', 301);
+
+ // Set up the header.
+ $header = array(
+ 'source' => array('data' => t('From'), 'field' => 'source', 'sort' => 'asc'),
+ 'redirect' => array('data' => t('To'), 'field' => 'redirect'),
+ 'status_code' => array('data' => t('Type'), 'field' => 'status_code'),
+ 'language' => array('data' => t('Language'), 'field' => 'language'),
+ 'count' => array('data' => t('Count'), 'field' => 'count'),
+ 'access' => array('data' => t('Last accessed'), 'field' => 'access'),
+ 'operations' => array('data' => t('Operations')),
+ );
+
+ // Do not include the language column if locale is disabled.
+ if (!module_exists('locale')) {
+ unset($header['language']);
+ }
+
+ // Get filter keys and add the filter form.
+ $keys = func_get_args();
+ $keys = array_splice($keys, 2); // Offset the $form and $form_state parameters.
+ $keys = implode('/', $keys);
+ $form['redirect_list_filter_form'] = redirect_list_filter_form($keys);
+
+ // Build the 'Update options' form.
+ $form['operations'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Update options'),
+ '#prefix' => '<div class="container-inline">',
+ '#suffix' => '</div>',
+ '#attributes' => array(
+ 'class' => array('redirect-list-operations'),
+ ),
+ );
+ $operations = array();
+ foreach ($form['#operations'] as $key => $operation) {
+ $operations[$key] = $operation['action'];
+ }
+ $form['operations']['operation'] = array(
+ '#type' => 'select',
+ '#options' => $operations,
+ '#default_value' => 'delete',
+ );
+ $form['operations']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Update'),
+ '#validate' => array('redirect_list_form_operations_validate'),
+ '#submit' => array('redirect_list_form_operations_submit'),
+ );
+
+ // Building the SQL query and load the redirects.
+ $query = db_select('redirect', 'r')->extend('TableSort')->extend('PagerDefault');
+ $query->addField('r', 'rid');
+ $query->condition('r.type', 'redirect');
+ $query->orderByHeader($header);
+ $query->limit(50);
+ $query->addTag('redirect_list');
+ $query->addTag('redirect_access');
+ redirect_build_filter_query($query, array('source', 'redirect'), $keys);
+ $rids = $query->execute()->fetchCol();
+ $redirects = redirect_load_multiple($rids);
+
+ $rows = array();
+ foreach ($redirects as $rid => $redirect) {
+ $row = array();
+ $redirect->source_options = array_merge($redirect->source_options, array('alias' => TRUE, 'language' => redirect_language_load($redirect->language)));
+ $source_url = redirect_url($redirect->source, $redirect->source_options);
+ $redirect_url = redirect_url($redirect->redirect, array_merge($redirect->redirect_options, array('alias' => TRUE)));
+ drupal_alter('redirect_url', $redirect->source, $redirect->source_options);
+ drupal_alter('redirect_url', $redirect->redirect, $redirect->redirect_options);
+ $row['source'] = l($source_url, $redirect->source, $redirect->source_options);
+ $row['redirect'] = l($redirect_url, $redirect->redirect, $redirect->redirect_options);
+ $row['status_code'] = $redirect->status_code ? $redirect->status_code : t('Default (@default)', array('@default' => $default_status_code));
+ $row['language'] = module_invoke('locale', 'language_name', $redirect->language);
+ $row['count'] = $redirect->count;
+ if ($redirect->access) {
+ $row['access'] = array(
+ 'data' => t('!interval ago', array('!interval' => format_interval(REQUEST_TIME - $redirect->access))),
+ 'title' => t('Last accessed on @date', array('@date' => format_date($redirect->access))),
+ );
+ }
+ else {
+ $row['access'] = t('Never');
+ }
+
+ // Mark redirects that override existing paths with a warning in the table.
+ if (drupal_valid_path($redirect->source)) {
+ $row['#attributes']['class'][] = 'warning';
+ $row['#attributes']['title'] = t('This redirect overrides an existing internal path.');
+ }
+
+ $operations = array();
+ if (redirect_access('update', $redirect)) {
+ $operations['edit'] = array(
+ 'title' => t('Edit'),
+ 'href' => 'admin/config/search/redirect/edit/' . $rid,
+ 'query' => $destination,
+ );
+ }
+ if (redirect_access('delete', $redirect)) {
+ $operations['delete'] = array(
+ 'title' => t('Delete'),
+ 'href' => 'admin/config/search/redirect/delete/' . $rid,
+ 'query' => $destination,
+ );
+ }
+ $row['operations'] = array(
+ 'data' => array(
+ '#theme' => 'links',
+ '#links' => $operations,
+ '#attributes' => array('class' => array('links', 'inline', 'nowrap')),
+ ),
+ );
+
+ $rows[$rid] = $row;
+ }
+
+ $form['rids'] = array(
+ '#type' => 'tableselect',
+ '#header' => $header,
+ '#options' => $rows,
+ '#empty' => t('No URL redirects available.'),
+ '#attributes' => array(
+ 'class' => array('redirect-list-tableselect'),
+ ),
+ '#attached' => array(
+ 'js' => array(
+ drupal_get_path('module', 'redirect') . '/redirect.admin.js',
+ ),
+ ),
+ );
+ if (redirect_access('create', 'redirect')) {
+ $form['rids']['#empty'] .= ' ' . l(t('Add URL redirect.'), 'admin/config/search/redirect/add', array('query' => $destination));
+ }
+ $form['pager'] = array('#theme' => 'pager');
+ return $form;
+}
+
+/**
+ * Return a form to filter URL redirects.
+ *
+ * @see redirect_list_filter_form_submit()
+ *
+ * @ingroup forms
+ */
+function redirect_list_filter_form($keys = '') {
+ $form['#attributes'] = array('class' => array('search-form'));
+ $form['basic'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Filter redirects'),
+ '#attributes' => array('class' => array('container-inline')),
+ );
+ $form['basic']['filter'] = array(
+ '#type' => 'textfield',
+ '#title' => '',
+ '#default_value' => $keys,
+ '#maxlength' => 128,
+ '#size' => 25,
+ );
+ $form['basic']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Filter'),
+ '#submit' => array('redirect_list_filter_form_submit'),
+ );
+ if ($keys) {
+ $form['basic']['reset'] = array(
+ '#type' => 'submit',
+ '#value' => t('Reset'),
+ '#submit' => array('redirect_list_filter_form_reset'),
+ );
+ }
+ return $form;
+}
+
+/**
+ * Process filter form submission when the Filter button is pressed.
+ */
+function redirect_list_filter_form_submit($form, &$form_state) {
+ $form_state['redirect'] = 'admin/config/search/redirect/list/' . trim($form_state['values']['filter']);
+}
+
+/**
+ * Process filter form submission when the Reset button is pressed.
+ */
+function redirect_list_filter_form_reset($form, &$form_state) {
+ $form_state['redirect'] = 'admin/config/search/redirect';
+}
+
+/**
+ * Extends a query object for URL redirect filters.
+ *
+ * @param $query
+ * Query object that should be filtered.
+ * @param $keys
+ * The filter string to use.
+ */
+function redirect_build_filter_query(QueryAlterableInterface $query, array $fields, $keys = '') {
+ if ($keys && $fields) {
+ // Replace wildcards with PDO wildcards.
+ $conditions = db_or();
+ $wildcard = '%' . trim(preg_replace('!\*+!', '%', db_like($keys)), '%') . '%';
+ foreach ($fields as $field) {
+ $conditions->condition($field, $wildcard, 'LIKE');
+ }
+ $query->condition($conditions);
+ }
+}
+
+/**
+ * Validate redirect_list_form form submissions.
+ *
+ * Check if any redirects have been selected to perform the chosen
+ * 'Update option' on.
+ */
+function redirect_list_form_operations_validate($form, &$form_state) {
+ // Error if there are no redirects selected.
+ if (!is_array($form_state['values']['rids']) || !count(array_filter($form_state['values']['rids']))) {
+ form_set_error('', t('No redirects selected.'));
+ }
+}
+
+/**
+ * Process redirect_list_form form submissions.
+ *
+ * Execute the chosen 'Update option' on the selected redirects.
+ */
+function redirect_list_form_operations_submit($form, &$form_state) {
+ $operations = $form['#operations'];
+ $operation = $operations[$form_state['values']['operation']];
+
+ // Filter out unchecked redirects
+ $rids = array_filter($form_state['values']['rids']);
+
+ if (!empty($operation['confirm']) && empty($form_state['values']['confirm'])) {
+ // We need to rebuild the form to go to a second step. For example, to
+ // show the confirmation form for the deletion of redirects.
+ $form_state['rebuild'] = TRUE;
+ }
+ else {
+ $function = $operation['callback'];
+
+ // Add in callback arguments if present.
+ if (isset($operation['callback arguments'])) {
+ $args = array_merge(array($rids), $operation['callback arguments']);
+ }
+ else {
+ $args = array($rids);
+ }
+ call_user_func_array($function, $args);
+
+ $count = count($form_state['values']['rids']);
+ watchdog('redirect', '@action @count redirects.', array('@action' => $operation['action_past'], '@count' => $count));
+ drupal_set_message(format_plural(count($rids), '@action @count redirect.', '@action @count redirects.', array('@action' => $operation['action_past'], '@count' => $count)));
+ }
+}
+
+function redirect_list_form_operations_confirm_form($form, &$form_state, $operation, $rids) {
+ $operations = $form['#operations'];
+ $operation = $operations[$form_state['values']['operation']];
+
+ $form['rids_list'] = array(
+ '#theme' => 'item_list',
+ '#items' => array(),
+ );
+ $form['rids'] = array(
+ '#type' => 'value',
+ '#value' => $rids,
+ );
+
+ $redirects = redirect_load_multiple($rids);
+ foreach ($redirects as $rid => $redirect) {
+ $form['rids_list']['#items'][$rid] = check_plain(redirect_url($redirect->source, $redirect->source_options));
+ }
+
+ $form['operation'] = array('#type' => 'hidden', '#value' => $form_state['values']['operation']);
+ $form['#submit'][] = 'redirect_list_form_operations_submit';
+ $confirm_question = format_plural(count($rids), 'Are you sure you want to @action this redirect?', 'Are you sure you want to @action these redirects?', array('@action' => drupal_strtolower($operation['action'])));
+
+ return confirm_form(
+ $form,
+ $confirm_question,
+ 'admin/config/search/redirect', // @todo This does not redirect back to filtered page.
+ t('This action cannot be undone.'),
+ $operation['action'],
+ t('Cancel')
+ );
+}
+
+/**
+ * Form builder to add or edit an URL redirect.
+ *
+ * @see redirect_element_validate_source()
+ * @see redirect_element_validate_redirect()
+ * @see redirect_edit_form_validate()
+ * @see redirect_edit_form_submit()
+ *
+ * @ingroup forms
+ */
+function redirect_edit_form($form, &$form_state, $redirect = NULL) {
+ if (!isset($redirect)) {
+ $redirect = new stdClass();
+ }
+
+ // Merge default values.
+ redirect_object_prepare($redirect, array(
+ 'source' => isset($_GET['source']) ? urldecode($_GET['source']) : '',
+ 'source_options' => isset($_GET['source_options']) ? drupal_get_query_array($_GET['source_options']) : array(),
+ 'redirect' => isset($_GET['redirect']) ? urldecode($_GET['redirect']) : '',
+ 'redirect_options' => isset($_GET['redirect_options']) ? drupal_get_query_array($_GET['redirect_options']) : array(),
+ 'language' => isset($_GET['language']) ? urldecode($_GET['language']) : LANGUAGE_NONE,
+ ));
+
+ $form['rid'] = array(
+ '#type' => 'value',
+ '#value' => $redirect->rid,
+ );
+ $form['type'] = array(
+ '#type' => 'value',
+ '#value' => $redirect->type,
+ );
+ $form['hash'] = array(
+ '#type' => 'value',
+ '#value' => $redirect->hash,
+ );
+
+ $form['source'] = array(
+ '#type' => 'textfield',
+ '#title' => t('From'),
+ '#description' => t("Enter an internal Drupal path or path alias to redirect (e.g. %example1 or %example2). Fragment anchors (e.g. %anchor) are <strong>not</strong> allowed.", array('%example1' => 'node/123', '%example2' => 'taxonomy/term/123', '%anchor' => '#anchor')),
+ '#maxlength' => 560,
+ '#default_value' => $redirect->rid || $redirect->source ? redirect_url($redirect->source, $redirect->source_options + array('alter' => FALSE)) : '',
+ '#required' => TRUE,
+ '#field_prefix' => $GLOBALS['base_url'] . '/' . (variable_get('clean_url', 0) ? '' : '?q='),
+ '#element_validate' => array('redirect_element_validate_source'),
+ );
+ $form['source_options'] = array(
+ '#type' => 'value',
+ '#value' => $redirect->source_options,
+ '#tree' => TRUE,
+ );
+ $form['redirect'] = array(
+ '#type' => 'textfield',
+ '#title' => t('To'),
+ '#maxlength' => 560,
+ '#default_value' => $redirect->rid || $redirect->redirect ? redirect_url($redirect->redirect, $redirect->redirect_options, TRUE) : '',
+ '#required' => TRUE,
+ '#description' => t('Enter an internal Drupal path, path alias, or complete external URL (like http://example.com/) to redirect to. Use %front to redirect to the front page.', array('%front' => '<front>')),
+ '#element_validate' => array('redirect_element_validate_redirect'),
+ );
+ $form['redirect_options'] = array(
+ '#type' => 'value',
+ '#value' => $redirect->redirect_options,
+ '#tree' => TRUE,
+ );
+
+ // This will be a hidden value unless locale module is enabled.
+ $form['language'] = array(
+ '#type' => 'value',
+ '#value' => $redirect->language,
+ );
+
+ $form['advanced'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Advanced options'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ );
+ $form['advanced']['status_code'] = array(
+ '#type' => 'select',
+ '#title' => t('Redirect status'),
+ '#description' => t('You can find more information about HTTP redirect status codes at <a href="@status-codes">@status-codes</a>.', array('@status-codes' => 'http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection')),
+ '#default_value' => $redirect->status_code,
+ '#options' => array(0 => t('Default (@default)', array('@default' => variable_get('redirect_default_status_code', 301)))) + redirect_status_code_options(),
+ );
+
+ $form['override'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('I understand the following warnings and would like to proceed with saving this URL redirect'),
+ '#default_value' => FALSE,
+ '#access' => FALSE,
+ '#required' => FALSE,
+ '#weight' => -100,
+ '#prefix' => '<div class="messages warning">',
+ '#suffix' => '</div>',
+ );
+ if (!empty($form_state['storage']['override_messages'])) {
+ $form['override']['#access'] = TRUE;
+ //$form['override']['#required'] = TRUE;
+ $form['override']['#description'] = theme('item_list', array('items' => $form_state['storage']['override_messages']));
+ // Reset the messages.
+ $form_state['storage']['override_messages'] = array();
+ }
+
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ );
+ $form['actions']['cancel'] = array(
+ '#type' => 'link',
+ '#title' => t('Cancel'),
+ '#href' => isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/search/redirect',
+ );
+
+ return $form;
+}
+
+/**
+ * Element validate handler; validate the source of an URL redirect.
+ *
+ * @see redirect_edit_form()
+ */
+function redirect_element_validate_source($element, &$form_state) {
+ $value = &$element['#value'];
+
+ // Check that the source contains no URL fragment.
+ if (strpos($value, '#') !== FALSE) {
+ form_error($element, t('The source path cannot contain an URL fragment anchor.'));
+ }
+
+ _redirect_extract_url_options($element, $form_state);
+
+ // Disallow redirections from the frontpage.
+ if ($value === '<front>') {
+ form_error($element, t('The source path cannot be the front page.'));
+ }
+
+ // Cannot create redirects for valid paths.
+ if (empty($form_state['values']['override'])) {
+ $menu_item = menu_get_item($value);
+ if ($menu_item && $menu_item['page_callback'] != 'redirect_redirect' && $value == $menu_item['path']) {
+ $form_state['storage']['override_messages']['valid-path'] = t('The source path %path is likely a valid path. It is preferred to <a href="@url-alias">create URL aliases</a> for existing paths rather than redirects.', array('%path' => $value, '@url-alias' => url('admin/config/search/path/add')));
+ $form_state['rebuild'] = TRUE;
+ }
+ }
+
+ return $element;
+}
+
+/**
+ * Element validate handler; validate the redirect of an URL redirect.
+ *
+ * @see redirect_edit_form()
+ */
+function redirect_element_validate_redirect($element, &$form_state) {
+ $value = &$element['#value'];
+ _redirect_extract_url_options($element, $form_state);
+ $value = &$form_state['values']['redirect'];
+
+
+ // Normalize the path.
+ $value = drupal_get_normal_path($value, $form_state['values']['language']);
+
+ if (!valid_url($value) && !valid_url($value, TRUE) && $value != '<front>' && $value != '') {
+ form_error($element, t('The redirect path %value is not valid.', array('%value' => $value)));
+ }
+
+ return $element;
+}
+
+/**
+ * Extract the query and fragment parts out of an URL field.
+ */
+function _redirect_extract_url_options(&$element, &$form_state) {
+ $value = &$element['#value'];
+ $type = $element['#name'];
+ $options = &$form_state['values']["{$type}_options"];
+
+ $parsed = redirect_parse_url($value);
+
+ if (isset($parsed['fragment'])) {
+ $options['fragment'] = $parsed['fragment'];
+ }
+ else {
+ unset($options['fragment']);
+ }
+
+ if (isset($parsed['query'])) {
+ $options['query'] = $parsed['query'];
+ }
+ else {
+ unset($options['query']);
+ }
+
+ if (isset($parsed['scheme']) && $parsed['scheme'] == 'https') {
+ $options['https'] = TRUE;
+ }
+ else {
+ unset($options['https']);
+ }
+
+ if (!url_is_external($parsed['url'])) {
+ $parsed['url'] = drupal_get_normal_path($parsed['url'], $form_state['values']['language']);
+ }
+
+ form_set_value($element, $parsed['url'], $form_state);
+ return $parsed;
+}
+
+/**
+ * Form validate handler; validate an URL redirect
+ *
+ * @see redirect_edit_form()
+ */
+function redirect_edit_form_validate($form, &$form_state) {
+ $redirect = (object) $form_state['values'];
+
+ if (empty($form_state['values']['override'])) {
+ if ($existing = redirect_load_by_source($redirect->source, $redirect->language)) {
+ if ($redirect->rid != $existing->rid && $redirect->language == $existing->language) {
+ // The "from" path should not conflict with another redirect
+ $form_state['storage']['override_messages']['redirect-conflict'] = t('The base source path %source is already being redirected. Do you want to <a href="@edit-page">edit the existing redirect</a>?', array('%source' => $redirect->source, '@edit-page' => url('admin/config/search/redirect/edit/'. $existing->rid)));
+ $form_state['rebuild'] = TRUE;
+ }
+ }
+
+ if ($form['override']['#access']) {
+ drupal_set_message('Did you read the warnings and click the checkbox?', 'error');
+ $form_state['rebuild'] = TRUE;
+ //form_set_error('override', 'CLICK DA BUTTON!');
+ }
+ }
+
+ redirect_validate($redirect, $form, $form_state);
+}
+
+/**
+ * Form submit handler; insert or update an URL redirect.
+ *
+ * @see redirect_edit_form()
+ */
+function redirect_edit_form_submit($form, &$form_state) {
+ form_state_values_clean($form_state);
+ $redirect = (object) $form_state['values'];
+ redirect_save($redirect);
+ drupal_set_message(t('The redirect has been saved.'));
+ $form_state['redirect'] = 'admin/config/search/redirect';
+}
+
+/**
+ * Form builder to delete an URL redirect.
+ *
+ * @see redirect_delete_form()
+ * @see confirm_form()
+ *
+ * @ingroup forms
+ */
+function redirect_delete_form($form, &$form_state, $redirect) {
+ $form['rid'] = array(
+ '#type' => 'value',
+ '#value' => $redirect->rid,
+ );
+
+ return confirm_form(
+ $form,
+ t('Are you sure you want to delete the URL redirect from %source to %redirect?', array('%source' => $redirect->source, '%redirect' => $redirect->redirect)),
+ 'admin/config/search/redirect'
+ );
+}
+
+/**
+ * Form submit handler; delete an URL redirect after confirmation.
+ *
+ * @see redirect_delete_form()
+ */
+function redirect_delete_form_submit($form, &$form_state) {
+ redirect_delete($form_state['values']['rid']);
+ drupal_set_message(t('The redirect has been deleted.'));
+ $form_state['redirect'] = 'admin/config/search/redirect';
+}
+
+/**
+ * Form builder for redirection settings.
+ *
+ * @see system_settings_form()
+ * @see redirect_settings_form_submit()
+ *
+ * @ingroup forms
+ */
+function redirect_settings_form($form, &$form_state) {
+ $form['redirect_auto_redirect'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Automatically create redirects when URL aliases are changed.'),
+ '#default_value' => variable_get('redirect_auto_redirect', TRUE),
+ '#disabled' => !module_exists('path'),
+ );
+ $form['redirect_passthrough_querystring'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Retain query string through redirect.'),
+ '#default_value' => variable_get('redirect_passthrough_querystring', 1),
+ '#description' => t('For example, given a redirect from %source to %redirect, if a user visits %sourcequery they would be redirected to %redirectquery. The query strings in the redirection will always take precedence over the current query string.', array('%source' => 'source-path', '%redirect' => 'node?a=apples', '%sourcequery' => 'source-path?a=alligators&b=bananas', '%redirectquery' => 'node?a=apples&b=bananas')),
+ );
+ $form['redirect_warning'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Display a warning message to users when they are redirected.'),
+ '#default_value' => variable_get('redirect_warning', FALSE),
+ '#access' => FALSE,
+ );
+ $form['redirect_default_status_code'] = array(
+ '#type' => 'select',
+ '#title' => t('Default redirect status'),
+ '#description' => t('You can find more information about HTTP redirect status codes at <a href="@status-codes">@status-codes</a>.', array('@status-codes' => 'http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection')),
+ '#options' => redirect_status_code_options(),
+ '#default_value' => variable_get('redirect_default_status_code', 301),
+ );
+ $form['redirect_page_cache'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Allow redirects to be saved into the page cache.'),
+ '#default_value' => variable_get('redirect_page_cache', 0),
+ '#description' => t('This feature requires <a href="@performance">Cache pages for anonymous users</a> to be enabled and the %variable variable to be TRUE.', array('@performance' => url('admin/config/development/performance'), '%variable' => "\$conf['page_cache_invoke_hooks']")),
+ '#disabled' => !variable_get('cache', 0) || !variable_get('page_cache_invoke_hooks', TRUE),
+ );
+ $form['redirect_purge_inactive'] = array(
+ '#type' => 'select',
+ '#title' => t('Delete redirects that have not been accessed for'),
+ '#default_value' => variable_get('redirect_purge_inactive', 0),
+ '#options' => array(0 => t('Never (do not discard)')) + drupal_map_assoc(array(604800, 1209600, 1814400, 2592000, 5184000, 7776000, 10368000, 15552000, 31536000), 'format_interval'),
+ '#description' => t('Only redirects managaged by the redirect module itself will be deleted. Redirects managed by other modules will be left alone.'),
+ '#disabled' => variable_get('redirect_page_cache', 0) && !variable_get('page_cache_invoke_hooks', TRUE),
+ );
+
+ $form['globals'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Always enabled redirections'),
+ '#description' => t('(formerly Global Redirect features)'),
+ '#access' => FALSE,
+ );
+ $form['globals']['redirect_global_home'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Redirect from paths like index.php and /node to the root directory.'),
+ '#default_value' => variable_get('redirect_global_home', 1),
+ '#access' => FALSE,
+ );
+ $form['globals']['redirect_global_clean'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Redirect from non-clean URLs to clean URLs.'),
+ '#default_value' => variable_get('redirect_global_clean', 1),
+ '#disabled' => !variable_get('clean_url', 0),
+ '#access' => FALSE,
+ );
+ $form['globals']['redirect_global_canonical'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Redirect from non-canonical URLs to the canonical URLs.'),
+ '#default_value' => variable_get('redirect_global_canonical', 1),
+ );
+ $form['globals']['redirect_global_deslash'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Remove trailing slashes from paths.'),
+ '#default_value' => variable_get('redirect_global_deslash', 0),
+ '#access' => FALSE,
+ );
+ $form['globals']['redirect_global_admin_paths'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Allow redirections on admin paths.'),
+ '#default_value' => variable_get('redirect_global_admin_paths', 0),
+ );
+
+ $form['#submit'][] = 'redirect_settings_form_submit';
+ return system_settings_form($form);
+}
+
+/**
+ * Form submit handler; clears the page cache.
+ *
+ * @see redirect_settings_form()
+ */
+function redirect_settings_form_submit($form, &$form_state) {
+ redirect_page_cache_clear();
+}
+
+function redirect_404_list($form = NULL) {
+ $destination = drupal_get_destination();
+
+ // Get filter keys and add the filter form.
+ $keys = func_get_args();
+ //$keys = array_splice($keys, 2); // Offset the $form and $form_state parameters.
+ $keys = implode('/', $keys);
+ $build['redirect_list_404_filter_form'] = drupal_get_form('redirect_list_404_filter_form', $keys);
+
+ $header = array(
+ array('data' => t('Page'), 'field' => 'message'),
+ array('data' => t('Count'), 'field' => 'count', 'sort' => 'desc'),
+ array('data' => t('Last accessed'), 'field' => 'timestamp'),
+ array('data' => t('Operations')),
+ );
+
+ $count_query = db_select('watchdog', 'w');
+ $count_query->addExpression('COUNT(DISTINCT(w.message))');
+ $count_query->leftJoin('redirect', 'r', 'w.message = r.source');
+ $count_query->condition('w.type', 'page not found');
+ $count_query->isNull('r.rid');
+ redirect_build_filter_query($count_query, array('w.message'), $keys);
+
+ $query = db_select('watchdog', 'w')->extend('PagerDefault')->extend('TableSort');
+ $query->fields('w', array('message'));
+ $query->addExpression('COUNT(wid)', 'count');
+ $query->addExpression('MAX(timestamp)', 'timestamp');
+ $query->leftJoin('redirect', 'r', 'w.message = r.source');
+ $query->isNull('r.rid');
+ $query->condition('w.type', 'page not found');
+ $query->groupBy('w.message');
+ $query->orderByHeader($header);
+ $query->limit(25);
+ redirect_build_filter_query($query, array('w.message'), $keys);
+ $query->setCountQuery($count_query);
+ $results = $query->execute();
+
+ $rows = array();
+ foreach ($results as $result) {
+ $row = array();
+ $row['source'] = l($result->message, $result->message, array('query' => $destination));
+ $row['count'] = $result->count;
+ $row['timestamp'] = format_date($result->timestamp, 'short');
+
+ $operations = array();
+ if (redirect_access('create', 'redirect')) {
+ $operations['add'] = array(
+ 'title' => t('Add redirect'),
+ 'href' => 'admin/config/search/redirect/add/',
+ 'query' => array('source' => $result->message) + $destination,
+ );
+ }
+ $row['operations'] = array(
+ 'data' => array(
+ '#theme' => 'links',
+ '#links' => $operations,
+ '#attributes' => array('class' => array('links', 'inline', 'nowrap')),
+ ),
+ );
+
+ $rows[] = $row;
+ }
+
+ $build['redirect_404_table'] = array(
+ '#theme' => 'table',
+ '#header' => $header,
+ '#rows' => $rows,
+ '#empty' => t('No 404 pages without redirects found.'),
+ );
+ $build['redirect_404_pager'] = array('#theme' => 'pager');
+ return $build;
+}
+
+/**
+ * Return a form to filter URL redirects.
+ *
+ * @see redirect_list_filter_form_submit()
+ *
+ * @ingroup forms
+ */
+function redirect_list_404_filter_form($form, &$form_state, $keys = '') {
+ $form['#attributes'] = array('class' => array('search-form'));
+ $form['basic'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Filter 404s'),
+ '#attributes' => array('class' => array('container-inline')),
+ );
+ $form['basic']['filter'] = array(
+ '#type' => 'textfield',
+ '#title' => '',
+ '#default_value' => $keys,
+ '#maxlength' => 128,
+ '#size' => 25,
+ );
+ $form['basic']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Filter'),
+ '#submit' => array('redirect_list_404_filter_form_submit'),
+ );
+ if ($keys) {
+ $form['basic']['reset'] = array(
+ '#type' => 'submit',
+ '#value' => t('Reset'),
+ '#submit' => array('redirect_list_404_filter_form_reset'),
+ );
+ }
+ return $form;
+}
+
+/**
+ * Process filter form submission when the Filter button is pressed.
+ */
+function redirect_list_404_filter_form_submit($form, &$form_state) {
+ $form_state['redirect'] = 'admin/config/search/redirect/404/' . trim($form_state['values']['filter']);
+}
+
+/**
+ * Process filter form submission when the Reset button is pressed.
+ */
+function redirect_list_404_filter_form_reset($form, &$form_state) {
+ $form_state['redirect'] = 'admin/config/search/redirect/404';
+}
+
+function redirect_list_table($redirects, $header) {
+ $destination = drupal_get_destination();
+ $default_status_code = variable_get('redirect_default_status_code', 301);
+
+ // Set up the header.
+ $header = array_combine($header, $header);
+ $header = array_intersect_key(array(
+ 'source' => array('data' => t('From'), 'field' => 'source', 'sort' => 'asc'),
+ 'redirect' => array('data' => t('To'), 'field' => 'redirect'),
+ 'status_code' => array('data' => t('Type'), 'field' => 'status_code'),
+ 'language' => array('data' => t('Language'), 'field' => 'language'),
+ 'count' => array('data' => t('Count'), 'field' => 'count'),
+ 'access' => array('data' => t('Last accessed'), 'field' => 'access'),
+ 'operations' => array('data' => t('Operations')),
+ ), $header);
+
+ // Do not include the language column if locale is disabled.
+ if (!module_exists('locale')) {
+ unset($header['language']);
+ }
+
+ $rows = array();
+ foreach ($redirects as $rid => $redirect) {
+ $row = array();
+ $redirect->source_options = array_merge($redirect->source_options, array('alias' => TRUE, 'language' => redirect_language_load($redirect->language)));
+ $source_url = redirect_url($redirect->source, $redirect->source_options);
+ $redirect_url = redirect_url($redirect->redirect, array_merge($redirect->redirect_options, array('alias' => TRUE)));
+ $row['data']['source'] = l($source_url, $redirect->source, $redirect->source_options);
+ $row['data']['redirect'] = l($redirect_url, $redirect->redirect, $redirect->redirect_options);
+ $row['data']['status_code'] = $redirect->status_code ? $redirect->status_code : t('Default (@default)', array('@default' => $default_status_code));
+ $row['data']['language'] = module_invoke('locale', 'language_name', $redirect->language);
+ $row['data']['count'] = $redirect->count;
+ if ($redirect->access) {
+ $row['data']['access'] = array(
+ 'data' => t('!interval ago', array('!interval' => format_interval(REQUEST_TIME - $redirect->access))),
+ 'title' => t('Last accessed on @date', array('@date' => format_date($redirect->access))),
+ );
+ }
+ else {
+ $row['data']['access'] = t('Never');
+ }
+
+ // Mark redirects that override existing paths with a warning in the table.
+ if (drupal_valid_path($redirect->source)) {
+ $row['class'][] = 'warning';
+ $row['title'] = t('This redirect overrides an existing internal path.');
+ }
+
+ $operations = array();
+ if (redirect_access('update', $redirect)) {
+ $operations['edit'] = array(
+ 'title' => t('Edit'),
+ 'href' => 'admin/config/search/redirect/edit/' . $rid,
+ 'query' => $destination,
+ );
+ }
+ if (redirect_access('delete', $redirect)) {
+ $operations['delete'] = array(
+ 'title' => t('Delete'),
+ 'href' => 'admin/config/search/redirect/delete/' . $rid,
+ 'query' => $destination,
+ );
+ }
+ $row['data']['operations'] = array(
+ 'data' => array(
+ '#theme' => 'links',
+ '#links' => $operations,
+ '#attributes' => array('class' => array('links', 'inline', 'nowrap')),
+ ),
+ );
+
+ $row['data'] = array_intersect_key($row['data'], $header);
+ $rows[$rid] = $row;
+ }
+
+ $build['list'] = array(
+ '#theme' => 'table',
+ '#header' => $header,
+ '#rows' => $rows,
+ '#empty' => t('No URL redirects available.'),
+ '#attributes' => array('class' => array('redirect-list')),
+ );
+
+ return $build;
+}
View
28 amani/modules/contrib/redirect/redirect.admin.js
@@ -0,0 +1,28 @@
+
+(function ($) {
+
+Drupal.behaviors.redirectAdmin = {
+ attach: function (context) {
+ $('table.redirect-list-tableselect tbody input:checkbox').bind('change', function(context) {
+ var checked = $('table.redirect-list-tableselect input:checkbox:checked').length;
+ if (checked) {
+ $('fieldset.redirect-list-operations').slideDown();
+ }
+ else {
+ $('fieldset.redirect-list-operations').slideUp();
+ }
+ });
+ $('table.redirect-list-tableselect th.select-all input:checkbox').bind('change', function(context) {
+ var checked = $(this, context).attr('checked');
+ if (checked) {
+ $('fieldset.redirect-list-operations').slideDown();
+ }
+ else {
+ $('fieldset.redirect-list-operations').slideUp();
+ }
+ });
+ $('fieldset.redirect-list-operations').hide();
+ }
+};
+
+})(jQuery);
View
295 amani/modules/contrib/redirect/redirect.api.php
@@ -0,0 +1,295 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the Redirect module.
+ */
+
+/**
+ * @defgroup redirect_api_hooks Redirect API Hooks
+ * @{
+ * During redirect operations (create, update, view, delete, etc.), there are
+ * several sets of hooks that get invoked to allow modules to modify the
+ * redirect operation:
+ * - All-module hooks: Generic hooks for "redirect" operations. These are
+ * always invoked on all modules.
+ * - Entity hooks: Generic hooks for "entity" operations. These are always
+ * invoked on all modules.
+ *
+ * Here is a list of the redirect and entity hooks that are invoked, and other
+ * steps that take place during redirect operations:
+ * - Creating a new redirect (calling redirect_save() on a new redirect):
+ * - hook_redirect_presave() (all)
+ * - Redirect written to the database
+ * - hook_redirect_insert() (all)
+ * - hook_entity_insert() (all)
+ * - Updating an existing redirect (calling redirect_save() on an existing redirect):
+ * - hook_redirect_presave() (all)
+ * - Redirect written to the database
+ * - hook_redirect_update() (all)
+ * - hook_entity_update() (all)
+ * - Loading a redirect (calling redirect_load(), redirect_load_multiple(), or
+ * entity_load() with $entity_type of 'redirect'):
+ * - Redirect information is read from database.
+ * - hook_entity_load() (all)
+ * - hook_redirect_load() (all)
+ * - Deleting a redirect (calling redirect_delete() or redirect_delete_multiple()):
+ * - Redirect is loaded (see Loading section above)
+ * - Redirect information is deleted from database
+ * - hook_redirect_delete() (all)
+ * - hook_entity_delete() (all)
+ * - Preparing a redirect for editing (note that if it's
+ * an existing redirect, it will already be loaded; see the Loading section
+ * above):
+ * - hook_redirect_prepare() (all)
+ * - Validating a redirect during editing form submit (calling
+ * redirect_form_validate()):
+ * - hook_redirect_validate() (all)
+ * @}
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Act on redirects being loaded from the database.
+ *
+ * This hook is invoked during redirect loading, which is handled by
+ * entity_load(), via classes RedirectController and
+ * DrupalDefaultEntityController. After the redirect information is read from
+ * the database or the entity cache, hook_entity_load() is invoked on all
+ * implementing modules, and then hook_redirect_load() is invoked on all
+ * implementing modules.
+ *
+ * This hook should only be used to add information that is not in the redirect
+ * table, not to replace information that is in that table (which could
+ * interfere with the entity cache). For performance reasons, information for
+ * all available redirects should be loaded in a single query where possible.
+ *
+ * The $types parameter allows for your module to have an early return (for
+ * efficiency) if your module only supports certain redirect types.
+ *
+ * @param $redirects
+ * An array of the redirects being loaded, keyed by rid.
+ * @param $types
+ * An array containing the types of the redirects.
+ *
+ * @ingroup redirect_api_hooks
+ */
+function hook_redirect_load(array &$redirects, $types) {
+
+}
+
+/**
+ * Alter the list of redirects matching a certain source.
+ *
+ * @param $redirects
+ * An array of redirect objects.
+ * @param $source
+ * The source request path.
+ * @param $context
+ * An array with the following key/value pairs:
+ * - language: The language code of the source request.
+ * - query: An array of the source request query string.
+ *
+ * @see redirect_load_by_source()
+ * @ingroup redirect_api_hooks
+ */
+function hook_redirect_load_by_source_alter(array &$redirects, $source, array $context) {
+ foreach ($redirects as $rid => $redirect) {
+ if ($redirect->source !== $source) {
+ // If the redirects to do not exactly match $source (e.g. case
+ // insensitive matches), then remove them from the results.
+ unset($redirects[$rid]);
+ }
+ }
+}
+
+/**
+ * Control access to a redirect.
+ *
+ * Modules may implement this hook if they want to have a say in whether or not
+ * a given user has access to perform a given operation on a redirect.
+ *
+ * The administrative account (user ID #1) always passes any access check,
+ * so this hook is not called in that case. Users with the "administer redirects"
+ * permission may always update and delete redirects through the administrative
+ * interface.
+ *
+ * Note that not all modules will want to influence access on all
+ * redirect types. If your module does not want to actively grant or
+ * block access, return REDIRECT_ACCESS_IGNORE or simply return nothing.
+ * Blindly returning FALSE will break other redirect access modules.
+ *
+ * @param $redirect
+ * The redirect object on which the operation is to be performed, or, if it
+ * does not yet exist, the type of redirect to be created.
+ * @param $op
+ * The operation to be performed. Possible values:
+ * - "create"
+ * - "delete"
+ * - "update"
+ * @param $account
+ * A user object representing the user for whom the operation is to be
+ * performed.
+ *
+ * @return
+ * REDIRECT_ACCESS_ALLOW if the operation is to be allowed;
+ * REDIRECT_ACCESS_DENY if the operation is to be denied;
+ * REDIRECT_ACCESSS_IGNORE to not affect this operation at all.
+ *
+ * @see redirect_access()
+ * @ingroup redirect_api_hooks
+ */
+function hook_redirect_access($op, $redirect, $account) {
+ $type = is_string($redirect) ? $redirect : $redirect->type;
+
+ if (in_array($type, array('normal', 'special'))) {
+ if ($op == 'create' && user_access('create ' . $type . ' redirects', $account)) {
+ return REDIRECT_ACCESS_ALLOW;
+ }
+
+ if ($op == 'update') {
+ if (user_access('edit any ' . $type . ' content', $account) || (user_access('edit own ' . $type . ' content', $account) && ($account->uid == $redirect->uid))) {
+ return REDIRECT_ACCESS_ALLOW;
+ }
+ }
+
+ if ($op == 'delete') {
+ if (user_access('delete any ' . $type . ' content', $account) || (user_access('delete own ' . $type . ' content', $account) && ($account->uid == $redirect->uid))) {
+ return REDIRECT_ACCESS_ALLOW;
+ }
+ }
+ }
+
+ // Returning nothing from this function would have the same effect.
+ return REDIRECT_ACCESS_IGNORE;
+}
+
+/**
+ * Act on a redirect object about to be shown on the add/edit form.
+ *
+ * This hook is invoked from redirect_object_prepare().
+ *
+ * @param $redirect
+ * The redirect that is about to be shown on the add/edit form.
+ *
+ * @ingroup redirect_api_hooks
+ */
+function hook_redirect_prepare($redirect) {
+
+}
+
+/**
+ * Perform redirect validation before a redirect is created or updated.
+ *
+ * This hook is invoked from redirect_validate(), after a user has has finished
+ * editing the redirect and is submitting it. It is invoked at the end of all
+ * the standard validation steps.
+ *
+ * To indicate a validation error, use form_set_error().
+ *
+ * Note: Changes made to the $redirect object within your hook implementation
+ * will have no effect. The preferred method to change a redirect's content is
+ * to use hook_redirect_presave() instead. If it is really necessary to change
+ * the redirect at the validate stage, you can use form_set_value().
+ *
+ * @param $redirect
+ * The redirect being validated.
+ * @param $form
+ * The form being used to edit the redirect.
+ * @param $form_state
+ * The form state array.
+ *
+ * @see redirect_validate()
+ * @ingroup redirect_api_hooks
+ */
+function hook_redirect_validate($redirect, $form, $form_state) {
+
+}
+
+/**
+ * Act on a redirect being inserted or updated.
+ *
+ * This hook is invoked from redirect_save() before the redirect is saved to
+ * the database.
+ *
+ * @param $redirect
+ * The redirect that is being inserted or updated.
+ *
+ * @see redirect_save()
+ * @ingroup redirect_api_hooks
+ */
+function hook_redirect_presave($redirect) {
+
+}
+
+/**
+ * Respond to creation of a new redirect.
+ *
+ * This hook is invoked from redirect_save() after the redirect is inserted
+ * into the redirect table in the database.
+ *
+ * @param $redirect
+ * The redirect that is being created.
+ *
+ * @see redirect_save()
+ * @ingroup redirect_api_hooks
+ */
+function hook_redirect_insert($redirect) {
+
+}
+
+/**
+ * Respond to updates to a redirect.
+ *
+ * This hook is invoked from redirect_save() after the redirect is updated in
+ * the redirect table in the database.
+ *
+ * @param $redirect
+ * The redirect that is being updated.
+ *
+ * @see redirect_save()
+ * @ingroup redirect_api_hooks
+ */
+function hook_redirect_update($redirect) {
+
+}
+
+/**
+ * Respond to redirect deletion.
+ *
+ * This hook is invoked from redirect_delete_multiple() after the redirect has
+ * been removed from the redirect table in the database.
+ *
+ * @param $redirect
+ * The redirect that is being deleted.
+ *
+ * @see redirect_delete_multiple()
+ * @ingroup redirect_api_hooks
+ */
+function hook_redirect_delete($redirect) {
+
+}
+
+/**
+ * Act on a redirect being redirected.
+ *
+ * This hook is invoked from redirect_redirect() before the redirect callback
+ * is invoked.
+ *
+ * @param $redirect
+ * The redirect that is being used for the redirect.
+ *
+ * @see redirect_redirect()
+ * @see drupal_page_is_cacheable()
+ * @ingroup redirect_api_hooks
+ */
+function hook_redirect_alter($redirect) {
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
View
72 amani/modules/contrib/redirect/redirect.drush.inc
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Drush integration for the redirect module.
+ */
+
+/**
+ * Implementation of hook_drush_command().
+ */
+function redirect_drush_command() {
+ $items['generate-redirects'] = array(
+ 'description' => 'Create redirects.',
+ 'drupal dependencies' => array('devel_generate'),
+ 'arguments' => array(
+ 'count' => 'Number of redirects to generate.',
+ ),
+ 'options' => array(
+ 'delete' => 'Delete all redirects before generating new ones.',
+ ),
+ );
+
+ return $items;
+}
+
+/**
+ * Command callback. Generate a number of redirects.
+ */
+function drush_redirect_generate_redirects($count = NULL) {
+ if (drush_generate_is_number($count) == FALSE) {
+ return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', t('Invalid number of redirects.'));
+ }
+ module_load_include('inc', 'redirect', 'redirect.generate');
+ drush_generate_include_devel();
+ redirect_run_unprogressive_batch('redirect_generate_redirects_batch_info', $count, drush_get_option('delete'));
+}
+
+/**
+ * Perform an unprogressive batch process for CLI.
+ */
+function redirect_run_unprogressive_batch() {
+ $batch = batch_get();
+ if (!empty($batch)) {
+ // If there is already something in the batch, don't run.
+ return FALSE;
+ }
+
+ $args = func_get_args();
+ $batch_callback = array_shift($args);
+
+ if (!lock_acquire($batch_callback)) {
+ return FALSE;
+ }
+
+ // Attempt to increase the execution time.
+ drupal_set_time_limit(240);
+
+ // Build the batch array.
+ $batch = call_user_func_array($batch_callback, $args);
+ batch_set($batch);
+
+ // We need to manually set the progressive variable again.
+ // @todo Remove when http://drupal.org/node/638712 is fixed.
+ $batch =& batch_get();
+ $batch['progressive'] = FALSE;
+
+ // Run the batch process.
+ batch_process();
+
+ lock_release($batch_callback);
+ return TRUE;
+}
View
182 amani/modules/contrib/redirect/redirect.generate.inc
@@ -0,0 +1,182 @@
+<?php
+
+/**
+ * @file
+ * Devel generate integration for the redirect module.
+ */
+
+function redirect_generate_form() {
+ $form['count'] = array(
+ '#type' => 'textfield',
+ '#title' => t('How many URL redirects would you like to generate?'),
+ '#default_value' => 50,
+ '#size' => 4,
+ );
+ $form['delete'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Delete all URL redirects before generating new URL redirects.'),
+ '#default_value' => FALSE,
+ );
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Generate'),
+ );
+
+ return $form;
+}
+
+function redirect_generate_form_submit(&$form, &$form_state) {
+ // Run the batch.
+ $batch = redirect_generate_redirects_batch_info($form_state['values']['count'], $form_state['values']['delete']);
+ batch_set($batch);
+}
+
+function redirect_generate_redirects_batch_info($count, $delete = FALSE) {
+ if ($delete) {
+ $operations[] = array('redirect_generate_batch_delete', array());
+ }
+
+ $operations[] = array('redirect_generate_batch_generate', array($count));
+
+ return array(
+ 'operations' => $operations,
+ 'finished' => 'redirect_generate_batch_finished',
+ 'file' => drupal_get_path('module', 'redirect') . '/redirect.generate.inc',
+ );
+}
+
+function redirect_generate_batch_delete(array &$context) {
+ if (empty($context['sandbox'])) {
+ $context['sandbox'] = array();
+ $context['sandbox']['progress'] = 0;
+ $context['sandbox']['current_rid'] = 0;
+ $context['sandbox']['max'] = db_query('SELECT COUNT(DISTINCT rid) FROM {redirect}')->fetchField();
+ }
+
+ $limit = 20;
+ $rids = db_query_range("SELECT rid FROM {redirect} WHERE rid > :rid ORDER BY rid", 0, $limit, array(':rid' => $context['sandbox']['current_rid']))->fetchCol();
+ redirect_delete_multiple($rids);
+
+ // Update our progress information.
+ $context['sandbox']['progress'] += count($rids);
+ $context['sandbox']['current_rid'] = end($rids);
+ $context['message'] = t('Deleted URL redirect @rid.', array('@rid' => end($rids)));
+
+ // Inform the batch engine that we are not finished,
+ // and provide an estimation of the completion level we reached.
+ if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
+ $context['finished'] = ($context['sandbox']['progress'] >= $context['sandbox']['max']);
+ }
+}
+
+function redirect_generate_batch_generate($num, array &$context) {
+ if (empty($context['sandbox'])) {
+ $context['sandbox'] = array();
+ $context['sandbox']['progress'] = 0;
+ $context['sandbox']['max'] = $num;
+
+ $query = db_select('node', 'n');
+ $query->addField('n', 'nid');
+ $query->condition('n.status', NODE_PUBLISHED);
+ $query->addTag('node_access');
+ $context['sandbox']['nids'] = $query->execute()->fetchAllKeyed(0, 0);
+ }
+
+ module_load_include('inc', 'devel_generate');
+
+ $limit = 20;
+ $types = array_keys(redirect_status_code_options());
+ $languages = module_exists('locale') ? array_keys(locale_language_list('name')) : array();
+
+ for ($i = 0; $i < min($limit, $context['sandbox']['max'] - $context['sandbox']['progress']); $i++) {
+ $rand = mt_rand(0, 100);
+
+ $redirect = new stdClass();
+ redirect_object_prepare($redirect);
+ $redirect->source = _redirect_generate_url();
+ $redirect->devel_generate = TRUE;
+
+ if ($context['sandbox']['nids'] && $rand >= 40) {
+ $redirect->redirect = 'node/'. array_rand($context['sandbox']['nids']);
+ }
+ else {
+ $redirect->redirect = _redirect_generate_url(TRUE);
+ if ($rand <= 20) {
+ $redirect->redirect_options['query'] = _redirect_generate_querystring();
+ }
+ if ($rand <= 5) {
+ $redirect->redirect_options['fragment'] = devel_generate_word(mt_rand(4, 8));
+ }
+ }
+
+ if ($rand <= 20) {
+ $redirect->status_code = $types[array_rand($types)];
+ }
+
+ if ($languages && $rand <= 20) {
+ $redirect->language = $languages[array_rand($languages)];
+ }
+
+ if ($rand <= 30) {
+ $redirect->source_options['query'] = _redirect_generate_querystring();
+ }
+
+ redirect_save($redirect);
+
+ if (mt_rand(0, 1)) {
+ db_update('redirect')
+ ->fields(array(
+ 'count' => mt_rand(1, 500),
+ 'access' => mt_rand(REQUEST_TIME - 31536000, REQUEST_TIME),
+ ))
+ ->condition('rid', $redirect->rid)
+ ->execute();
+ }
+
+ $context['results'][] = $redirect->rid;
+ }
+
+ // Update our progress information.
+ $context['sandbox']['progress'] += $limit;
+ //$context['message'] = t('Deleted URL redirect @rid.', array('@rid' => end($rids)));
+
+ // Inform the batch engine that we are not finished,
+ // and provide an estimation of the completion level we reached.
+ if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
+ $context['finished'] = ($context['sandbox']['progress'] >= $context['sandbox']['max']);
+ }
+}
+
+function redirect_generate_batch_finished($success, $results, $operations) {
+ if ($success) {
+ drupal_set_message(format_plural(count($results), 'One URL redirect created.', '@count URL redirects created.'));
+ }
+ else {
+ // An error occurred.
+ // $operations contains the operations that remained unprocessed.
+ $error_operation = reset($operations);
+ drupal_set_message(t('An error occurred while processing @operation with arguments : @args', array('@operation' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE))));
+ }
+}
+
+function _redirect_generate_url($external = FALSE, $max_levels = 2) {
+ module_load_include('inc', 'devel_generate');
+
+ $url = array();
+ if ($external) {
+ $tlds = array('com', 'net', 'org');
+ $url[] = 'http://www.example.'. $tlds[array_rand($tlds)];
+ }
+ $max_levels = mt_rand($external ? 0 : 1, $max_levels);
+ for ($i = 1; $i <= $max_levels; $i++) {
+ $url[] = devel_generate_word(mt_rand(6 / $i, 8));
+ }
+ return implode('/', $url);
+}
+
+function _redirect_generate_querystring() {
+ module_load_include('inc', 'devel_generate');
+
+ $query = array(devel_generate_word(mt_rand(1, 3)) => devel_generate_word(mt_rand(2, 4)));
+ return $query;
+}
View
23 amani/modules/contrib/redirect/redirect.info
@@ -0,0 +1,23 @@
+name = Redirect
+description = Allows users to redirect from old URLs to new URLs.
+core = 7.x
+files[] = redirect.module
+files[] = redirect.admin.inc
+files[] = redirect.install
+files[] = redirect.test
+files[] = views/redirect.views.inc
+;files[] = views/redirect_handler_field_redirect_type.inc
+files[] = views/redirect_handler_filter_redirect_type.inc
+files[] = views/redirect_handler_field_redirect_source.inc
+files[] = views/redirect_handler_field_redirect_redirect.inc
+files[] = views/redirect_handler_field_redirect_operations.inc
+files[] = views/redirect_handler_field_redirect_link_edit.inc
+files[] = views/redirect_handler_field_redirect_link_delete.inc
+configure = admin/config/search/redirect/settings
+
+; Information added by drupal.org packaging script on 2012-09-18
+version = "7.x-1.0-rc1"
+core = "7.x"
+project = "redirect"
+datestamp = "1347989995"
+
View
358 amani/modules/contrib/redirect/redirect.install
@@ -0,0 +1,358 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the redirect module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function redirect_schema() {
+ $schema['redirect'] = array(
+ 'description' => 'Stores information on redirects.',
+ 'fields' => array(
+ 'rid' => array(
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ 'description' => 'Primary Key: Unique redirect ID.',
+ ),
+ 'hash' => array(
+ 'type' => 'varchar',
+ 'length' => 64,
+ 'not null' => TRUE,
+ 'description' => 'A unique hash based on source, source_options, and language.',
+ ),
+ 'type' => array(
+ 'type' => 'varchar',
+ 'length' => 64,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => "The redirect type; if value is 'redirect' it is a normal redirect handled by the module.",
+ ),
+ 'uid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The {users}.uid of the user who created the redirect.',
+ ),
+ 'source' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'description' => 'The source path to redirect from.',
+ ),
+ 'source_options' => array(
+ 'type' => 'text',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ 'description' => 'A serialized array of source options.',
+ ),
+ 'redirect' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'description' => 'The destination path to redirect to.',
+ ),
+ 'redirect_options' => array(
+ 'type' => 'text',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ 'description' => 'A serialized array of redirect options.',
+ ),
+ 'language' => array(
+ 'description' => 'The language this redirect is for; if blank, the alias will be used for unknown languages.',
+ 'type' => 'varchar',
+ 'length' => 12,
+ 'not null' => TRUE,
+ 'default' => 'und',
+ ),
+ 'status_code' => array(
+ 'type' => 'int',
+ 'size' => 'small',
+ 'not null' => TRUE,
+ 'description' => 'The HTTP status code to use for the redirect.',
+ ),
+ 'count' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The number of times the redirect has been used.',
+ ),
+ 'access' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The timestamp of when the redirect was last accessed.'
+ ),
+ ),
+ 'primary key' => array('rid'),
+ 'unique keys' => array(
+ 'hash' => array('hash'),
+ ),
+ 'indexes' => array(
+ 'expires' => array('type', 'access'),
+ 'source_language' => array('source', 'language'),
+ ),
+ );
+
+ return $schema;
+}
+
+/**
+ * Implements hook_install().
+ */
+function redirect_install() {
+ // If the path redirect table exists, then set the schema to run the
+ // migration update function.
+ if (db_table_exists('path_redirect')) {
+ drupal_set_installed_schema_version('redirect', 6999);
+ }
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function redirect_uninstall() {
+ drupal_load('module', 'redirect');
+ $variables = array_keys(redirect_variables());
+ foreach ($variables as $variable) {
+ variable_del($variable);
+ }
+}
+
+/**
+ * Add the {redirect}.count field.
+ */
+function redirect_update_1() {
+ $field = array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The number of times the redirect has been used.',
+ );
+ db_add_field('redirect', 'count', $field);
+}
+
+/**
+ * Add the {redirect}.uid field.
+ */
+function redirect_update_2() {
+ $field = array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The {users}.uid of the user who created the redirect.',
+ );
+ db_add_field('redirect', 'uid', $field);
+ db_update('redirect')
+ ->fields(array('uid' => 1))
+ ->execute();
+}
+
+/**
+ * Enable bootstrap status for the module.
+ */
+function redirect_update_3() {
+ db_update('system')
+ ->fields(array('bootstrap' => 1))
+ ->condition('type', 'module')
+ ->condition('name', 'redirect')
+ ->execute();
+}
+
+/**
+ * Change empty redirect types to 'redirect'.
+ */
+function redirect_update_4() {
+ db_update('redirect')
+ ->fields(array('type' => 'redirect'))
+ ->condition('type', '')
+ ->execute();
+}
+
+/**
+ * Rename {redirect}.last_used to {redirect}.access.
+ */
+function redirect_update_5() {
+ if (db_field_exists('redirect', 'last_used')) {
+ db_drop_index('redirect', 'expires');
+ db_change_field('redirect', 'last_used', 'access', array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The timestamp of when the redirect was last accessed.',
+ ));
+ db_add_index('redirect', 'expires', array('type', 'access'));
+ }
+}
+
+/**
+ * Add an index on the source and language columns in the redirect table.
+ */
+function redirect_update_6() {
+ if (!db_index_exists('redirect', 'source_language')) {
+ db_add_index('redirect', 'source_language', array('source', 'language'));
+ }
+}
+
+/**
+ * Migrate data and variables from the Drupal 6 path_redirect module.
+ */
+function redirect_update_7000(&$sandbox) {
+ if (!isset($sandbox['progress']) && db_table_exists('path_redirect')) {
+ $sandbox['progress'] = 0;
+ $sandbox['current_rid'] = 0;
+ $sandbox['max'] = db_query('SELECT COUNT(rid) FROM {path_redirect}')->fetchField();
+ $sandbox['skipped'] = array();
+ }
+
+ if (empty($sandbox['max'])) {
+ $sandbox['#finished'] = 1;
+ return t('No redirects to migrate.');
+ }
+
+ // Ensure the redirect module is loaded since we need to use its functions.
+ drupal_load('module', 'redirect');
+
+ $query = db_query_range("SELECT * FROM {path_redirect} WHERE rid > :rid ORDER BY rid", 0, 25, array(':rid' => $sandbox['current_rid']));
+ foreach ($query as $old_redirect) {
+ $redirect = _redirect_migrate_path_redirect_redirect($old_redirect);
+
+ if (empty($redirect->success)) {
+ $sandbox['skipped'][$old_redirect->rid] = t('RID @rid: @from to @to', array(
+ '@rid' => $old_redirect->rid,
+ '@from' => redirect_url($redirect->source, $redirect->source_options),
+ '@to' => redirect_url($redirect->redirect, $redirect->redirect_options),
+ ));
+ }
+ $sandbox['progress']++;
+ $sandbox['current_rid'] = $old_redirect->rid;
+ }
+
+ $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
+
+ if ($sandbox['#finished'] >= 1) {
+ // Once finished, drop the old table.
+ db_drop_table('path_redirect');
+
+ // Migrate variables.
+ _redirect_migrate_path_redirect_variables();
+
+ // Remove the path_redirect entry from the system table.
+ db_delete('system')
+ ->condition('name', 'path_redirect')
+ ->execute();
+
+ // Show a message about how many redirects were migrated, and how many
+ // were skipped.
+ $skipped = count($sandbox['skipped']);
+ $migrated = $sandbox['progress'] - $skipped;
+ // @todo The following strings should be using t().
+ $return = "Migrated $migrated redirects.";
+ if (!empty($sandbox['skipped'])) {
+ $return .= " The following $skipped redirects were not migrated since there were already existing redirects for the path and language combination:" . theme('item_list', array('items' => $sandbox['skipped']));
+ }
+ return $return;
+ }
+}
+
+/**
+ * Migrate a path redirect redirect to a redirect redirect.