first import
This commit is contained in:
4
sites/all/modules/xmlsitemap/CHANGELOG.txt
Normal file
4
sites/all/modules/xmlsitemap/CHANGELOG.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
XML Sitemap 7.x-2.x-dev
|
||||
-----------------------
|
||||
|
339
sites/all/modules/xmlsitemap/LICENSE.txt
Normal file
339
sites/all/modules/xmlsitemap/LICENSE.txt
Normal file
@@ -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.
|
86
sites/all/modules/xmlsitemap/README.txt
Normal file
86
sites/all/modules/xmlsitemap/README.txt
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
* Installing
|
||||
* Uninstalling
|
||||
* Frequently Asked Questions (FAQ)
|
||||
* Known Issues
|
||||
* More Information
|
||||
* How Can You Contribute?
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
Current Maintainer: Dave Reid <http://drupal.org/user/53892>
|
||||
Co-maintainer: Kiam <http://drupal.org/user/55077>
|
||||
Co-maintainer: Earnie <http://drupal.org/user/86710>
|
||||
Co-maintainer: Darren Oh <http://drupal.org/user/30772>
|
||||
Original Author: Matthew Loar <http://drupal.org/user/24879>
|
||||
|
||||
XML Sitemap automatically creates a sitemap that conforms to the sitemaps.org
|
||||
specification. This helps search engines keep their search results up to date.
|
||||
|
||||
|
||||
INSTALLING
|
||||
----------
|
||||
|
||||
See http://drupal.org/getting-started/install-contrib for instructions on
|
||||
how to install or update Drupal modules.
|
||||
|
||||
Once XML Sitemap is installed and enabled, you can adjust the settings for your
|
||||
site's sitemap at admin/config/search/xmlsitemap. Your can view your site's
|
||||
sitemap at http://yoursite.com/sitemap.xml.
|
||||
|
||||
It is highly recommended that you have clean URLs enabled for this module.
|
||||
|
||||
|
||||
UNINSTALLING
|
||||
------------
|
||||
|
||||
Because Drupal does not uninstall modules in reverse order of their
|
||||
dependencies, if you want to uninstall all the XML sitemap modules, be sure to
|
||||
disable and uninstall all the sub-modules before the base XML sitemap module.
|
||||
To help fix this bug in Drupal core, visit http://drupal.org/node/151452.
|
||||
|
||||
|
||||
FREQUENTLY ASKED QUESTIONS (FAQ)
|
||||
--------------------------------
|
||||
|
||||
- There are no frequently asked questions at this time.
|
||||
|
||||
|
||||
KNOWN ISSUES
|
||||
------------
|
||||
|
||||
- See http://drupal.org/node/482550 for a list of the current known issues.
|
||||
|
||||
|
||||
MORE INFORMATION
|
||||
----------------
|
||||
|
||||
- To issue any bug reports, feature or support requests, see the module issue
|
||||
queue at http://drupal.org/project/issues/xmlsitemap.
|
||||
|
||||
- For additional documentation, see the online module handbook at
|
||||
http://drupal.org/handbook/modules/gsitemap.
|
||||
|
||||
- You can view the sitemap.org specification at http://sitemaps.org.
|
||||
|
||||
|
||||
HOW CAN YOU CONTRIBUTE?
|
||||
-----------------------
|
||||
|
||||
- Report any bugs, feature requests, etc. in the issue tracker.
|
||||
http://drupal.org/project/issues/xmlsitemap
|
||||
|
||||
- Help translate this module.
|
||||
http://localize.drupal.org/translate/projects/xmlsitemap
|
||||
|
||||
- Write a review for this module at drupalmodules.com.
|
||||
http://drupalmodules.com/module/xml-sitemap
|
||||
|
||||
- Help keep development active by dontating to the developer.
|
||||
http://davereid.chipin.com/
|
861
sites/all/modules/xmlsitemap/xmlsitemap.admin.inc
Normal file
861
sites/all/modules/xmlsitemap/xmlsitemap.admin.inc
Normal file
@@ -0,0 +1,861 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Administrative page callbacks for the xmlsitemap module.
|
||||
*
|
||||
* @ingroup xmlsitemap
|
||||
*/
|
||||
|
||||
/**
|
||||
* Render a tableselect list of XML sitemaps for the main admin page.
|
||||
*/
|
||||
function xmlsitemap_sitemap_list_form() {
|
||||
$destination = drupal_get_destination();
|
||||
|
||||
// Build the 'Update options' form.
|
||||
$form['#operations'] = module_invoke_all('xmlsitemap_sitemap_operations');
|
||||
$operations = array();
|
||||
foreach ($form['#operations'] as $operation => $operation_info) {
|
||||
$operations[$operation] = $operation_info['label'];
|
||||
}
|
||||
asort($operations);
|
||||
|
||||
$form['operations'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Update options'),
|
||||
'#prefix' => '<div class="container-inline">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
$form['operations']['operation'] = array(
|
||||
'#type' => 'select',
|
||||
'#options' => $operations,
|
||||
'#default_value' => 'update',
|
||||
);
|
||||
$form['operations']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Update'),
|
||||
//'#validate' => array('xmlsitemap_sitemap_list_form_validate'),
|
||||
//'#submit' => array('xmlsitemap_sitemap_list_form_submit'),
|
||||
);
|
||||
|
||||
$contexts = xmlsitemap_get_context_info();
|
||||
|
||||
$header = array();
|
||||
$header['url'] = array('data' => t('URL'));
|
||||
foreach ($contexts as $context_key => $context_info) {
|
||||
if (!empty($context_info['summary callback'])) {
|
||||
$header['context_' . $context_key] = $context_info['label'];
|
||||
}
|
||||
}
|
||||
$header['updated'] = array('data' => t('Last updated'), 'field' => 'updated', 'sort' => 'asc');
|
||||
$header['links'] = array('data' => t('Links'), 'field' => 'links');
|
||||
$header['chunks'] = array('data' => t('Pages'), 'field' => 'chunks');
|
||||
$header['operations'] = array('data' => t('Operations'));
|
||||
|
||||
$query = db_select('xmlsitemap_sitemap');
|
||||
$query->fields('xmlsitemap_sitemap', array('smid'));
|
||||
$query->extend('TableSort')->orderByHeader($header);
|
||||
$smids = $query->execute()->fetchCol();
|
||||
$sitemaps = $smids ? xmlsitemap_sitemap_load_multiple($smids) : array();
|
||||
|
||||
$options = array();
|
||||
foreach ($sitemaps as $smid => $sitemap) {
|
||||
$sitemap->url = url($sitemap->uri['path'], $sitemap->uri['options']);
|
||||
|
||||
$options[$smid]['url'] = array(
|
||||
'data' => array(
|
||||
'#type' => 'link',
|
||||
'#title' => $sitemap->url,
|
||||
'#href' => $sitemap->url,
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($contexts as $context_key => $context_info) {
|
||||
if (!empty($context_info['summary callback'])) {
|
||||
$options[$smid]['context_' . $context_key] = _xmlsitemap_sitemap_context_summary($sitemap, $context_key, $context_info);
|
||||
}
|
||||
}
|
||||
|
||||
$options[$smid]['updated'] = $sitemap->updated ? format_date($sitemap->updated, 'short') : t('Never');
|
||||
$options[$smid]['links'] = $sitemap->updated ? $sitemap->links : '-';
|
||||
$options[$smid]['chunks'] = $sitemap->updated ? $sitemap->chunks : '-';
|
||||
|
||||
// @todo Highlight sitemaps that need updating.
|
||||
//$options[$smid]['#attributes']['class'][] = 'warning';
|
||||
|
||||
$operations = array();
|
||||
$operations['edit'] = xmlsitemap_get_operation_link('admin/config/search/xmlsitemap/edit/' . $smid, array('title' => t('Edit'), 'modal' => TRUE));
|
||||
$operations['delete'] = xmlsitemap_get_operation_link('admin/config/search/xmlsitemap/delete/' . $smid, array('title' => t('Delete'), 'modal' => TRUE));
|
||||
if ($operations) {
|
||||
$options[$smid]['operations'] = array(
|
||||
'data' => array(
|
||||
'#theme' => 'links',
|
||||
'#links' => $operations,
|
||||
'#attributes' => array('class' => array('links', 'inline')),
|
||||
),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$options[$smid]['operations'] = t('None (sitemap locked)');
|
||||
}
|
||||
}
|
||||
|
||||
$form['sitemaps'] = array(
|
||||
'#type' => 'tableselect',
|
||||
'#header' => $header,
|
||||
'#options' => $options,
|
||||
'#empty' => t('No XML sitemaps available.') . ' ' . l('Add a new XML sitemap', 'admin/config/search/xmlsitemap/add'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate xmlsitemap_sitemap_list_form submissions.
|
||||
*/
|
||||
function xmlsitemap_sitemap_list_form_validate($form, &$form_state) {
|
||||
$form_state['values']['sitemaps'] = array_filter($form_state['values']['sitemaps']);
|
||||
|
||||
// Error if there are no items to select.
|
||||
if (!count($form_state['values']['sitemaps'])) {
|
||||
form_set_error('', t('No sitemaps selected.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process xmlsitemap_sitemap_list_form submissions.
|
||||
*
|
||||
* Execute the chosen 'Update option' on the selected sitemaps.
|
||||
*/
|
||||
function xmlsitemap_sitemap_list_form_submit($form, &$form_state) {
|
||||
$operation = $form['#operations'][$form_state['values']['operation']];
|
||||
|
||||
// Filter out unchecked sitemaps.
|
||||
$sitemaps = array_filter($form_state['values']['sitemaps']);
|
||||
|
||||
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($sitemaps), $operation['callback arguments']);
|
||||
}
|
||||
else {
|
||||
$args = array($sitemaps);
|
||||
}
|
||||
call_user_func_array($function, $args);
|
||||
|
||||
$count = count($form_state['values']['sitemaps']);
|
||||
//watchdog('xmlsitemap', '@action @count XML sitemaps.', array('@action' => $operation['action past'], '@count' => $count));
|
||||
drupal_set_message(format_plural(count($sitemaps), '@action @count XML sitemap.', '@action @count XML sitemaps.', array('@action' => $operation['action past'], '@count' => $count)));
|
||||
//$form_state['redirect'] = 'admin/config/search/xmlsitemap';
|
||||
}
|
||||
}
|
||||
|
||||
function xmlsitemap_sitemap_edit_form(array $form, array &$form_state, stdClass $sitemap = NULL) {
|
||||
_xmlsitemap_set_breadcrumb();
|
||||
|
||||
if (!isset($sitemap)) {
|
||||
$sitemap = new stdClass();
|
||||
$sitemap->smid = NULL;
|
||||
$sitemap->context = array();
|
||||
}
|
||||
|
||||
$form['#sitemap'] = $sitemap;
|
||||
|
||||
$form['smid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $sitemap->smid,
|
||||
);
|
||||
// The context settings should be form_alter'ed by the context modules.
|
||||
$form['context'] = array(
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
$form['actions'] = array(
|
||||
'#type' => 'actions',
|
||||
);
|
||||
$form['actions']['save'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save'),
|
||||
);
|
||||
$form['actions']['cancel'] = array(
|
||||
'#type' => 'link',
|
||||
'#href' => isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/search/xmlsitemap',
|
||||
'#title' => t('Cancel'),
|
||||
);
|
||||
|
||||
// Let other modules alter this form with their context settings.
|
||||
$form['#pre_render'][] = 'xmlsitemap_sitemap_edit_form_pre_render';
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
function xmlsitemap_sitemap_edit_form_pre_render($form) {
|
||||
$visible_children = element_get_visible_children($form['context']);
|
||||
if (empty($visible_children)) {
|
||||
$form['context']['empty'] = array(
|
||||
'#type' => 'markup',
|
||||
'#markup' => '<p>' . t('There are currently no XML sitemap contexts available.') . '</p>',
|
||||
);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
function xmlsitemap_sitemap_edit_form_validate($form, &$form_state) {
|
||||
// If there are no context options, the $form_state['values']['context']
|
||||
// disappears.
|
||||
$form_state['values'] += array('context' => array());
|
||||
$existing = xmlsitemap_sitemap_load_by_context($form_state['values']['context']);
|
||||
if ($existing && $existing->smid != $form_state['values']['smid']) {
|
||||
form_set_error('context', t('A sitemap with the same context already exists.'));
|
||||
}
|
||||
}
|
||||
|
||||
function xmlsitemap_sitemap_edit_form_submit($form, &$form_state) {
|
||||
form_state_values_clean($form_state);
|
||||
$sitemap = (object) $form_state['values'];
|
||||
xmlsitemap_sitemap_save($sitemap);
|
||||
drupal_set_message(t('The sitemap has been saved.'));
|
||||
$form_state['redirect'] = 'admin/config/search/xmlsitemap';
|
||||
}
|
||||
|
||||
function xmlsitemap_sitemap_delete_form(array $form, array &$form_state, stdClass $sitemap) {
|
||||
_xmlsitemap_set_breadcrumb();
|
||||
|
||||
$count = (int) db_query("SELECT COUNT(smid) FROM {xmlsitemap_sitemap}")->fetchField();
|
||||
if ($count === 1 && empty($_POST)) {
|
||||
drupal_set_message(t('It is not recommended to delete the only XML sitemap.'), 'error');
|
||||
}
|
||||
|
||||
$form['#sitemap'] = $sitemap;
|
||||
$form['smid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $sitemap->smid,
|
||||
);
|
||||
return confirm_form(
|
||||
$form,
|
||||
t('Are you sure you want to delete the XML sitemap?'),
|
||||
'admin/config/search/xmlsitemap',
|
||||
'',
|
||||
t('Delete'),
|
||||
t('Cancel')
|
||||
);
|
||||
}
|
||||
|
||||
function xmlsitemap_sitemap_delete_form_submit($form, $form_state) {
|
||||
xmlsitemap_sitemap_delete($form_state['values']['smid']);
|
||||
drupal_set_message(t('The sitemap has been deleted.'));
|
||||
$form_state['redirect'] = 'admin/config/search/xmlsitemap';
|
||||
}
|
||||
|
||||
/**
|
||||
* Form builder; Administration settings form.
|
||||
*
|
||||
* @see system_settings_form()
|
||||
* @see xmlsitemap_settings_form_validate()
|
||||
*/
|
||||
function xmlsitemap_settings_form($form, &$form_state) {
|
||||
$form['xmlsitemap_minimum_lifetime'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Minimum sitemap lifetime'),
|
||||
'#options' => array(0 => t('No minimum')) + drupal_map_assoc(array(300, 900, 1800, 3600, 10800, 21600, 43200, 86400, 172800, 259200, 604800), 'format_interval'),
|
||||
'#description' => t('The minimum amount of time that will elapse before the sitemaps are regenerated. The sitemaps will also only be regenerated on cron if any links have been added, updated, or deleted.') . '<br />' . t('Recommended value: %value.', array('%value' => t('1 day'))),
|
||||
'#default_value' => variable_get('xmlsitemap_minimum_lifetime', 0),
|
||||
);
|
||||
$form['xmlsitemap_xsl'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Include a stylesheet in the sitemaps for humans.'),
|
||||
'#description' => t('When enabled, this will add formatting and tables with sorting to make it easier to view the XML sitemap data instead of viewing raw XML output. Search engines will ignore this.'),
|
||||
'#default_value' => variable_get('xmlsitemap_xsl', 1),
|
||||
);
|
||||
$form['xmlsitemap_prefetch_aliases'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Prefetch URL aliases during sitemap generation.'),
|
||||
'#description' => t('When enabled, this will fetch all URL aliases at once instead of one at a time during sitemap generation. For medium or large sites, it is recommended to disable this feature as it uses a lot of memory.'),
|
||||
'#default_value' => variable_get('xmlsitemap_prefetch_aliases', 1),
|
||||
);
|
||||
|
||||
$form['advanced'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Advanced settings'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => !variable_get('xmlsitemap_developer_mode', 0),
|
||||
'#weight' => 10,
|
||||
);
|
||||
//$form['advanced']['xmlsitemap_gz'] = array(
|
||||
// '#type' => 'checkbox',
|
||||
// '#title' => t('Generate additional compressed sitemaps using gzip.'),
|
||||
// '#default_value' => xmlsitemap_var('gz'),
|
||||
// '#disabled' => !function_exists('gzencode'),
|
||||
//);
|
||||
$form['advanced']['xmlsitemap_chunk_size'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Number of links in each sitemap page'),
|
||||
'#options' => array('auto' => t('Automatic (recommended)')) + drupal_map_assoc(array(100, 500, 1000, 2500, 5000, 10000, 25000, XMLSITEMAP_MAX_SITEMAP_LINKS)),
|
||||
'#default_value' => xmlsitemap_var('chunk_size'),
|
||||
// @todo This description is not clear.
|
||||
'#description' => t('If there are problems with rebuilding the sitemap, you may want to manually set this value. If you have more than @max links, an index with multiple sitemap pages will be generated. There is a maximum of @max sitemap pages.', array('@max' => XMLSITEMAP_MAX_SITEMAP_LINKS)),
|
||||
);
|
||||
$form['advanced']['xmlsitemap_batch_limit'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Maximum number of sitemap links to process at once'),
|
||||
'#options' => drupal_map_assoc(array(5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000)),
|
||||
'#default_value' => xmlsitemap_var('batch_limit'),
|
||||
'#description' => t('If you have problems running cron or rebuilding the sitemap, you may want to lower this value.'),
|
||||
);
|
||||
if (!xmlsitemap_check_directory()) {
|
||||
form_set_error('xmlsitemap_path', t('The directory %directory does not exist or is not writable.', array('%directory' => xmlsitemap_get_directory())));
|
||||
}
|
||||
$form['advanced']['xmlsitemap_path'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Sitemap cache directory'),
|
||||
'#default_value' => variable_get('xmlsitemap_path', 'xmlsitemap'),
|
||||
'#size' => 30,
|
||||
'#maxlength' => 255,
|
||||
'#description' => t('Subdirectory where the sitemap data will be stored. This folder <strong>must not be shared</strong> with any other Drupal site or install using XML sitemap.'),
|
||||
'#field_prefix' => file_build_uri(''),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['advanced']['xmlsitemap_base_url'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Default base URL'),
|
||||
'#default_value' => variable_get('xmlsitemap_base_url', $GLOBALS['base_url']),
|
||||
'#size' => 30,
|
||||
'#description' => t('This is the default base URL used for sitemaps and sitemap links.'),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['advanced']['xmlsitemap_lastmod_format'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Last modification date format'),
|
||||
'#options' => array(
|
||||
XMLSITEMAP_LASTMOD_SHORT => t('Short'),
|
||||
XMLSITEMAP_LASTMOD_MEDIUM => t('Medium'),
|
||||
XMLSITEMAP_LASTMOD_LONG => t('Long'),
|
||||
),
|
||||
'#default_value' => variable_get('xmlsitemap_lastmod_format', XMLSITEMAP_LASTMOD_MEDIUM),
|
||||
);
|
||||
foreach ($form['advanced']['xmlsitemap_lastmod_format']['#options'] as $key => &$label) {
|
||||
$label .= ' (' . gmdate($key, REQUEST_TIME) . ')';
|
||||
}
|
||||
$form['advanced']['xmlsitemap_developer_mode'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable developer mode to expose additional settings.'),
|
||||
'#default_value' => variable_get('xmlsitemap_developer_mode', 0),
|
||||
);
|
||||
|
||||
$form['xmlsitemap_settings'] = array(
|
||||
'#type' => 'vertical_tabs',
|
||||
'#weight' => 20,
|
||||
);
|
||||
|
||||
$entities = xmlsitemap_get_link_info(NULL, TRUE);
|
||||
module_load_all_includes('inc', 'xmlsitemap');
|
||||
foreach ($entities as $entity => $entity_info) {
|
||||
$form[$entity] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $entity_info['label'],
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#group' => 'xmlsitemap_settings',
|
||||
);
|
||||
|
||||
if (!empty($entity_info['bundles'])) {
|
||||
// If this entity has bundles, show a bundle setting summary.
|
||||
xmlsitemap_add_form_entity_summary($form[$entity], $entity, $entity_info);
|
||||
}
|
||||
|
||||
if (!empty($entity_info['xmlsitemap']['settings callback'])) {
|
||||
// Add any entity-specific settings.
|
||||
$entity_info['xmlsitemap']['settings callback']($form[$entity]);
|
||||
}
|
||||
|
||||
// Ensure that the entity fieldset is not shown if there are no accessible
|
||||
// sub-elements.
|
||||
$form[$entity]['#access'] = (bool) element_get_visible_children($form[$entity]);
|
||||
}
|
||||
|
||||
$form['#validate'][] = 'xmlsitemap_settings_form_validate';
|
||||
$form['#submit'][] = 'xmlsitemap_settings_form_submit';
|
||||
array_unshift($form['#submit'], 'xmlsitemap_form_submit_flag_regenerate');
|
||||
$form['array_filter'] = array('#type' => 'value', '#value' => TRUE);
|
||||
|
||||
$form = system_settings_form($form);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form validator; Check the sitemap files directory.
|
||||
*
|
||||
* @see xmlsitemap_settings_form()
|
||||
*/
|
||||
function xmlsitemap_settings_form_validate($form, &$form_state) {
|
||||
// Check that the chunk size will not create more than 1000 chunks.
|
||||
$chunk_size = $form_state['values']['xmlsitemap_chunk_size'];
|
||||
if ($chunk_size != 'auto' && $chunk_size != 50000 && (xmlsitemap_get_link_count() / $chunk_size) > 1000) {
|
||||
form_set_error('xmlsitemap_chunk_size', t('The sitemap page link count of @size will create more than 1,000 sitemap pages. Please increase the link count.', array('@size' => $chunk_size)));
|
||||
}
|
||||
|
||||
$base_url = &$form_state['values']['xmlsitemap_base_url'];
|
||||
$base_url = rtrim($base_url, '/');
|
||||
if ($base_url != '' && !valid_url($base_url, TRUE)) {
|
||||
form_set_error('xmlsitemap_base_url', t('Invalid base URL.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler;
|
||||
*
|
||||
* @see xmlsitemap_settings_form()
|
||||
*/
|
||||
function xmlsitemap_settings_form_submit($form, $form_state) {
|
||||
// Save any changes to the frontpage link.
|
||||
xmlsitemap_link_save(array('type' => 'frontpage', 'id' => 0, 'loc' => ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; Confirm rebuilding of the sitemap.
|
||||
*
|
||||
* @see xmlsitemap_rebuild_form_submit()
|
||||
*/
|
||||
function xmlsitemap_rebuild_form() {
|
||||
if (!$_POST && !variable_get('xmlsitemap_rebuild_needed', FALSE)) {
|
||||
if (!variable_get('xmlsitemap_regenerate_needed', FALSE)) {
|
||||
drupal_set_message(t('Your sitemap is up to date and does not need to be rebuilt.'), 'error');
|
||||
}
|
||||
else {
|
||||
$_REQUEST += array('destination' => 'admin/config/search/xmlsitemap');
|
||||
drupal_set_message(t('A rebuild is not necessary. If you are just wanting to regenerate the XML sitemap files, you can <a href="@link-cron">run cron manually</a>.', array('@link-cron' => url('admin/reports/status/run-cron', array('query' => drupal_get_destination())))), 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
// Build a list of rebuildable link types.
|
||||
module_load_include('generate.inc', 'xmlsitemap');
|
||||
$rebuild_types = xmlsitemap_get_rebuildable_link_types();
|
||||
|
||||
$form['entities'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t("Select which link types you would like to rebuild"),
|
||||
'#description' => t('If no link types are selected, the sitemap files will just be regenerated.'),
|
||||
'#multiple' => TRUE,
|
||||
'#options' => drupal_map_assoc($rebuild_types),
|
||||
'#default_value' => variable_get('xmlsitemap_rebuild_needed', FALSE) || !variable_get('xmlsitemap_developer_mode', 0) ? $rebuild_types : array(),
|
||||
'#access' => variable_get('xmlsitemap_developer_mode', 0),
|
||||
);
|
||||
$form['save_custom'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Save and restore any custom inclusion and priority links.'),
|
||||
'#default_value' => TRUE,
|
||||
);
|
||||
|
||||
return confirm_form(
|
||||
$form,
|
||||
t('Are you sure you want to rebuild the XML sitemap?'),
|
||||
'admin/config/search/xmlsitemap',
|
||||
'',
|
||||
t('Rebuild sitemap'),
|
||||
t('Cancel')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler; Starts the sitemap rebuild batch.
|
||||
*
|
||||
* @see xmlsitemap_rebuild_form()
|
||||
* @see xmlsitemap_rebuild_batch()
|
||||
*/
|
||||
function xmlsitemap_rebuild_form_submit($form, &$form_state) {
|
||||
module_load_include('generate.inc', 'xmlsitemap');
|
||||
$batch = xmlsitemap_rebuild_batch($form_state['values']['entities'], $form_state['values']['save_custom']);
|
||||
batch_set($batch);
|
||||
$form_state['redirect'] = 'admin/config/search/xmlsitemap';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a table summary for an entity and its bundles.
|
||||
*/
|
||||
function xmlsitemap_add_form_entity_summary(&$form, $entity, array $entity_info) {
|
||||
$priorities = xmlsitemap_get_priority_options(NULL, FALSE);
|
||||
$statuses = xmlsitemap_get_status_options(NULL);
|
||||
$destination = drupal_get_destination();
|
||||
|
||||
$rows = array();
|
||||
$totals = array('total' => 0, 'indexed' => 0, 'visible' => 0);
|
||||
foreach ($entity_info['bundles'] as $bundle => $bundle_info) {
|
||||
// Fetch current per-bundle link total and indexed counts.
|
||||
$status = xmlsitemap_get_link_type_indexed_status($entity, $bundle);
|
||||
$totals['total'] += $status['total'];
|
||||
$totals['indexed'] += $status['indexed'];
|
||||
$totals['visible'] += $status['visible'];
|
||||
|
||||
$row = array();
|
||||
if (drupal_valid_path("admin/config/search/xmlsitemap/settings/$entity/$bundle")) {
|
||||
$edit_link = xmlsitemap_get_operation_link("admin/config/search/xmlsitemap/settings/$entity/$bundle", array('title' => $bundle_info['label'], 'modal' => TRUE));
|
||||
$row[] = l($edit_link['title'], $edit_link['href'], $edit_link);
|
||||
}
|
||||
else {
|
||||
// Bundle labels are assumed to be un-escaped input.
|
||||
$row[] = check_plain($bundle_info['label']);
|
||||
}
|
||||
$row[] = $statuses[$bundle_info['xmlsitemap']['status'] ? 1 : 0];
|
||||
$row[] = $priorities[number_format($bundle_info['xmlsitemap']['priority'], 1)];
|
||||
$row[] = $status['total'];
|
||||
$row[] = $status['indexed'];
|
||||
$row[] = $status['visible'];
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
if ($rows) {
|
||||
$header = array(
|
||||
isset($entity_info['bundle label']) ? $entity_info['bundle label'] : '',
|
||||
t('Inclusion'),
|
||||
t('Priority'),
|
||||
t('Available'),
|
||||
t('Indexed'),
|
||||
t('Visible'),
|
||||
);
|
||||
$rows[] = array(
|
||||
array(
|
||||
'data' => t('Totals'),
|
||||
'colspan' => 3,
|
||||
'header' => TRUE,
|
||||
),
|
||||
array(
|
||||
'data' => $totals['total'],
|
||||
'header' => TRUE,
|
||||
),
|
||||
array(
|
||||
'data' => $totals['indexed'],
|
||||
'header' => TRUE,
|
||||
),
|
||||
array(
|
||||
'data' => $totals['visible'],
|
||||
'header' => TRUE,
|
||||
),
|
||||
);
|
||||
$form['summary'] = array(
|
||||
'#theme' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the link type XML sitemap options to the link type's form.
|
||||
*
|
||||
* Caller is responsible for ensuring xmlsitemap_link_bundle_settings_save()
|
||||
* is called during submission.
|
||||
*/
|
||||
function xmlsitemap_add_link_bundle_settings(array &$form, array &$form_state, $entity, $bundle) {
|
||||
$entity_info = xmlsitemap_get_link_info($entity);
|
||||
|
||||
//if (!isset($bundle) && isset($entity_info['bundle keys']['bundle'])) {
|
||||
// $bundle_key = $entity_info['bundle keys']['bundle'];
|
||||
// if (isset($form[$bundle_key]['#value'])) {
|
||||
// $bundle = $form[$bundle_key]['#value'];
|
||||
// }
|
||||
// elseif (isset($form[$bundle_key]['#default_value'])) {
|
||||
// $bundle = $form[$bundle_key]['#default_value'];
|
||||
// }
|
||||
//}
|
||||
|
||||
$bundle_info = xmlsitemap_link_bundle_load($entity, $bundle);
|
||||
|
||||
$form['xmlsitemap'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('XML sitemap'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#access' => user_access('administer xmlsitemap'),
|
||||
'#group' => 'additional_settings',
|
||||
'#attached' => array(
|
||||
'js' => array(
|
||||
'vertical-tabs' => drupal_get_path('module', 'xmlsitemap') . '/xmlsitemap.js',
|
||||
),
|
||||
),
|
||||
'#tree' => TRUE,
|
||||
'#entity' => $entity,
|
||||
'#bundle' => $bundle,
|
||||
'#entity_info' => $entity_info,
|
||||
'#bundle_info' => $bundle_info,
|
||||
);
|
||||
|
||||
// Hack to remove fieldset summary if Vertical tabs is not enabled.
|
||||
if (!isset($form['additional_settings'])) {
|
||||
unset($form['xmlsitemap']['#attached']['js']['vertical-tabs']);
|
||||
}
|
||||
|
||||
$form['xmlsitemap']['description'] = array(
|
||||
'#prefix' => '<div class="description">',
|
||||
'#suffix' => '</div>',
|
||||
'#markup' => t('Changing these type settings will affect any items of this type that have either inclusion or priority set to default.'),
|
||||
);
|
||||
$form['xmlsitemap']['status'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Inclusion'),
|
||||
'#options' => xmlsitemap_get_status_options(),
|
||||
'#default_value' => $bundle_info['status'],
|
||||
);
|
||||
$form['xmlsitemap']['priority'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Default priority'),
|
||||
'#options' => xmlsitemap_get_priority_options(),
|
||||
'#default_value' => $bundle_info['priority'],
|
||||
'#states' => array(
|
||||
'invisible' => array(
|
||||
'select[name="xmlsitemap[status]"]' => array('value' => '0'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$form += array('#submit' => array());
|
||||
array_unshift($form['#submit'], 'xmlsitemap_link_bundle_settings_form_submit');
|
||||
|
||||
if (isset($form['submit'])) {
|
||||
$form['submit'] += array('#weight' => 40);
|
||||
}
|
||||
if (isset($form['delete'])) {
|
||||
$form['delete'] += array('#weight' => 50);
|
||||
}
|
||||
}
|
||||
|
||||
function xmlsitemap_link_bundle_settings_form(array $form, array &$form_state, array $bundle) {
|
||||
if (empty($form_state['ajax']) && $admin_path = xmlsitemap_get_bundle_path($bundle['entity'], $bundle['bundle'])) {
|
||||
// If this is a non-ajax form, redirect to the bundle administration page.
|
||||
$destination = drupal_get_destination();
|
||||
unset($_GET['destination']);
|
||||
drupal_goto($admin_path, array('query' => $destination));
|
||||
}
|
||||
else {
|
||||
drupal_set_title( t('@bundle XML sitemap settings', array('@bundle' => $bundle['info']['label'])));
|
||||
}
|
||||
|
||||
$form = array();
|
||||
xmlsitemap_add_link_bundle_settings($form, $form_state, $bundle['entity'], $bundle['bundle']);
|
||||
$form['xmlsitemap']['#type'] = 'markup';
|
||||
$form['xmlsitemap']['#value'] = '';
|
||||
$form['xmlsitemap']['#access'] = TRUE;
|
||||
$form['xmlsitemap']['#show_message'] = TRUE;
|
||||
|
||||
$form['actions'] = array(
|
||||
'#type' => 'actions',
|
||||
);
|
||||
$form['actions']['save'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save'),
|
||||
);
|
||||
$form['actions']['cancel'] = array(
|
||||
'#value' => l(t('Cancel'), isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/search/xmlsitemap/settings'),
|
||||
'#weight' => 10,
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a link's XML sitemap options to the link's form.
|
||||
*
|
||||
* @todo Add changefreq overridability.
|
||||
*/
|
||||
function xmlsitemap_add_form_link_options(array &$form, $entity, $bundle, $id) {
|
||||
$info = xmlsitemap_get_link_info($entity);
|
||||
|
||||
if (!$info || empty($info['bundles'][$bundle])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$link = xmlsitemap_link_load($entity, $id)) {
|
||||
$link = array();
|
||||
}
|
||||
|
||||
$bundle_info = xmlsitemap_link_bundle_load($entity, $bundle);
|
||||
$link += array(
|
||||
'access' => 1,
|
||||
'status' => $bundle_info['status'],
|
||||
'status_default' => $bundle_info['status'],
|
||||
'status_override' => 0,
|
||||
'priority' => $bundle_info['priority'],
|
||||
'priority_default' => $bundle_info['priority'],
|
||||
'priority_override' => 0,
|
||||
);
|
||||
|
||||
$form['xmlsitemap'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#tree' => TRUE,
|
||||
'#title' => t('XML sitemap'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => !$link['status_override'] && !$link['priority_override'],
|
||||
'#access' => user_access('administer xmlsitemap') || xmlsitemap_link_bundle_access($bundle_info),
|
||||
'#group' => 'additional_settings',
|
||||
'#attached' => array(
|
||||
'js' => array(
|
||||
'vertical-tabs' => drupal_get_path('module', 'xmlsitemap') . '/xmlsitemap.js',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Hack to remove fieldset summary if Vertical tabs is not enabled.
|
||||
if (!isset($form['additional_settings'])) {
|
||||
unset($form['xmlsitemap']['#attached']['js']['vertical-tabs']);
|
||||
}
|
||||
|
||||
if (xmlsitemap_link_bundle_access($bundle_info) && $path = xmlsitemap_get_bundle_path($entity, $bundle)) {
|
||||
$form['xmlsitemap']['description'] = array(
|
||||
'#prefix' => '<div class="description">',
|
||||
'#suffix' => '</div>',
|
||||
'#markup' => t('The default XML sitemap settings for this @bundle can be changed <a href="@link-type">here</a>.', array('@bundle' => drupal_strtolower($info['bundle label']), '@link-type' => url($path, array('query' => drupal_get_destination())))),
|
||||
);
|
||||
}
|
||||
|
||||
// Show a warning if the link is not accessible and will not be included in
|
||||
// the sitemap.
|
||||
if ($id && !$link['access']) {
|
||||
$form['xmlsitemap']['warning'] = array(
|
||||
'#type' => 'markup',
|
||||
'#prefix' => '<p><strong>',
|
||||
'#suffix' => '</strong></p>',
|
||||
'#value' => ('This item is not currently visible to anonymous users, so it will not be included in the sitemap.'),
|
||||
);
|
||||
}
|
||||
|
||||
// Status field (inclusion/exclusion)
|
||||
$form['xmlsitemap']['status'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Inclusion'),
|
||||
'#options' => xmlsitemap_get_status_options($link['status_default']),
|
||||
'#default_value' => $link['status_override'] ? $link['status'] : 'default',
|
||||
);
|
||||
$form['xmlsitemap']['status_default'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $link['status_default'],
|
||||
);
|
||||
$form['xmlsitemap']['status_override'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $link['status_override'],
|
||||
);
|
||||
|
||||
// Priority field
|
||||
$form['xmlsitemap']['priority'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Priority'),
|
||||
'#options' => xmlsitemap_get_priority_options($link['priority_default']),
|
||||
'#default_value' => $link['priority_override'] ? number_format($link['priority'], 1) : 'default',
|
||||
'#description' => t('The priority of this URL relative to other URLs on your site.'),
|
||||
'#states' => array(
|
||||
'invisible' => array(
|
||||
'select[name="xmlsitemap[status]"]' => array('value' => '0'),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (!$link['status_default']) {
|
||||
// If the default status is excluded, add a visible state on the include
|
||||
// override option.
|
||||
$form['xmlsitemap']['priority']['#states']['visible'] = array(
|
||||
'select[name="xmlsitemap[status]"]' => array('value' => '1'),
|
||||
);
|
||||
}
|
||||
$form['xmlsitemap']['priority_default'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $link['priority_default'],
|
||||
);
|
||||
$form['xmlsitemap']['priority_override'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $link['priority_override'],
|
||||
);
|
||||
|
||||
// Other persistent fields.
|
||||
//$form['xmlsitemap']['lastmod'] = array(
|
||||
// '#type' => 'value',
|
||||
// '#value' => $node->xmlsitemap['lastmod'],
|
||||
//);
|
||||
//$form['xmlsitemap']['changefreq'] = array(
|
||||
// '#type' => 'value',
|
||||
// '#value' => $node->xmlsitemap['changefreq'],
|
||||
//);
|
||||
//$form['xmlsitemap']['changecount'] = array(
|
||||
// '#type' => 'value',
|
||||
// '#value' => $node->xmlsitemap['changecount'],
|
||||
//);
|
||||
|
||||
// Add the submit handler to adjust the default values if selected.
|
||||
$form += array('#submit' => array());
|
||||
if (!in_array('xmlsitemap_process_form_link_options', $form['#submit'])) {
|
||||
array_unshift($form['#submit'], 'xmlsitemap_process_form_link_options');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of priority options.
|
||||
*
|
||||
* @param $default
|
||||
* Include a 'default' option.
|
||||
* @param $guides
|
||||
* Add helpful indicators for the highest, middle and lowest values.
|
||||
* @return
|
||||
* An array of options.
|
||||
*/
|
||||
function xmlsitemap_get_priority_options($default = NULL, $guides = TRUE) {
|
||||
$options = array();
|
||||
$priorities = array(
|
||||
'1.0' => t('1.0'),
|
||||
'0.9' => t('0.9'),
|
||||
'0.8' => t('0.8'),
|
||||
'0.7' => t('0.7'),
|
||||
'0.6' => t('0.6'),
|
||||
'0.5' => t('0.5'),
|
||||
'0.4' => t('0.4'),
|
||||
'0.3' => t('0.3'),
|
||||
'0.2' => t('0.2'),
|
||||
'0.1' => t('0.1'),
|
||||
'0.0' => t('0.0'),
|
||||
);
|
||||
|
||||
if (isset($default)) {
|
||||
$default = number_format($default, 1);
|
||||
$options['default'] = t('Default (@value)', array('@value' => $priorities[$default]));
|
||||
}
|
||||
|
||||
// Add the rest of the options.
|
||||
$options += $priorities;
|
||||
|
||||
if ($guides) {
|
||||
$options['1.0'] .= ' ' . t('(highest)');
|
||||
$options['0.5'] .= ' ' . t('(normal)');
|
||||
$options['0.0'] .= ' ' . t('(lowest)');
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of priority options.
|
||||
*
|
||||
* @param $default
|
||||
* Include a 'default' option.
|
||||
* @return
|
||||
* An array of options.
|
||||
*
|
||||
* @see _xmlsitemap_translation_strings()
|
||||
*/
|
||||
function xmlsitemap_get_status_options($default = NULL) {
|
||||
$options = array();
|
||||
$statuses = array(
|
||||
1 => t('Included'),
|
||||
0 => t('Excluded'),
|
||||
);
|
||||
|
||||
if (isset($default)) {
|
||||
$default = $default ? 1 : 0;
|
||||
$options['default'] = t('Default (@value)', array('@value' => drupal_strtolower($statuses[$default])));
|
||||
}
|
||||
|
||||
$options += $statuses;
|
||||
|
||||
return $options;
|
||||
}
|
219
sites/all/modules/xmlsitemap/xmlsitemap.api.php
Normal file
219
sites/all/modules/xmlsitemap/xmlsitemap.api.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the XML sitemap module.
|
||||
*
|
||||
* @ingroup xmlsitemap
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provide information on the type of links this module provides.
|
||||
*
|
||||
* @see hook_entity_info()
|
||||
* @see hook_entity_info_alter()
|
||||
*/
|
||||
function hook_xmlsitemap_link_info() {
|
||||
return array(
|
||||
'mymodule' => array(
|
||||
'label' => 'My module',
|
||||
'base table' => 'mymodule',
|
||||
'entity keys' => array(
|
||||
// Primary ID key on {base table}
|
||||
'id' => 'myid',
|
||||
// Subtype key on {base table}
|
||||
'bundle' => 'mysubtype',
|
||||
),
|
||||
'path callback' => 'mymodule_path',
|
||||
'bundle label' => t('Subtype name'),
|
||||
'bundles' => array(
|
||||
'mysubtype1' => array(
|
||||
'label' => t('My subtype 1'),
|
||||
'admin' => array(
|
||||
'real path' => 'admin/settings/mymodule/mysubtype1/edit',
|
||||
'access arguments' => array('administer mymodule'),
|
||||
),
|
||||
'xmlsitemap' => array(
|
||||
'status' => XMLSITEMAP_STATUS_DEFAULT,
|
||||
'priority' => XMLSITEMAP_PRIORITY_DEFAULT,
|
||||
),
|
||||
),
|
||||
),
|
||||
'xmlsitemap' => array(
|
||||
// Callback function to take an array of IDs and save them as sitemap
|
||||
// links.
|
||||
'process callback' => '',
|
||||
// Callback function used in batch API for rebuilding all links.
|
||||
'rebuild callback' => '',
|
||||
// Callback function called from the XML sitemap settings page.
|
||||
'settings callback' => '',
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the data of a sitemap link before the link is saved.
|
||||
*
|
||||
* @param $link
|
||||
* An array with the data of the sitemap link.
|
||||
*/
|
||||
function hook_xmlsitemap_link_alter(&$link) {
|
||||
if ($link['type'] == 'mymodule') {
|
||||
$link['priority'] += 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform modules that an XML sitemap link has been created.
|
||||
*
|
||||
* @param $link
|
||||
* Associative array defining an XML sitemap link as passed into
|
||||
* xmlsitemap_link_save().
|
||||
*
|
||||
* @see hook_xmlsitemap_link_update()
|
||||
*/
|
||||
function hook_xmlsitemap_link_insert(array $link) {
|
||||
db_insert('mytable')
|
||||
->fields(array(
|
||||
'link_type' => $link['type'],
|
||||
'link_id' => $link['id'],
|
||||
'link_status' => $link['status'],
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform modules that an XML sitemap link has been updated.
|
||||
*
|
||||
* @param $link
|
||||
* Associative array defining an XML sitemap link as passed into
|
||||
* xmlsitemap_link_save().
|
||||
*
|
||||
* @see hook_xmlsitemap_link_insert()
|
||||
*/
|
||||
function hook_xmlsitemap_link_update(array $link) {
|
||||
db_update('mytable')
|
||||
->fields(array(
|
||||
'link_type' => $link['type'],
|
||||
'link_id' => $link['id'],
|
||||
'link_status' => $link['status'],
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Index links for the XML sitemaps.
|
||||
*/
|
||||
function hook_xmlsitemap_index_links($limit) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide information about contexts available to XML sitemap.
|
||||
*
|
||||
* @see hook_xmlsitemap_context_info_alter().
|
||||
*/
|
||||
function hook_xmlsitemap_context_info() {
|
||||
$info['vocabulary'] = array(
|
||||
'label' => t('Vocabulary'),
|
||||
'summary callback' => 'mymodule_xmlsitemap_vocabulary_context_summary',
|
||||
'default' => 0,
|
||||
);
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter XML sitemap context info.
|
||||
*
|
||||
* @see hook_xmlsitemap_context_info().
|
||||
*/
|
||||
function hook_xmlsitemap_context_info_alter(&$info) {
|
||||
$info['vocabulary']['label'] = t('Site vocabularies');
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide information about the current context on the site.
|
||||
*
|
||||
* @see hook_xmlsitemap_context_alter()
|
||||
*/
|
||||
function hook_xmlsitemap_context() {
|
||||
$context = array();
|
||||
if ($vid = mymodule_get_current_vocabulary()) {
|
||||
$context['vocabulary'] = $vid;
|
||||
}
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the current context information.
|
||||
*
|
||||
* @see hook_xmlsitemap_context()
|
||||
*/
|
||||
function hook_xmlsitemap_context_alter(&$context) {
|
||||
if (user_access('administer taxonomy')) {
|
||||
unset($context['vocabulary']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide options for the url() function based on an XML sitemap context.
|
||||
*/
|
||||
function hook_xmlsitemap_context_url_options(array $context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the url() options based on an XML sitemap context.
|
||||
*/
|
||||
function hook_xmlsitemap_context_url_options_alter(array &$options, array $context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the query selecting data from {xmlsitemap} during sitemap generation.
|
||||
*
|
||||
* @param $query
|
||||
* A Query object describing the composite parts of a SQL query.
|
||||
*
|
||||
* @see hook_query_TAG_alter()
|
||||
*/
|
||||
function hook_query_xmlsitemap_generate_alter(QueryAlterableInterface $query) {
|
||||
$sitemap = $query->getMetaData('sitemap');
|
||||
if (!empty($sitemap->context['vocabulary'])) {
|
||||
$node_condition = db_and();
|
||||
$node_condition->condition('type', 'taxonomy_term');
|
||||
$node_condition->condition('subtype', $sitemap->context['vocabulary']);
|
||||
$normal_condition = db_and();
|
||||
$normal_condition->condition('type', 'taxonomy_term', '<>');
|
||||
$condition = db_or();
|
||||
$condition->condition($node_condition);
|
||||
$condition->condition($normal_condition);
|
||||
$query->condition($condition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide information about XML sitemap bulk operations.
|
||||
*/
|
||||
function hook_xmlsitemap_sitemap_operations() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to XML sitemap deletion.
|
||||
*
|
||||
* This hook is invoked from xmlsitemap_sitemap_delete_multiple() after the XML
|
||||
* sitemap has been removed from the table in the database.
|
||||
*
|
||||
* @param $sitemap
|
||||
* The XML sitemap object that was deleted.
|
||||
*/
|
||||
function hook_xmlsitemap_sitemap_delete(stdClass $sitemap) {
|
||||
db_query("DELETE FROM {mytable} WHERE smid = '%s'", $sitemap->smid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
86
sites/all/modules/xmlsitemap/xmlsitemap.drush.inc
Normal file
86
sites/all/modules/xmlsitemap/xmlsitemap.drush.inc
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Drush integration functions for the xmlsitemap module.
|
||||
*
|
||||
* @ingroup xmlsitemap
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_drush_command().
|
||||
*/
|
||||
function xmlsitemap_drush_command() {
|
||||
$items['xmlsitemap-regenerate'] = array(
|
||||
'description' => 'Regenerate the XML sitemap files.',
|
||||
'callback' => 'drush_xmlsitemap_regenerate',
|
||||
'drupal dependencies' => array('xmlsitemap'),
|
||||
);
|
||||
$items['xmlsitemap-rebuild'] = array(
|
||||
'description' => 'Dump and re-process all possible XML sitemap data, and then regenerate the files.',
|
||||
'callback' => 'drush_xmlsitemap_rebuild',
|
||||
'drupal dependencies' => array('xmlsitemap'),
|
||||
);
|
||||
$items['xmlsitemap-index'] = array(
|
||||
'description' => 'Process un-indexed XML sitemap links.',
|
||||
'callback' => 'drush_xmlsitemap_index',
|
||||
'drupal dependencies' => array('xmlsitemap'),
|
||||
'options' => array(
|
||||
'--limit' => 'The limit of links of each type to process. Default value: ' . variable_get('xmlsitemap_batch_limit', 100),
|
||||
),
|
||||
);
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate the sitemap files from existing data.
|
||||
*/
|
||||
function drush_xmlsitemap_regenerate() {
|
||||
module_load_include('generate.inc', 'xmlsitemap');
|
||||
|
||||
// Run the batch process.
|
||||
xmlsitemap_run_unprogressive_batch('xmlsitemap_regenerate_batch');
|
||||
|
||||
$vars = array(
|
||||
'@timer' => timer_read('xmlsitemap_regenerate'),
|
||||
'@memory-peak' => format_size(memory_get_peak_usage(TRUE)),
|
||||
);
|
||||
drush_print(dt('XML sitemap files regenerated in @timer ms. Peak memory usage: @memory-peak.', $vars));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump and rebuild all the sitemap data, then regenerate the files.
|
||||
*/
|
||||
function drush_xmlsitemap_rebuild() {
|
||||
module_load_include('generate.inc', 'xmlsitemap');
|
||||
|
||||
// Build a list of rebuildable link types.
|
||||
$rebuild_types = xmlsitemap_get_rebuildable_link_types();
|
||||
|
||||
// Run the batch process.
|
||||
xmlsitemap_run_unprogressive_batch('xmlsitemap_rebuild_batch', $rebuild_types, TRUE);
|
||||
|
||||
$vars = array(
|
||||
'@timer' => timer_read('xmlsitemap_rebuild'),
|
||||
'@memory-peak' => format_size(memory_get_peak_usage(TRUE)),
|
||||
);
|
||||
drush_print(dt('XML sitemap files rebuilt in @timer ms. Peak memory usage: @memory-peak.', $vars));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process un-indexed XML sitemap links.
|
||||
*/
|
||||
function drush_xmlsitemap_index() {
|
||||
$limit = (int) drush_get_option('limit', variable_get('xmlsitemap_batch_limit', 100));
|
||||
$count_before = db_query("SELECT COUNT(id) FROM {xmlsitemap}")->fetchField();
|
||||
|
||||
module_invoke_all('xmlsitemap_index_links', $limit);
|
||||
$count_after = db_query("SELECT COUNT(id) FROM {xmlsitemap}")->fetchField();
|
||||
|
||||
if ($count_after == $count_before) {
|
||||
drush_print(dt('No new XML sitemap links to index.'));
|
||||
}
|
||||
else {
|
||||
drush_print(dt('Indexed @count new XML sitemap links.', array('@count' => $count_after - $count_before)));
|
||||
}
|
||||
}
|
501
sites/all/modules/xmlsitemap/xmlsitemap.generate.inc
Normal file
501
sites/all/modules/xmlsitemap/xmlsitemap.generate.inc
Normal file
@@ -0,0 +1,501 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sitemap generation and rebuilding functions for the xmlsitemap module.
|
||||
*
|
||||
* @ingroup xmlsitemap
|
||||
*/
|
||||
|
||||
/**
|
||||
* Given an internal Drupal path, return the alias for the path.
|
||||
*
|
||||
* This is similar to drupal_get_path_alias(), but designed to fetch all alises
|
||||
* at once so that only one database query is executed instead of several or
|
||||
* possibly thousands during sitemap generation.
|
||||
*
|
||||
* @param $path
|
||||
* An internal Drupal path.
|
||||
* @param $language
|
||||
* A language code to use when looking up the paths.
|
||||
*/
|
||||
function xmlsitemap_get_path_alias($path, $language) {
|
||||
static $aliases;
|
||||
static $last_language;
|
||||
|
||||
if (!isset($aliases)) {
|
||||
$aliases[LANGUAGE_NONE] = db_query("SELECT source, alias FROM {url_alias} WHERE language = :language ORDER BY pid", array(':language' => LANGUAGE_NONE))->fetchAllKeyed();
|
||||
}
|
||||
if ($language != LANGUAGE_NONE && $last_language != $language) {
|
||||
unset($aliases[$last_language]);
|
||||
$aliases[$language] = db_query("SELECT source, alias FROM {url_alias} WHERE language = :language ORDER BY pid", array(':language' => $language))->fetchAllKeyed();
|
||||
$last_language = $language;
|
||||
}
|
||||
|
||||
if ($language != LANGUAGE_NONE && isset($aliases[$language][$path])) {
|
||||
return $aliases[$language][$path];
|
||||
}
|
||||
elseif (isset($aliases[LANGUAGE_NONE][$path])) {
|
||||
return $aliases[LANGUAGE_NONE][$path];
|
||||
}
|
||||
else {
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform operations before rebuilding the sitemap.
|
||||
*/
|
||||
function _xmlsitemap_regenerate_before() {
|
||||
// Attempt to increase the memory limit.
|
||||
_xmlsitemap_set_memory_limit();
|
||||
|
||||
if (variable_get('xmlsitemap_developer_mode', 0)) {
|
||||
watchdog('xmlsitemap', 'Starting XML sitemap generation. Memory usage: @memory-peak.', array(
|
||||
'@memory-peak' => format_size(memory_get_peak_usage(TRUE)),
|
||||
),
|
||||
WATCHDOG_DEBUG
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function _xmlsitemap_get_memory_usage($start = FALSE) {
|
||||
static $memory_start;
|
||||
$current = memory_get_peak_usage(TRUE);
|
||||
if (!isset($memory_start) || $start) {
|
||||
$memory_start = $current;
|
||||
}
|
||||
return $current - $memory_start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the optimal PHP memory limit for sitemap generation.
|
||||
*
|
||||
* This function just makes a guess. It does not take into account
|
||||
* the currently loaded modules.
|
||||
*/
|
||||
function _xmlsitemap_get_optimal_memory_limit() {
|
||||
$optimal_limit = &drupal_static(__FUNCTION__);
|
||||
if (!isset($optimal_limit)) {
|
||||
// Set the base memory amount from the provided core constant.
|
||||
$optimal_limit = parse_size(DRUPAL_MINIMUM_PHP_MEMORY_LIMIT);
|
||||
|
||||
// Add memory based on the chunk size.
|
||||
$optimal_limit += xmlsitemap_get_chunk_size() * 500;
|
||||
|
||||
// Add memory for storing the url aliases.
|
||||
if (variable_get('xmlsitemap_prefetch_aliases', 1)) {
|
||||
$aliases = db_query("SELECT COUNT(pid) FROM {url_alias}")->fetchField();
|
||||
$optimal_limit += $aliases * 250;
|
||||
}
|
||||
}
|
||||
return $optimal_limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the optimal memory level for sitemap generation.
|
||||
*
|
||||
* @param $new_limit
|
||||
* An optional PHP memory limit in bytes. If not provided, the value of
|
||||
* _xmlsitemap_get_optimal_memory_limit() will be used.
|
||||
*/
|
||||
function _xmlsitemap_set_memory_limit($new_limit = NULL) {
|
||||
$current_limit = @ini_get('memory_limit');
|
||||
if ($current_limit && $current_limit != -1) {
|
||||
if (!is_null($new_limit)) {
|
||||
$new_limit = _xmlsitemap_get_optimal_memory_limit();
|
||||
}
|
||||
if (parse_size($current_limit) < $new_limit) {
|
||||
return @ini_set('memory_limit', $new_limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate one page (chunk) of the sitemap.
|
||||
*
|
||||
* @param $sitemap
|
||||
* An unserialized data array for an XML sitemap.
|
||||
* @param $page
|
||||
* An integer of the specific page of the sitemap to generate.
|
||||
*/
|
||||
function xmlsitemap_generate_page(stdClass $sitemap, $page) {
|
||||
try {
|
||||
$writer = new XMLSitemapWriter($sitemap, $page);
|
||||
$writer->startDocument();
|
||||
$writer->generateXML();
|
||||
$writer->endDocument();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
watchdog_exception('xmlsitemap', $e);
|
||||
throw $e;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return $writer->getSitemapElementCount();
|
||||
}
|
||||
|
||||
function xmlsitemap_generate_chunk(stdClass $sitemap, XMLSitemapWriter $writer, $chunk) {
|
||||
$lastmod_format = variable_get('xmlsitemap_lastmod_format', XMLSITEMAP_LASTMOD_MEDIUM);
|
||||
|
||||
$url_options = $sitemap->uri['options'];
|
||||
$url_options += array(
|
||||
'absolute' => TRUE,
|
||||
'base_url' => variable_get('xmlsitemap_base_url', $GLOBALS['base_url']),
|
||||
'language' => language_default(),
|
||||
'alias' => variable_get('xmlsitemap_prefetch_aliases', TRUE),
|
||||
);
|
||||
|
||||
$last_url = '';
|
||||
$link_count = 0;
|
||||
|
||||
$query = db_select('xmlsitemap', 'x');
|
||||
$query->fields('x', array('loc', 'lastmod', 'changefreq', 'changecount', 'priority', 'language', 'access', 'status'));
|
||||
$query->condition('x.access', 1);
|
||||
$query->condition('x.status', 1);
|
||||
$query->orderBy('x.language', 'DESC');
|
||||
$query->orderBy('x.loc');
|
||||
$query->addTag('xmlsitemap_generate');
|
||||
$query->addMetaData('sitemap', $sitemap);
|
||||
|
||||
$offset = max($chunk - 1, 0) * xmlsitemap_get_chunk_size();
|
||||
$limit = xmlsitemap_get_chunk_size();
|
||||
$query->range($offset, $limit);
|
||||
$links = $query->execute();
|
||||
|
||||
while ($link = $links->fetchAssoc()) {
|
||||
$link['language'] = $link['language'] != LANGUAGE_NONE ? xmlsitemap_language_load($link['language']) : $url_options['language'];
|
||||
if ($url_options['alias']) {
|
||||
$link['loc'] = xmlsitemap_get_path_alias($link['loc'], $link['language']->language);
|
||||
}
|
||||
$link_options = array(
|
||||
'language' => $link['language'],
|
||||
'xmlsitemap_link' => $link,
|
||||
'xmlsitemap_sitemap' => $sitemap,
|
||||
);
|
||||
// @todo Add a separate hook_xmlsitemap_link_url_alter() here?
|
||||
$link_url = url($link['loc'], $link_options + $url_options);
|
||||
|
||||
// Skip this link if it was a duplicate of the last one.
|
||||
// @todo Figure out a way to do this before generation so we can report
|
||||
// back to the user about this.
|
||||
if ($link_url == $last_url) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
$last_url = $link_url;
|
||||
// Keep track of the total number of links written.
|
||||
$link_count++;
|
||||
}
|
||||
|
||||
$element = array();
|
||||
$element['loc'] = $link_url;
|
||||
if ($link['lastmod']) {
|
||||
$element['lastmod'] = gmdate($lastmod_format, $link['lastmod']);
|
||||
// If the link has a lastmod value, update the changefreq so that links
|
||||
// with a short changefreq but updated two years ago show decay.
|
||||
// We use abs() here just incase items were created on this same cron run
|
||||
// because lastmod would be greater than REQUEST_TIME.
|
||||
$link['changefreq'] = (abs(REQUEST_TIME - $link['lastmod']) + $link['changefreq']) / 2;
|
||||
}
|
||||
if ($link['changefreq']) {
|
||||
$element['changefreq'] = xmlsitemap_get_changefreq($link['changefreq']);
|
||||
}
|
||||
if (isset($link['priority']) && $link['priority'] != 0.5) {
|
||||
// Don't output the priority value for links that have 0.5 priority. This
|
||||
// is the default 'assumed' value if priority is not included as per the
|
||||
// sitemaps.org specification.
|
||||
$element['priority'] = number_format($link['priority'], 1);
|
||||
}
|
||||
$writer->writeSitemapElement('url', $element);
|
||||
}
|
||||
|
||||
return $link_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the index sitemap.
|
||||
*
|
||||
* @param $sitemap
|
||||
* An unserialized data array for an XML sitemap.
|
||||
*/
|
||||
function xmlsitemap_generate_index(stdClass $sitemap) {
|
||||
try {
|
||||
$writer = new XMLSitemapIndexWriter($sitemap);
|
||||
$writer->startDocument();
|
||||
$writer->generateXML();
|
||||
$writer->endDocument();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
watchdog_exception('xmlsitemap', $e);
|
||||
throw $e;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return $writer->getSitemapElementCount();
|
||||
}
|
||||
|
||||
// BATCH OPERATIONS ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Batch information callback for regenerating the sitemap files.
|
||||
*
|
||||
* @param $smids
|
||||
* An optional array of XML sitemap IDs. If not provided, it will load all
|
||||
* existing XML sitemaps.
|
||||
*/
|
||||
function xmlsitemap_regenerate_batch(array $smids = array()) {
|
||||
if (empty($smids)) {
|
||||
$smids = db_query("SELECT smid FROM {xmlsitemap_sitemap}")->fetchCol();
|
||||
}
|
||||
|
||||
//$t = get_t();
|
||||
$batch = array(
|
||||
'operations' => array(),
|
||||
//'error_message' => $t('An error has occurred.'),
|
||||
'finished' => 'xmlsitemap_regenerate_batch_finished',
|
||||
'title' => t('Regenerating Sitemap'),
|
||||
'file' => drupal_get_path('module', 'xmlsitemap') . '/xmlsitemap.generate.inc',
|
||||
);
|
||||
|
||||
// Set the regenerate flag in case something fails during file generation.
|
||||
$batch['operations'][] = array('xmlsitemap_batch_variable_set', array(array('xmlsitemap_regenerate_needed' => TRUE)));
|
||||
|
||||
// @todo Get rid of this batch operation.
|
||||
$batch['operations'][] = array('_xmlsitemap_regenerate_before', array());
|
||||
|
||||
// Generate all the sitemap pages for each context.
|
||||
foreach ($smids as $smid) {
|
||||
$batch['operations'][] = array('xmlsitemap_regenerate_batch_generate', array($smid));
|
||||
$batch['operations'][] = array('xmlsitemap_regenerate_batch_generate_index', array($smid));
|
||||
}
|
||||
|
||||
// Clear the regeneration flag.
|
||||
$batch['operations'][] = array('xmlsitemap_batch_variable_set', array(array('xmlsitemap_regenerate_needed' => FALSE)));
|
||||
|
||||
return $batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch callback; generate all pages of a sitemap.
|
||||
*/
|
||||
function xmlsitemap_regenerate_batch_generate($smid, array &$context) {
|
||||
if (!isset($context['sandbox']['sitemap'])) {
|
||||
$context['sandbox']['sitemap'] = xmlsitemap_sitemap_load($smid);
|
||||
$context['sandbox']['sitemap']->chunks = 1;
|
||||
$context['sandbox']['sitemap']->links = 0;
|
||||
$context['sandbox']['max'] = XMLSITEMAP_MAX_SITEMAP_LINKS;
|
||||
|
||||
// Clear the cache directory for this sitemap before generating any files.
|
||||
xmlsitemap_check_directory($context['sandbox']['sitemap']);
|
||||
xmlsitemap_clear_directory($context['sandbox']['sitemap']);
|
||||
}
|
||||
|
||||
$sitemap = &$context['sandbox']['sitemap'];
|
||||
$links = xmlsitemap_generate_page($sitemap, $sitemap->chunks);
|
||||
$context['message'] = t('Now generating %sitemap-url.', array('%sitemap-url' => url('sitemap.xml', $sitemap->uri['options'] + array('query' => array('page' => $sitemap->chunks)))));
|
||||
|
||||
if ($links) {
|
||||
$sitemap->links += $links;
|
||||
$sitemap->chunks++;
|
||||
}
|
||||
else {
|
||||
// Cleanup the 'extra' empty file.
|
||||
$file = xmlsitemap_sitemap_get_file($sitemap, $sitemap->chunks);
|
||||
if (file_exists($file) && $sitemap->chunks > 1) {
|
||||
file_unmanaged_delete($file);
|
||||
}
|
||||
$sitemap->chunks--;
|
||||
|
||||
// Save the updated chunks and links values.
|
||||
$context['sandbox']['max'] = $sitemap->chunks;
|
||||
$sitemap->updated = REQUEST_TIME;
|
||||
xmlsitemap_sitemap_get_max_filesize($sitemap);
|
||||
xmlsitemap_sitemap_save($sitemap);
|
||||
}
|
||||
|
||||
if ($sitemap->chunks != $context['sandbox']['max']) {
|
||||
$context['finished'] = $sitemap->chunks / $context['sandbox']['max'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch callback; generate the index page of a sitemap.
|
||||
*/
|
||||
function xmlsitemap_regenerate_batch_generate_index($smid, array &$context) {
|
||||
$sitemap = xmlsitemap_sitemap_load($smid);
|
||||
if ($sitemap->chunks > 1) {
|
||||
xmlsitemap_generate_index($sitemap);
|
||||
$context['message'] = t('Now generating sitemap index %sitemap-url.', array('%sitemap-url' => url('sitemap.xml', $sitemap->uri['options'])));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch callback; sitemap regeneration finished.
|
||||
*/
|
||||
function xmlsitemap_regenerate_batch_finished($success, $results, $operations, $elapsed) {
|
||||
if ($success && !variable_get('xmlsitemap_regenerate_needed', FALSE)) {
|
||||
variable_set('xmlsitemap_generated_last', REQUEST_TIME);
|
||||
//drupal_set_message(t('The sitemaps were regenerated.'));
|
||||
// Show a watchdog message that the sitemap was regenerated.
|
||||
watchdog('xmlsitemap',
|
||||
'Finished XML sitemap generation in @elapsed. Memory usage: @memory-peak.',
|
||||
array(
|
||||
'@elapsed' => $elapsed,
|
||||
'@memory-peak' => format_size(memory_get_peak_usage(TRUE)),
|
||||
),
|
||||
WATCHDOG_NOTICE
|
||||
);
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('The sitemaps were not successfully regenerated.'), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch information callback for rebuilding the sitemap data.
|
||||
*/
|
||||
function xmlsitemap_rebuild_batch(array $entities, $save_custom = FALSE) {
|
||||
$batch = array(
|
||||
'operations' => array(),
|
||||
'finished' => 'xmlsitemap_rebuild_batch_finished',
|
||||
'title' => t('Rebuilding Sitemap'),
|
||||
'file' => drupal_get_path('module', 'xmlsitemap') . '/xmlsitemap.generate.inc',
|
||||
);
|
||||
|
||||
// Set the rebuild flag in case something fails during the rebuild.
|
||||
$batch['operations'][] = array('xmlsitemap_batch_variable_set', array(array('xmlsitemap_rebuild_needed' => TRUE)));
|
||||
|
||||
// Purge any links first.
|
||||
$batch['operations'][] = array('xmlsitemap_rebuild_batch_clear', array($entities, (bool) $save_custom));
|
||||
|
||||
// Fetch all the sitemap links and save them to the {xmlsitemap} table.
|
||||
foreach ($entities as $entity) {
|
||||
$info = xmlsitemap_get_link_info($entity);
|
||||
$batch['operations'][] = array($info['xmlsitemap']['rebuild callback'], array($entity));
|
||||
}
|
||||
|
||||
// Clear the rebuild flag.
|
||||
$batch['operations'][] = array('xmlsitemap_batch_variable_set', array(array('xmlsitemap_rebuild_needed' => FALSE)));
|
||||
|
||||
// Add the regeneration batch.
|
||||
$regenerate_batch = xmlsitemap_regenerate_batch();
|
||||
$batch['operations'] = array_merge($batch['operations'], $regenerate_batch['operations']);
|
||||
|
||||
return $batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch callback; set an array of variables and their values.
|
||||
*/
|
||||
function xmlsitemap_batch_variable_set(array $variables) {
|
||||
foreach ($variables as $variable => $value) {
|
||||
variable_set($variable, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch callback; clear sitemap links for entites.
|
||||
*/
|
||||
function xmlsitemap_rebuild_batch_clear(array $entities, $save_custom, &$context) {
|
||||
if (!empty($entities)) {
|
||||
$query = db_delete('xmlsitemap');
|
||||
$query->condition('type', $entities);
|
||||
|
||||
// If we want to save the custom data, make sure to exclude any links
|
||||
// that are not using default inclusion or priority.
|
||||
if ($save_custom) {
|
||||
$query->condition('status_override', 0);
|
||||
$query->condition('priority_override', 0);
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
$context['message'] = t('Purging links.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch callback; fetch and add the sitemap links for a specific entity.
|
||||
*/
|
||||
function xmlsitemap_rebuild_batch_fetch($entity, &$context) {
|
||||
if (!isset($context['sandbox']['info'])) {
|
||||
$context['sandbox']['info'] = xmlsitemap_get_link_info($entity);
|
||||
$context['sandbox']['progress'] = 0;
|
||||
$context['sandbox']['last_id'] = 0;
|
||||
}
|
||||
$info = $context['sandbox']['info'];
|
||||
|
||||
$query = new EntityFieldQuery();
|
||||
$query->entityCondition('entity_type', $entity);
|
||||
$query->entityCondition('entity_id', $context['sandbox']['last_id'], '>');
|
||||
$query->addTag('xmlsitemap_link_bundle_access');
|
||||
$query->addTag('xmlsitemap_rebuild');
|
||||
$query->addMetaData('entity', $entity);
|
||||
$query->addMetaData('entity_info', $info);
|
||||
|
||||
if (!isset($context['sandbox']['max'])) {
|
||||
$count_query = clone $query;
|
||||
$count_query->count();
|
||||
$context['sandbox']['max'] = $count_query->execute();
|
||||
if (!$context['sandbox']['max']) {
|
||||
// If there are no items to process, skip everything else.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// PostgreSQL cannot have the ORDERED BY in the count query.
|
||||
$query->entityOrderBy('entity_id');
|
||||
|
||||
$limit = 20; //variable_get('xmlsitemap_batch_limit', 100)
|
||||
$query->range(0, $limit);
|
||||
|
||||
$result = $query->execute();
|
||||
$ids = array_keys($result[$entity]);
|
||||
|
||||
$info['xmlsitemap']['process callback']($ids);
|
||||
$context['sandbox']['last_id'] = end($ids);
|
||||
$context['sandbox']['progress'] += count($ids);
|
||||
$context['message'] = t('Now processing %entity @last_id (@progress of @count).', array('%entity' => $entity, '@last_id' => $context['sandbox']['last_id'], '@progress' => $context['sandbox']['progress'], '@count' => $context['sandbox']['max']));
|
||||
|
||||
if ($context['sandbox']['progress'] >= $context['sandbox']['max']) {
|
||||
$context['finished'] = 1;
|
||||
}
|
||||
else {
|
||||
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch callback; sitemap rebuild finished.
|
||||
*/
|
||||
function xmlsitemap_rebuild_batch_finished($success, $results, $operations, $elapsed) {
|
||||
if ($success && !variable_get('xmlsitemap_rebuild_needed', FALSE)) {
|
||||
drupal_set_message(t('The sitemap links were rebuilt.'));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('The sitemap links were not successfully rebuilt.'), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function xmlsitemap_get_rebuildable_link_types() {
|
||||
$rebuild_types = array();
|
||||
$entities = xmlsitemap_get_link_info();
|
||||
|
||||
foreach ($entities as $entity => $info) {
|
||||
if (empty($info['xmlsitemap']['rebuild callback'])) {
|
||||
// If the entity is missing a rebuild callback, skip.
|
||||
continue;
|
||||
}
|
||||
if (!empty($info['entity keys']['bundle']) && !xmlsitemap_get_link_type_enabled_bundles($entity)) {
|
||||
// If the entity has bundles, but no enabled bundles, skip since
|
||||
// rebuilding wouldn't get any links.
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
$rebuild_types[] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
return $rebuild_types;
|
||||
}
|
89
sites/all/modules/xmlsitemap/xmlsitemap.inc
Normal file
89
sites/all/modules/xmlsitemap/xmlsitemap.inc
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Miscellaneous functions for the xmlsitemap module.
|
||||
*
|
||||
* @ingroup xmlsitemap
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fetch a short blurb string about module maintainership and sponsors.
|
||||
*
|
||||
* This message will be FALSE in 'official' releases.
|
||||
*/
|
||||
function _xmlsitemap_get_blurb($check_version = TRUE) {
|
||||
static $blurb;
|
||||
|
||||
if (!isset($blurb)) {
|
||||
$blurb = FALSE;
|
||||
if (!$check_version || (($version = _xmlsitemap_get_version()) && preg_match('/dev|unstable|alpha|beta|HEAD/i', $version))) {
|
||||
$sponsors = array(
|
||||
l('Symantec', 'http://www.symantec.com/'),
|
||||
l('WebWise Solutions', 'http://www.webwiseone.com/'),
|
||||
l('Volacci', 'http://www.volacci.com/'),
|
||||
l('lanetro', 'http://www.lanetro.com/'),
|
||||
l('Coupons Dealuxe', 'http://couponsdealuxe.com/'),
|
||||
);
|
||||
// Don't extract the following string for translation.
|
||||
$blurb = '<div class="description"><p>Thank you for helping test the XML sitemap module rewrite. Please consider helping offset developer free time by <a href="http://davereid.chipin.com/">donating</a> or if your company is interested in sponsoring the rewrite or a specific feature, please <a href="http://davereid.net/contact">contact the developer</a>. Thank you to the following current sponsors: ' . implode(', ', $sponsors) . ', and all the individuals that have donated. This message will not be seen in the stable versions.</p></div>';
|
||||
//http://drupalmodules.com/module/xml-sitemap
|
||||
}
|
||||
}
|
||||
|
||||
return $blurb;
|
||||
}
|
||||
|
||||
function _xmlsitemap_get_version() {
|
||||
static $version;
|
||||
if (!isset($version)) {
|
||||
$modules = _system_rebuild_module_data();
|
||||
$version = $modules['xmlsitemap']->info['version'];
|
||||
}
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the status of all hook_requirements() from any xmlsitemap modules.
|
||||
*/
|
||||
function xmlsitemap_check_status() {
|
||||
$messages = &drupal_static(__FUNCTION__);
|
||||
|
||||
if (!isset($messages)) {
|
||||
// Cache the list of modules that are checked.
|
||||
if ($cache = cache_get('xmlsitemap:registry:requirements')) {
|
||||
$modules = $cache->data;
|
||||
}
|
||||
else {
|
||||
$modules = array();
|
||||
module_load_all_includes('install');
|
||||
foreach (module_implements('requirements') as $module) {
|
||||
if (strpos($module, 'xmlsitemap') !== FALSE) {
|
||||
$modules[] = $module;
|
||||
}
|
||||
}
|
||||
cache_set('xmlsitemap:registry:requirements', $modules);
|
||||
}
|
||||
|
||||
$messages = array();
|
||||
foreach ($modules as $module) {
|
||||
module_load_install($module);
|
||||
$requirements = module_invoke($module, 'requirements', 'runtime');
|
||||
foreach ($requirements as $requirement) {
|
||||
if (isset($requirement['severity']) && max(REQUIREMENT_OK, $requirement['severity'])) {
|
||||
$messages[] = $requirement['description'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($messages) {
|
||||
$message = t('One or more problems were detected with your XML sitemap configuration: !messages', array('!messages' => theme('item_list', array('items' => $messages))));
|
||||
if (user_access('access site reports')) {
|
||||
$message .= t('Check the <a href="@status-report">status report</a> for more information.', array('@status-report' => url('admin/reports/status')));
|
||||
}
|
||||
drupal_set_message($message, 'warning', FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
return !empty($messages);
|
||||
}
|
22
sites/all/modules/xmlsitemap/xmlsitemap.info
Normal file
22
sites/all/modules/xmlsitemap/xmlsitemap.info
Normal file
@@ -0,0 +1,22 @@
|
||||
name = XML sitemap
|
||||
description = Creates an XML sitemap conforming to the <a href="http://sitemaps.org/">sitemaps.org protocol</a>.
|
||||
package = XML sitemap
|
||||
core = 7.x
|
||||
files[] = xmlsitemap.module
|
||||
files[] = xmlsitemap.inc
|
||||
files[] = xmlsitemap.admin.inc
|
||||
files[] = xmlsitemap.drush.inc
|
||||
files[] = xmlsitemap.generate.inc
|
||||
files[] = xmlsitemap.xmlsitemap.inc
|
||||
files[] = xmlsitemap.pages.inc
|
||||
files[] = xmlsitemap.install
|
||||
files[] = xmlsitemap.test
|
||||
recommends[] = robotstxt
|
||||
configure = admin/config/search/xmlsitemap
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-08
|
||||
version = "7.x-2.0-rc2+0-dev"
|
||||
core = "7.x"
|
||||
project = "xmlsitemap"
|
||||
datestamp = "1354931808"
|
||||
|
560
sites/all/modules/xmlsitemap/xmlsitemap.install
Normal file
560
sites/all/modules/xmlsitemap/xmlsitemap.install
Normal file
@@ -0,0 +1,560 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the xmlsitemap module.
|
||||
*
|
||||
* @ingroup xmlsitemap
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_requirements().
|
||||
*/
|
||||
function xmlsitemap_requirements($phase) {
|
||||
$requirements = array();
|
||||
$t = get_t();
|
||||
|
||||
// Check that required PHP extensions are enabled.
|
||||
// Note: Drupal 7 already requires the 'xml' extension.
|
||||
$required_extensions = array('xmlwriter');
|
||||
$missing_extensions = array_diff($required_extensions, array_filter($required_extensions, 'extension_loaded'));
|
||||
|
||||
if (!empty($missing_extensions)) {
|
||||
$requirements['xmlsitemap_php_extensions'] = array(
|
||||
'title' => $t('XML sitemap PHP extensions'),
|
||||
'value' => $t('Disabled'),
|
||||
'severity' => REQUIREMENT_ERROR,
|
||||
'description' => $t("The XML sitemap module requires you to enable the PHP extensions in the following list (see the <a href=\"@xmlsitemap_requirements\">module's system requirements page</a> for more information):", array(
|
||||
'@xmlsitemap_requirements' => 'http://drupal.org/documentation/modules/xmlsitemap/requirements',
|
||||
)) . theme('item_list', array('items' => $missing_extensions)),
|
||||
);
|
||||
}
|
||||
|
||||
if ($phase == 'runtime') {
|
||||
// If clean URLs are disabled there must not be an actual sitemap.xml in
|
||||
// the root directory.
|
||||
if (variable_get('clean_url', 0) && file_exists(DRUPAL_ROOT . '/sitemap.xml')) {
|
||||
$requirements['xmlsitemap_file'] = array(
|
||||
'title' => $t('XML sitemap'),
|
||||
'value' => $t('Existing sitemap.xml file found.'),
|
||||
'severity' => REQUIREMENT_ERROR,
|
||||
'description' => $t('The XML sitemap module cannot display its XML output if there is an existing sitemap.xml file in your website root.'),
|
||||
);
|
||||
}
|
||||
|
||||
// Check that the base directory and all its subdirectories are writable.
|
||||
$requirements['xmlsitemap_directory'] = array(
|
||||
'title' => $t('XML sitemap cache directory'),
|
||||
'value' => $t('Writable'),
|
||||
);
|
||||
if (!xmlsitemap_check_directory()) {
|
||||
$requirements['xmlsitemap_directory']['value'] = $t('Not found or not writable');
|
||||
$requirements['xmlsitemap_directory']['severity'] = REQUIREMENT_ERROR;
|
||||
$requirements['xmlsitemap_directory']['description'] = $t('The directory %directory was not found or is not writable by the server. See <a href="@docpage">@docpage</a> for more information.', array('%directory' => xmlsitemap_get_directory(), '@docpage' => 'http://drupal.org/node/34025'));
|
||||
}
|
||||
else {
|
||||
$directories = xmlsitemap_check_all_directories();
|
||||
foreach ($directories as $directory => $writable) {
|
||||
if ($writable) {
|
||||
unset($directories[$directory]);
|
||||
}
|
||||
}
|
||||
if (!empty($directories)) {
|
||||
$requirements['xmlsitemap_directory']['value'] = $t('Not found or not writable');
|
||||
$requirements['xmlsitemap_directory']['severity'] = REQUIREMENT_ERROR;
|
||||
$requirements['xmlsitemap_directory']['description'] = $t('The following directories were not found or are not writable by the server. See <a href="@docpage">@docpage</a> for more information. !directories', array('!directories' => theme('item_list', array('items' => array_keys($directories))), '@docpage' => 'http://drupal.org/node/34025'));
|
||||
}
|
||||
}
|
||||
|
||||
// The maximum number of links in a sitemap.
|
||||
$max_links = db_query("SELECT MAX(links) FROM {xmlsitemap_sitemap}")->fetchField();
|
||||
$max_links_limit = XMLSITEMAP_MAX_SITEMAP_LINKS * XMLSITEMAP_MAX_SITEMAP_LINKS;
|
||||
if ($max_links > $max_links_limit) {
|
||||
$requirements['xmlsitemap_link_count'] = array(
|
||||
'title' => $t('XML sitemap link count'),
|
||||
'value' => $max_links,
|
||||
'description' => $t('You have exceeded the number of links that your sitemap can contain (@num).', array('@num' => number_format($max_links))),
|
||||
'severity' => REQUIREMENT_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
// The maximum number of chunks in a sitemap.
|
||||
$max_chunks = db_query("SELECT MAX(chunks) FROM {xmlsitemap_sitemap}")->fetchField();
|
||||
if ($max_chunks > XMLSITEMAP_MAX_SITEMAP_LINKS) {
|
||||
$requirements['xmlsitemap_chunk_count'] = array(
|
||||
'title' => $t('XML sitemap page count'),
|
||||
'value' => $max_chunks,
|
||||
'description' => $t('You have exceeded the number of sitemap pages (@number).', array('@number' => number_format(XMLSITEMAP_MAX_SITEMAP_LINKS))),
|
||||
'severity' => REQUIREMENT_ERROR,
|
||||
);
|
||||
if (!in_array(xmlsitemap_get_chunk_size(), array(50000, 'auto'))) {
|
||||
$requirements['xmlsitemap_chunk_count']['description'] .= ' ' . t('Please increase the number of links per page.');
|
||||
}
|
||||
}
|
||||
|
||||
// Check maximum file size.
|
||||
$max_filesize = db_query("SELECT MAX(max_filesize) FROM {xmlsitemap_sitemap}")->fetchField();
|
||||
$requirements['xmlsitemap_file_size'] = array(
|
||||
'title' => $t('XML sitemap maximum file size'),
|
||||
'value' => format_size($max_filesize),
|
||||
);
|
||||
if ($max_filesize > XMLSITEMAP_MAX_SITEMAP_FILESIZE) {
|
||||
$requirements['xmlsitemap_file_size']['description'] = $t('You have exceeded the maximum sitemap file size of @size. If possible, decrease the number of links per sitemap page.', array('@size' => format_size(XMLSITEMAP_MAX_SITEMAP_FILESIZE)));
|
||||
$requirements['xmlsitemap_file_size']['severity'] = REQUIREMENT_ERROR;
|
||||
}
|
||||
elseif (!variable_get('xmlsitemap_developer_mode', 0)) {
|
||||
unset($requirements['xmlsitemap_file_size']);
|
||||
}
|
||||
|
||||
// Check when the cached files were last generated.
|
||||
$generated_last = variable_get('xmlsitemap_generated_last', 0);
|
||||
$generated_ago = REQUEST_TIME - $generated_last;
|
||||
$requirements['xmlsitemap_generated'] = array(
|
||||
'title' => $t('XML sitemap'),
|
||||
'value' => $generated_last ? $t('Last attempted generation on !date (!interval ago).', array('!date' => format_date($generated_last, 'small'), '!interval' => format_interval($generated_ago))) : $t('Cached files have not been generated yet.'),
|
||||
'severity' => REQUIREMENT_OK,
|
||||
);
|
||||
if (variable_get('xmlsitemap_rebuild_needed', FALSE) && _xmlsitemap_rebuild_form_access()) {
|
||||
$requirements['xmlsitemap_generated']['severity'] = REQUIREMENT_ERROR;
|
||||
$requirements['xmlsitemap_generated']['description'] = $t('The XML sitemap data is out of sync and needs to be <a href="@link-rebuild">completely rebuilt<a>.', array('@link-rebuild' => url('admin/config/search/xmlsitemap/rebuild')));
|
||||
}
|
||||
elseif (variable_get('xmlsitemap_regenerate_needed', FALSE)) {
|
||||
if ($max_filesize == 0) {
|
||||
// A maximum sitemap file size of 0 indicates an error in generation.
|
||||
$requirements['xmlsitemap_generated']['severity'] = REQUIREMENT_ERROR;
|
||||
}
|
||||
elseif ($generated_ago >= variable_get('cron_threshold_error', 1209600)) {
|
||||
$requirements['xmlsitemap_generated']['severity'] = REQUIREMENT_ERROR;
|
||||
}
|
||||
elseif ($generated_ago >= variable_get('cron_threshold_warning', 172800)) {
|
||||
$requirements['xmlsitemap_generated']['severity'] = REQUIREMENT_WARNING;
|
||||
}
|
||||
if ($requirements['xmlsitemap_generated']['severity']) {
|
||||
$requirements['xmlsitemap_generated']['description'] = $t('The XML cached files are out of date and need to be regenerated. You can <a href="@link-cron">run cron manually</a> to regenerate the sitemap files.', array('@link-cron' => url('admin/reports/status/run-cron', array('query' => drupal_get_destination()))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function xmlsitemap_schema() {
|
||||
// @todo Rename to xmlsitemap_link
|
||||
$schema['xmlsitemap'] = array(
|
||||
'description' => 'The base table for xmlsitemap links.',
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
'description' => 'Primary key with type; a unique id for the item.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'unsigned' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'type' => array(
|
||||
'description' => 'Primary key with id; the type of item (e.g. node, user, etc.).',
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'subtype' => array(
|
||||
'description' => 'A sub-type identifier for the link (node type, menu name, term VID, etc.).',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'loc' => array(
|
||||
'description' => 'The URL to the item relative to the Drupal path.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'language' => array(
|
||||
'description' => 'The {languages}.language of this link or an empty string if it is language-neutral.',
|
||||
'type' => 'varchar',
|
||||
'length' => 12,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'access' => array(
|
||||
'description' => 'A boolean that represents if the item is viewable by the anonymous user. This field is useful to store the result of node_access() so we can retain changefreq and priority_override information.',
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
),
|
||||
'status' => array(
|
||||
'description' => 'An integer that represents if the item is included in the sitemap.',
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
),
|
||||
'status_override' => array(
|
||||
'description' => 'A boolean that if TRUE means that the status field has been overridden from its default value.',
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'lastmod' => array(
|
||||
'description' => 'The UNIX timestamp of last modification of the item.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'priority' => array(
|
||||
'description' => 'The priority of this URL relative to other URLs on your site. Valid values range from 0.0 to 1.0.',
|
||||
'type' => 'float',
|
||||
'default' => NULL,
|
||||
// @todo Convert this field to non-nullable.
|
||||
//'default' => 0.5,
|
||||
//'not null' => NULL,
|
||||
),
|
||||
'priority_override' => array(
|
||||
'description' => 'A boolean that if TRUE means that the priority field has been overridden from its default value.',
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'changefreq' => array(
|
||||
'description' => 'The average time in seconds between changes of this item.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'changecount' => array(
|
||||
'description' => 'The number of times this item has been changed. Used to help calculate the next changefreq value.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('id', 'type'),
|
||||
'indexes' => array(
|
||||
'loc' => array('loc'),
|
||||
'access_status_loc' => array('access', 'status', 'loc'),
|
||||
'type_subtype' => array('type', 'subtype'),
|
||||
'language' => array('language'),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['xmlsitemap_sitemap'] = array(
|
||||
'fields' => array(
|
||||
'smid' => array(
|
||||
'description' => 'The sitemap ID (the hashed value of {xmlsitemap}.context.',
|
||||
'type' => 'varchar',
|
||||
'length' => 64,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'context' => array(
|
||||
'description' => 'Serialized array with the sitemaps context',
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'updated' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'links' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'chunks' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'max_filesize' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
//'queued' => array(
|
||||
// 'type' => 'int',
|
||||
// 'unsigned' => TRUE,
|
||||
// 'not null' => TRUE,
|
||||
// 'default' => 0,
|
||||
// 'description' => 'Time when this sitemap was queued for regeneration, 0 if not queued.',
|
||||
//),
|
||||
),
|
||||
'primary key' => array('smid'),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function xmlsitemap_install() {
|
||||
// Set this module's weight to 1 so xmlsitemap_cron() runs after all other
|
||||
// xmlsitemap_x_cron() runs.
|
||||
db_update('system')
|
||||
->fields(array('weight' => 1))
|
||||
->condition('type', 'module')
|
||||
->condition('name', 'xmlsitemap')
|
||||
->execute();
|
||||
|
||||
// Load the module.
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
|
||||
// Insert the homepage link into the {xmlsitemap} table so we do not show an
|
||||
// empty sitemap after install.
|
||||
db_insert('xmlsitemap')
|
||||
->fields(array(
|
||||
'type' => 'frontpage',
|
||||
'id' => 0,
|
||||
'loc' => '',
|
||||
'priority' => variable_get('xmlsitemap_frontpage_priority', 1.0),
|
||||
'changefreq' => variable_get('xmlsitemap_frontpage_changefreq', XMLSITEMAP_FREQUENCY_DAILY),
|
||||
'language' => LANGUAGE_NONE,
|
||||
))
|
||||
->execute();
|
||||
|
||||
// Insert the default context sitemap.
|
||||
$context = array();
|
||||
db_insert('xmlsitemap_sitemap')
|
||||
->fields(array(
|
||||
'smid' => xmlsitemap_sitemap_get_context_hash($context),
|
||||
'context' => serialize($context),
|
||||
))
|
||||
->execute();
|
||||
|
||||
// @todo Does the sitemap show up on first install or is it a 404 page?
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_enable().
|
||||
*/
|
||||
function xmlsitemap_enable() {
|
||||
// Ensure the file cache directory is available and ready.
|
||||
xmlsitemap_check_directory();
|
||||
|
||||
variable_set('xmlsitemap_regenerate_needed', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function xmlsitemap_uninstall() {
|
||||
// Remove variables.
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
$variables = array_keys(xmlsitemap_variables());
|
||||
foreach ($variables as $variable) {
|
||||
variable_del($variable);
|
||||
}
|
||||
|
||||
// Remove the file cache directory.
|
||||
xmlsitemap_clear_directory(NULL, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_update_last_removed().
|
||||
*/
|
||||
function xmlsitemap_update_last_removed() {
|
||||
return 6201;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {xmlsitemap_sitemap} table and add the sitemap context data.
|
||||
*/
|
||||
function xmlsitemap_update_6202() {
|
||||
if (!db_table_exists('xmlsitemap_sitemap')) {
|
||||
$schema['xmlsitemap_sitemap'] = array(
|
||||
'fields' => array(
|
||||
'smid' => array(
|
||||
'description' => 'Sitemap ID',
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'context_hash' => array(
|
||||
'description' => 'The MD5 hash of the context field.',
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'context' => array(
|
||||
'description' => 'Serialized array with the sitemaps context',
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'updated' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'links' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'chunks' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('smid'),
|
||||
'unique keys' => array(
|
||||
'context_hash' => array('context_hash'),
|
||||
),
|
||||
);
|
||||
db_create_table('xmlsitemap_sitemap', $schema['xmlsitemap_sitemap']);
|
||||
}
|
||||
|
||||
// Add the default sitemap(s) and use language contexts if possible.
|
||||
if (!db_query_range("SELECT 1 FROM {xmlsitemap_sitemap}", 0, 1)->fetchField()) {
|
||||
// Refresh the schema and load the module if it's disabled.
|
||||
drupal_get_schema(NULL, TRUE);
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
|
||||
if (module_exists('xmlsitemap_i18n') && $languages = variable_get('xmlsitemap_languages', array())) {
|
||||
foreach ($languages as $language) {
|
||||
$sitemap = new stdClass();
|
||||
$sitemap->context = array('language' => $language);
|
||||
xmlsitemap_sitemap_save($sitemap);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$sitemap = new stdClass();
|
||||
$sitemap->context = array();
|
||||
xmlsitemap_sitemap_save($sitemap);
|
||||
}
|
||||
}
|
||||
|
||||
// Language variable is no longer needed, so go ahead and delete it.
|
||||
variable_del('xmlsitemap_languages');
|
||||
|
||||
// Ensure that the sitemaps will be refreshed on next cron.
|
||||
variable_set('xmlsitemap_generated_last', 0);
|
||||
variable_set('xmlsitemap_regenerate_needed', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the xmlsitemap_max_filesize variable to a max_filesize column
|
||||
* per-sitemap.
|
||||
*/
|
||||
function xmlsitemap_update_6203() {
|
||||
if (db_field_exists('xmlsitemap_sitemap', 'max_filesize')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the max_filesize column.
|
||||
$field = array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
);
|
||||
db_add_field('xmlsitemap_sitemap', 'max_filesize', $field);
|
||||
|
||||
// Scan each sitemap directory for the largest file.
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
$sitemaps = xmlsitemap_sitemap_load_multiple(FALSE);
|
||||
foreach ($sitemaps as $sitemap) {
|
||||
xmlsitemap_sitemap_get_max_filesize($sitemap);
|
||||
db_update('xmlsitemap_sitemap')
|
||||
->fields(array('max_filesize' => $sitemap->max_filesize))
|
||||
->condition('smid', $sitemap->smid)
|
||||
->execute();
|
||||
}
|
||||
|
||||
variable_del('xmlsitemap_max_filesize');
|
||||
variable_del('xmlsitemap_max_chunks');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert {xmlsitemap}.context_hash to replace {xmlsitemap}.smid.
|
||||
*/
|
||||
function xmlsitemap_update_6204() {
|
||||
if (db_field_exists('xmlsitemap_sitemap', 'context_hash')) {
|
||||
db_drop_unique_key('xmlsitemap_sitemap', 'context_hash');
|
||||
db_drop_field('xmlsitemap_sitemap', 'smid');
|
||||
|
||||
// Rename context_hash to the new smid column.
|
||||
$smid_field = array(
|
||||
'description' => 'The sitemap ID (the hashed value of {xmlsitemap}.context.',
|
||||
'type' => 'varchar',
|
||||
'length' => 64,
|
||||
'not null' => TRUE,
|
||||
);
|
||||
db_change_field('xmlsitemap_sitemap', 'context_hash', 'smid', $smid_field);
|
||||
|
||||
// Re-add the primary key now that the smid field is changed.
|
||||
// We don't need to drop the primary key since we already dropped the field
|
||||
// that was the primary key.
|
||||
db_add_primary_key('xmlsitemap_sitemap', array('smid'));
|
||||
}
|
||||
|
||||
_xmlsitemap_sitemap_rehash_all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update empty string languages to LANGUAGE_NONE.
|
||||
*/
|
||||
function xmlsitemap_update_7200() {
|
||||
db_update('xmlsitemap')
|
||||
->fields(array('language' => LANGUAGE_NONE))
|
||||
->condition('language', '')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-run xmlsitemap_update_6202() to ensure sitemap data has been added.
|
||||
*/
|
||||
function xmlsitemap_update_7201() {
|
||||
xmlsitemap_update_6202();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the xmlsitemap_max_filesize variable to a max_filesize column
|
||||
* per-sitemap.
|
||||
*/
|
||||
function xmlsitemap_update_7202() {
|
||||
xmlsitemap_update_6203();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert {xmlsitemap}.context_hash to replace {xmlsitemap}.smid.
|
||||
*/
|
||||
function xmlsitemap_update_7203() {
|
||||
xmlsitemap_update_6204();
|
||||
_xmlsitemap_sitemap_rehash_all();
|
||||
}
|
||||
|
||||
function _xmlsitemap_sitemap_rehash_all() {
|
||||
// Reload the schema cache and reprocess all sitemap hashes into smids.
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
drupal_get_schema(NULL, TRUE);
|
||||
|
||||
// Force a rehash of all sitemaps.
|
||||
$sitemaps = xmlsitemap_sitemap_load_multiple(FALSE);
|
||||
foreach ($sitemaps as $sitemap) {
|
||||
$hash = xmlsitemap_sitemap_get_context_hash($sitemap->context);
|
||||
if ($hash != $sitemap->smid) {
|
||||
xmlsitemap_sitemap_save($sitemap);
|
||||
}
|
||||
}
|
||||
}
|
22
sites/all/modules/xmlsitemap/xmlsitemap.js
Normal file
22
sites/all/modules/xmlsitemap/xmlsitemap.js
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
(function ($) {
|
||||
|
||||
Drupal.behaviors.xmlsitemapFieldsetSummaries = {
|
||||
attach: function (context) {
|
||||
$('fieldset#edit-xmlsitemap', context).drupalSetSummary(function (context) {
|
||||
var vals = [];
|
||||
|
||||
// Inclusion select field.
|
||||
var status = $('#edit-xmlsitemap-status option:selected').text();
|
||||
vals.push(Drupal.t('Inclusion: @value', { '@value': status }));
|
||||
|
||||
// Priority select field.
|
||||
var priority = $('#edit-xmlsitemap-priority option:selected').text();
|
||||
vals.push(Drupal.t('Priority: @value', { '@value': priority }));
|
||||
|
||||
return vals.join('<br />');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
1539
sites/all/modules/xmlsitemap/xmlsitemap.module
Normal file
1539
sites/all/modules/xmlsitemap/xmlsitemap.module
Normal file
File diff suppressed because it is too large
Load Diff
159
sites/all/modules/xmlsitemap/xmlsitemap.pages.inc
Normal file
159
sites/all/modules/xmlsitemap/xmlsitemap.pages.inc
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Page callbacks for the xmlsitemap module.
|
||||
*
|
||||
* @ingroup xmlsitemap
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the sitemap chunk/page of the current request.
|
||||
*/
|
||||
function xmlsitemap_get_current_chunk(stdClass $sitemap) {
|
||||
// Check if we should be displaing the index.
|
||||
if (!isset($_GET['page']) || !is_numeric($_GET['page'])) {
|
||||
if ($sitemap->chunks > 1) {
|
||||
return 'index';
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return (int) $_GET['page'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a sitemap page.
|
||||
*
|
||||
* @see xmlsitemap_sitemap_load_by_context()
|
||||
* @see xmlsitemap_get_current_chunk()
|
||||
* @see xmlsitemap_sitemap_get_file()
|
||||
* @see xmlsitemap_output_file()
|
||||
*/
|
||||
function xmlsitemap_output_chunk() {
|
||||
$sitemap = xmlsitemap_sitemap_load_by_context();
|
||||
if (!$sitemap) {
|
||||
return MENU_NOT_FOUND;
|
||||
}
|
||||
|
||||
$chunk = xmlsitemap_get_current_chunk($sitemap);
|
||||
$file = xmlsitemap_sitemap_get_file($sitemap, $chunk);
|
||||
|
||||
// Provide debugging information if enabled.
|
||||
if (variable_get('xmlsitemap_developer_mode', 0) && isset($_GET['debug'])) {
|
||||
$output = array();
|
||||
$context = xmlsitemap_get_current_context();
|
||||
$output[] = "Current context: " . print_r($context, TRUE);
|
||||
$output[] = "Sitemap: " . print_r($sitemap, TRUE);
|
||||
$output[] = "Chunk: $chunk";
|
||||
$output[] = "Cache file location: $file";
|
||||
$output[] = "Cache file exists: " . (file_exists($file) ? 'Yes' : 'No');
|
||||
return implode('<br />', $output);
|
||||
}
|
||||
|
||||
return xmlsitemap_output_file($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the contents of a file to the browser and check caching headers.
|
||||
*/
|
||||
function xmlsitemap_output_file($file, array $headers = array()) {
|
||||
if (!file_exists($file) || !is_readable($file)) {
|
||||
return MENU_NOT_FOUND;
|
||||
}
|
||||
|
||||
$mtime = filemtime($file);
|
||||
$last_modified = gmdate(DATE_RFC1123, $mtime);
|
||||
$etag = '"' . md5($last_modified) . '"';
|
||||
|
||||
// See if the client has provided the required HTTP headers.
|
||||
$if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
|
||||
$if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;
|
||||
if ($if_modified_since && $if_none_match && $if_none_match == $etag && $if_modified_since == $last_modified) {
|
||||
header('HTTP/1.1 304 Not Modified');
|
||||
// All 304 responses must send an etag if the 200 response for the same object contained an etag
|
||||
header('Etag: ' . $etag);
|
||||
exit;
|
||||
}
|
||||
|
||||
$headers += array(
|
||||
'Content-type' => 'text/xml; charset=utf-8',
|
||||
//'Content-length' => filesize($file),
|
||||
'Last-modified' => $last_modified,
|
||||
'Etag' => $etag,
|
||||
'Expires' => gmdate(DATE_RFC1123, $mtime + variable_get('xmlsitemap_minimum_lifetime', 0)),
|
||||
'Cache-Control' => 'must-revalidate',
|
||||
'X-Robots-Tag' => 'noindex, follow',
|
||||
);
|
||||
|
||||
// Transfer the file as output.
|
||||
xmlsitemap_file_transfer($file, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modified version of file_transfer() that invokes hook_exit()s afterwards.
|
||||
*
|
||||
* @see file_transfer()
|
||||
*/
|
||||
function xmlsitemap_file_transfer($uri, $headers) {
|
||||
if (ob_get_level()) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
foreach ($headers as $name => $value) {
|
||||
drupal_add_http_header($name, $value);
|
||||
}
|
||||
drupal_send_headers();
|
||||
|
||||
// Attempt to increase time to transfer file.
|
||||
drupal_set_time_limit(240);
|
||||
|
||||
$scheme = variable_get('file_default_scheme', 'public');
|
||||
// Transfer file in 16 KB chunks to save memory usage.
|
||||
if ($scheme && file_stream_wrapper_valid_scheme($scheme) && $fd = fopen($uri, 'rb')) {
|
||||
while (!feof($fd)) {
|
||||
print fread($fd, 1024*16);
|
||||
}
|
||||
fclose($fd);
|
||||
}
|
||||
else {
|
||||
drupal_not_found();
|
||||
}
|
||||
drupal_exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an XML transformation file for the sitemap XML.
|
||||
*/
|
||||
function xmlsitemap_output_xsl() {
|
||||
// Read the XSL content from the file.
|
||||
$module_path = drupal_get_path('module', 'xmlsitemap');
|
||||
$xsl_content = file_get_contents($module_path . '/xsl/xmlsitemap.xsl');
|
||||
|
||||
// Make sure the strings in the XSL content are translated properly.
|
||||
$replacements = array(
|
||||
'Sitemap file' => t('Sitemap file'),
|
||||
'Generated by the <a href="http://drupal.org/project/xmlsitemap">Drupal XML sitemap module</a>.' => t('Generated by the <a href="@link-xmlsitemap">Drupal XML sitemap module</a>.', array('@link-xmlsitemap' => 'http://drupal.org/project/xmlsitemap')),
|
||||
'Number of sitemaps in this index' => t('Number of sitemaps in this index'),
|
||||
'Click on the table headers to change sorting.' => t('Click on the table headers to change sorting.'),
|
||||
'Sitemap URL' => t('Sitemap URL'),
|
||||
'Last modification date' => t('Last modification date'),
|
||||
'Number of URLs in this sitemap' => t('Number of URLs in this sitemap'),
|
||||
'URL location' => t('URL location'),
|
||||
'Change frequency' => t('Change frequency'),
|
||||
'Priority' => t('Priority'),
|
||||
'[jquery]' => base_path() . 'misc/jquery.js',
|
||||
'[jquery-tablesort]' => base_path() . $module_path . '/xsl/jquery.tablesorter.min.js',
|
||||
'[xsl-js]' => base_path() . $module_path . '/xsl/xmlsitemap.xsl.js',
|
||||
'[xsl-css]' => base_path() . $module_path . '/xsl/xmlsitemap.xsl.css',
|
||||
);
|
||||
$xsl_content = strtr($xsl_content, $replacements);
|
||||
|
||||
// Output the XSL content.
|
||||
drupal_add_http_header('Content-type', 'application/xml; charset=utf-8');
|
||||
drupal_add_http_header('X-Robots-Tag', 'noindex, follow');
|
||||
print $xsl_content;
|
||||
}
|
783
sites/all/modules/xmlsitemap/xmlsitemap.test
Normal file
783
sites/all/modules/xmlsitemap/xmlsitemap.test
Normal file
@@ -0,0 +1,783 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Unit tests for the xmlsitemap module.
|
||||
*
|
||||
* @ingroup xmlsitemap
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper test class with some added functions for testing.
|
||||
*/
|
||||
class XMLSitemapTestHelper extends DrupalWebTestCase {
|
||||
protected $admin_user;
|
||||
|
||||
function setUp($modules = array()) {
|
||||
array_unshift($modules, 'xmlsitemap');
|
||||
parent::setUp($modules);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
// Capture any (remaining) watchdog errors.
|
||||
$this->assertNoWatchdogErrors();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the page does not respond with the specified response code.
|
||||
*
|
||||
* @param $code
|
||||
* Response code. For example 200 is a successful page request. For a list
|
||||
* of all codes see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.
|
||||
* @param $message
|
||||
* Message to display.
|
||||
* @return
|
||||
* Assertion result.
|
||||
*/
|
||||
protected function assertNoResponse($code, $message = '') {
|
||||
$curl_code = curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE);
|
||||
$match = is_array($code) ? in_array($curl_code, $code) : $curl_code == $code;
|
||||
return $this->assertFalse($match, $message ? $message : t('HTTP response not expected !code, actual !curl_code', array('!code' => $code, '!curl_code' => $curl_code)), t('Browser'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the files directory is created (massive fails if not done).
|
||||
*
|
||||
* @todo This can be removed when http://drupal.org/node/654752 is fixed.
|
||||
*/
|
||||
protected function checkFilesDirectory() {
|
||||
if (!xmlsitemap_check_directory()) {
|
||||
$this->fail(t('Sitemap directory was found and writable for testing.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an XML sitemap.
|
||||
*
|
||||
* @param $context
|
||||
* An optional array of the XML sitemap's context.
|
||||
* @param $options
|
||||
* Options to be forwarded to url(). These values will be merged with, but
|
||||
* always override $sitemap->uri['options'].
|
||||
* @param $headers
|
||||
* An array containing additional HTTP request headers, each formatted as
|
||||
* "name: value".
|
||||
* @return
|
||||
* The retrieved HTML string, also available as $this->drupalGetContent()
|
||||
*/
|
||||
protected function drupalGetSitemap(array $context = array(), array $options = array(), array $headers = array()) {
|
||||
$sitemap = xmlsitemap_sitemap_load_by_context($context);
|
||||
if (!$sitemap) {
|
||||
return $this->fail('Could not load sitemap by context.');
|
||||
}
|
||||
return $this->drupalGet($sitemap->uri['path'], $options + $sitemap->uri['options'], $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate the sitemap by setting the regenerate flag and running cron.
|
||||
*/
|
||||
protected function regenerateSitemap() {
|
||||
variable_set('xmlsitemap_regenerate_needed', TRUE);
|
||||
variable_set('xmlsitemap_generated_last', 0);
|
||||
$this->cronRun();
|
||||
$this->assertTrue(variable_get('xmlsitemap_generated_last', 0) && !variable_get('xmlsitemap_regenerate_needed', FALSE), t('XML sitemaps regenerated and flag cleared.'));
|
||||
}
|
||||
|
||||
protected function assertSitemapLink($entity_type, $entity_id = NULL) {
|
||||
if (is_array($entity_type)) {
|
||||
$links = xmlsitemap_link_load_multiple($entity_type);
|
||||
$link = $links ? reset($links) : FALSE;
|
||||
}
|
||||
else {
|
||||
$link = xmlsitemap_link_load($entity_type, $entity_id);
|
||||
}
|
||||
$this->assertTrue(is_array($link), 'Link loaded.');
|
||||
return $link;
|
||||
}
|
||||
|
||||
protected function assertNoSitemapLink($entity_type, $entity_id = NULL) {
|
||||
if (is_array($entity_type)) {
|
||||
$links = xmlsitemap_link_load_multiple($entity_type);
|
||||
$link = $links ? reset($links) : FALSE;
|
||||
}
|
||||
else {
|
||||
$link = xmlsitemap_link_load($entity_type, $entity_id);
|
||||
}
|
||||
$this->assertFalse($link, 'Link not loaded.');
|
||||
return $link;
|
||||
}
|
||||
|
||||
protected function assertSitemapLinkVisible($entity_type, $entity_id) {
|
||||
$link = xmlsitemap_link_load($entity_type, $entity_id);
|
||||
$this->assertTrue($link && $link['access'] && $link['status'], t('Sitemap link @type @id is visible.', array('@type' => $entity_type, '@id' => $entity_id)));
|
||||
}
|
||||
|
||||
protected function assertSitemapLinkNotVisible($entity_type, $entity_id) {
|
||||
$link = xmlsitemap_link_load($entity_type, $entity_id);
|
||||
$this->assertTrue($link && !($link['access'] && $link['status']), t('Sitemap link @type @id is not visible.', array('@type' => $entity_type, '@id' => $entity_id)));
|
||||
}
|
||||
|
||||
protected function assertSitemapLinkValues($entity_type, $entity_id, array $conditions) {
|
||||
$link = xmlsitemap_link_load($entity_type, $entity_id);
|
||||
|
||||
if (!$link) {
|
||||
return $this->fail(t('Could not load sitemap link for @type @id.', array('@type' => $entity_type, '@id' => $entity_id)));
|
||||
}
|
||||
|
||||
foreach ($conditions as $key => $value) {
|
||||
if ($value === NULL || $link[$key] === NULL) {
|
||||
// For nullable fields, always check for identical values (===).
|
||||
$this->assertIdentical($link[$key], $value, t('Identical values for @type @id link field @key.', array('@type' => $entity_type, '@id' => $entity_id, '@key' => $key)));
|
||||
}
|
||||
else {
|
||||
// Otherwise check simple equality (==).
|
||||
$this->assertEqual($link[$key], $value, t('Equal values for @type @id link field @key.', array('@type' => $entity_type, '@id' => $entity_id, '@key' => $key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function assertNotSitemapLinkValues($entity_type, $entity_id, array $conditions) {
|
||||
$link = xmlsitemap_link_load($entity_type, $entity_id);
|
||||
|
||||
if (!$link) {
|
||||
return $this->fail(t('Could not load sitemap link for @type @id.', array('@type' => $entity_type, '@id' => $entity_id)));
|
||||
}
|
||||
|
||||
foreach ($conditions as $key => $value) {
|
||||
if ($value === NULL || $link[$key] === NULL) {
|
||||
// For nullable fields, always check for identical values (===).
|
||||
$this->assertNotIdentical($link[$key], $value, t('Not identical values for @type @id link field @key.', array('@type' => $entity_type, '@id' => $entity_id, '@key' => $key)));
|
||||
}
|
||||
else {
|
||||
// Otherwise check simple equality (==).
|
||||
$this->assertNotEqual($link[$key], $value, t('Not equal values for link @type @id field @key.', array('@type' => $entity_type, '@id' => $entity_id, '@key' => $key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function assertRawSitemapLinks() {
|
||||
$links = func_get_args();
|
||||
foreach ($links as $link) {
|
||||
$path = url($link['loc'], array('language' => xmlsitemap_language_load($link['language']), 'absolute' => TRUE));
|
||||
$this->assertRaw($link['loc'], t('Link %path found in the sitemap.', array('%path' => $path)));
|
||||
}
|
||||
}
|
||||
|
||||
protected function assertNoRawSitemapLinks() {
|
||||
$links = func_get_args();
|
||||
foreach ($links as $link) {
|
||||
$path = url($link['loc'], array('language' => xmlsitemap_language_load($link['language']), 'absolute' => TRUE));
|
||||
$this->assertNoRaw($link['loc'], t('Link %path not found in the sitemap.', array('%path' => $path)));
|
||||
}
|
||||
}
|
||||
|
||||
protected function addSitemapLink(array $link = array()) {
|
||||
$last_id = &drupal_static(__FUNCTION__, 1);
|
||||
|
||||
$link += array(
|
||||
'type' => 'testing',
|
||||
'id' => $last_id,
|
||||
'access' => 1,
|
||||
'status' => 1,
|
||||
);
|
||||
|
||||
// Make the default path easier to read than a random string.
|
||||
$link += array('loc' => $link['type'] . '-' . $link['id']);
|
||||
|
||||
$last_id = max($last_id, $link['id']) + 1;
|
||||
xmlsitemap_link_save($link);
|
||||
return $link;
|
||||
}
|
||||
|
||||
protected function assertFlag($variable, $assert_value = TRUE, $reset_if_true = TRUE) {
|
||||
$value = xmlsitemap_var($variable);
|
||||
|
||||
if ($reset_if_true && $value) {
|
||||
variable_set('xmlsitemap_' . $variable, FALSE);
|
||||
}
|
||||
|
||||
return $this->assertEqual($value, $assert_value, "xmlsitemap_$variable is " . ($assert_value ? 'TRUE' : 'FALSE'));
|
||||
}
|
||||
|
||||
protected function assertXMLSitemapProblems($problem_text = FALSE) {
|
||||
$this->drupalGet('admin/config/search/xmlsitemap');
|
||||
$this->assertText(t('One or more problems were detected with your XML sitemap configuration'));
|
||||
if ($problem_text) {
|
||||
$this->assertText($problem_text);
|
||||
}
|
||||
}
|
||||
|
||||
protected function assertNoXMLSitemapProblems() {
|
||||
$this->drupalGet('admin/config/search/xmlsitemap');
|
||||
$this->assertNoText(t('One or more problems were detected with your XML sitemap configuration'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all seen watchdog messages.
|
||||
*
|
||||
* @todo Add unit tests for this function.
|
||||
*/
|
||||
protected function getWatchdogMessages(array $conditions = array(), $reset = FALSE) {
|
||||
static $seen_ids = array();
|
||||
|
||||
if (!module_exists('dblog') || $reset) {
|
||||
$seen_ids = array();
|
||||
return array();
|
||||
}
|
||||
|
||||
$query = db_select('watchdog');
|
||||
$query->fields('watchdog', array('wid', 'type', 'severity', 'message', 'variables', 'timestamp'));
|
||||
foreach ($conditions as $field => $value) {
|
||||
if ($field == 'variables' && !is_string($value)) {
|
||||
$value = serialize($value);
|
||||
}
|
||||
$query->condition($field, $value);
|
||||
}
|
||||
if ($seen_ids) {
|
||||
$query->condition('wid', $seen_ids, 'NOT IN');
|
||||
}
|
||||
$query->orderBy('timestamp');
|
||||
$messages = $query->execute()->fetchAllAssoc('wid');
|
||||
|
||||
$seen_ids = array_merge($seen_ids, array_keys($messages));
|
||||
return $messages;
|
||||
}
|
||||
|
||||
protected function assertWatchdogMessage(array $conditions, $message = 'Watchdog message found.') {
|
||||
$this->assertTrue($this->getWatchdogMessages($conditions), $message);
|
||||
}
|
||||
|
||||
protected function assertNoWatchdogMessage(array $conditions, $message = 'Watchdog message not found.') {
|
||||
$this->assertFalse($this->getWatchdogMessages($conditions), $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that there were no watchdog errors or worse.
|
||||
*/
|
||||
protected function assertNoWatchdogErrors() {
|
||||
$messages = $this->getWatchdogMessages();
|
||||
$verbose = array();
|
||||
|
||||
foreach ($messages as $message) {
|
||||
$message->text = $this->formatWatchdogMessage($message);
|
||||
if (in_array($message->severity, array(WATCHDOG_EMERGENCY, WATCHDOG_ALERT, WATCHDOG_CRITICAL, WATCHDOG_ERROR, WATCHDOG_WARNING))) {
|
||||
$this->fail($message->text);
|
||||
}
|
||||
$verbose[] = $message->text;
|
||||
}
|
||||
|
||||
if ($verbose) {
|
||||
array_unshift($verbose, '<h2>Watchdog messages</h2>');
|
||||
$this->verbose(implode("<br />", $verbose), 'Watchdog messages from test run');
|
||||
}
|
||||
|
||||
// Clear the seen watchdog messages since we've failed on any errors.
|
||||
$this->getWatchdogMessages(array(), TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a watchdog message in a one-line summary.
|
||||
*
|
||||
* @param $message
|
||||
* A watchdog messsage object.
|
||||
* @return
|
||||
* A string containing the watchdog message's timestamp, severity, type,
|
||||
* and actual message.
|
||||
*/
|
||||
private function formatWatchdogMessage(stdClass $message) {
|
||||
static $levels;
|
||||
|
||||
if (!isset($levels)) {
|
||||
module_load_include('admin.inc', 'dblog');
|
||||
$levels = watchdog_severity_levels();
|
||||
}
|
||||
|
||||
return t('@timestamp - @severity - @type - !message', array(
|
||||
'@timestamp' => $message->timestamp,
|
||||
'@severity' => $levels[$message->severity],
|
||||
'@type' => $message->type,
|
||||
'!message' => theme_dblog_message(array('event' => $message, 'link' => FALSE)),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log verbose message in a text file.
|
||||
*
|
||||
* This is a copy of DrupalWebTestCase->verbose() but allows a customizable
|
||||
* summary message rather than hard-coding 'Verbose message'.
|
||||
*
|
||||
* @param $verbose_message
|
||||
* The verbose message to be stored.
|
||||
* @param $message
|
||||
* Message to display.
|
||||
* @see simpletest_verbose()
|
||||
*
|
||||
* @todo Remove when http://drupal.org/node/800426 is fixed.
|
||||
*/
|
||||
protected function verbose($verbose_message, $message = 'Verbose message') {
|
||||
if ($id = simpletest_verbose($verbose_message)) {
|
||||
$url = file_create_url($this->originalFileDirectory . '/simpletest/verbose/' . get_class($this) . '-' . $id . '.html');
|
||||
$this->error(l($message, $url, array('attributes' => array('target' => '_blank'))), 'User notice');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class XMLSitemapUnitTest extends XMLSitemapTestHelper {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XML sitemap unit tests',
|
||||
'description' => 'Unit tests for the XML sitemap module.',
|
||||
'group' => 'XML sitemap',
|
||||
);
|
||||
}
|
||||
|
||||
function testAssertFlag() {
|
||||
variable_set('xmlsitemap_rebuild_needed', TRUE);
|
||||
$this->assertTrue(xmlsitemap_var('rebuild_needed'));
|
||||
$this->assertTrue($this->assertFlag('rebuild_needed', TRUE, FALSE));
|
||||
$this->assertTrue(xmlsitemap_var('rebuild_needed'));
|
||||
$this->assertTrue($this->assertFlag('rebuild_needed', TRUE, TRUE));
|
||||
$this->assertFalse(xmlsitemap_var('rebuild_needed'));
|
||||
$this->assertTrue($this->assertFlag('rebuild_needed', FALSE, FALSE));
|
||||
$this->assertFalse(xmlsitemap_var('rebuild_needed'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for xmlsitemap_get_changefreq().
|
||||
*/
|
||||
function testGetChangefreq() {
|
||||
// The test values.
|
||||
$values = array(
|
||||
0,
|
||||
mt_rand(1, XMLSITEMAP_FREQUENCY_ALWAYS),
|
||||
mt_rand(XMLSITEMAP_FREQUENCY_ALWAYS + 1, XMLSITEMAP_FREQUENCY_HOURLY),
|
||||
mt_rand(XMLSITEMAP_FREQUENCY_HOURLY + 1, XMLSITEMAP_FREQUENCY_DAILY),
|
||||
mt_rand(XMLSITEMAP_FREQUENCY_DAILY + 1, XMLSITEMAP_FREQUENCY_WEEKLY),
|
||||
mt_rand(XMLSITEMAP_FREQUENCY_WEEKLY + 1, XMLSITEMAP_FREQUENCY_MONTHLY),
|
||||
mt_rand(XMLSITEMAP_FREQUENCY_MONTHLY + 1, XMLSITEMAP_FREQUENCY_YEARLY),
|
||||
mt_rand(XMLSITEMAP_FREQUENCY_YEARLY + 1, mt_getrandmax()),
|
||||
);
|
||||
|
||||
// The expected values.
|
||||
$expected = array(
|
||||
FALSE,
|
||||
'always',
|
||||
'hourly',
|
||||
'daily',
|
||||
'weekly',
|
||||
'monthly',
|
||||
'yearly',
|
||||
'never',
|
||||
);
|
||||
|
||||
foreach ($values as $i => $value) {
|
||||
$actual = xmlsitemap_get_changefreq($value);
|
||||
$this->assertIdentical($actual, $expected[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for xmlsitemap_get_chunk_count().
|
||||
*/
|
||||
function testGetChunkCount() {
|
||||
// Set a low chunk size for testing.
|
||||
variable_set('xmlsitemap_chunk_size', 4);
|
||||
|
||||
// Make the total number of links just equal to the chunk size.
|
||||
$count = db_query("SELECT COUNT(id) FROM {xmlsitemap}")->fetchField();
|
||||
for ($i = $count; $i < 4; $i++) {
|
||||
$this->addSitemapLink();
|
||||
$this->assertEqual(xmlsitemap_get_chunk_count(TRUE), 1);
|
||||
}
|
||||
$this->assertEqual(db_query("SELECT COUNT(id) FROM {xmlsitemap}")->fetchField(), 4);
|
||||
|
||||
// Add a disabled link, should not change the chunk count.
|
||||
$this->addSitemapLink(array('status' => FALSE));
|
||||
$this->assertEqual(xmlsitemap_get_chunk_count(TRUE), 1);
|
||||
|
||||
// Add a visible link, should finally bump up the chunk count.
|
||||
$this->addSitemapLink();
|
||||
$this->assertEqual(xmlsitemap_get_chunk_count(TRUE), 2);
|
||||
|
||||
// Change all links to disabled. The chunk count should be 1 not 0.
|
||||
db_query("UPDATE {xmlsitemap} SET status = 0");
|
||||
$this->assertEqual(xmlsitemap_get_chunk_count(TRUE), 1);
|
||||
$this->assertEqual(xmlsitemap_get_link_count(), 0);
|
||||
|
||||
// Delete all links. The chunk count should be 1 not 0.
|
||||
db_query("DELETE FROM {xmlsitemap}");
|
||||
$this->assertEqual(db_query("SELECT COUNT(id) FROM {xmlsitemap}")->fetchField(), 0);
|
||||
$this->assertEqual(xmlsitemap_get_chunk_count(TRUE), 1);
|
||||
}
|
||||
|
||||
//function testGetChunkFile() {
|
||||
//}
|
||||
//
|
||||
//function testGetChunkSize() {
|
||||
//}
|
||||
//
|
||||
//function testGetLinkCount() {
|
||||
//}
|
||||
|
||||
/**
|
||||
* Tests for xmlsitemap_calculate_changereq().
|
||||
*/
|
||||
function testCalculateChangefreq() {
|
||||
// The test values.
|
||||
$values = array(
|
||||
array(),
|
||||
array(REQUEST_TIME),
|
||||
array(REQUEST_TIME, REQUEST_TIME - 200),
|
||||
array(REQUEST_TIME - 200, REQUEST_TIME, REQUEST_TIME - 600),
|
||||
);
|
||||
|
||||
// Expected values.
|
||||
$expected = array(0, 0, 200, 300);
|
||||
|
||||
foreach ($values as $i => $value) {
|
||||
$actual = xmlsitemap_calculate_changefreq($value);
|
||||
$this->assertEqual($actual, $expected[$i]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for xmlsitemap_recalculate_changefreq().
|
||||
*/
|
||||
function testRecalculateChangefreq() {
|
||||
// The starting test value.
|
||||
$value = array('lastmod' => REQUEST_TIME - 1000, 'changefreq' => 0, 'changecount' => 0);
|
||||
|
||||
// Expected values.
|
||||
$expecteds = array(
|
||||
array('lastmod' => REQUEST_TIME, 'changefreq' => 1000, 'changecount' => 1),
|
||||
array('lastmod' => REQUEST_TIME, 'changefreq' => 500, 'changecount' => 2),
|
||||
array('lastmod' => REQUEST_TIME, 'changefreq' => 333, 'changecount' => 3),
|
||||
);
|
||||
|
||||
foreach ($expecteds as $expected) {
|
||||
xmlsitemap_recalculate_changefreq($value);
|
||||
$this->assertEqual($value, $expected);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for xmlsitemap_switch_user and xmlsitemap_restore_user().
|
||||
*/
|
||||
function testSwitchUser() {
|
||||
global $user;
|
||||
|
||||
$original_user = $user;
|
||||
$new_user = $this->drupalCreateUser();
|
||||
|
||||
// Switch to a new valid user.
|
||||
$this->assertEqual(xmlsitemap_switch_user($new_user), TRUE);
|
||||
$this->assertEqual($user->uid, $new_user->uid);
|
||||
|
||||
// Switch again to the anonymous user.
|
||||
$this->assertEqual(xmlsitemap_switch_user(0), TRUE);
|
||||
$this->assertEqual($user->uid, 0);
|
||||
|
||||
// Switch again to the new user.
|
||||
$this->assertEqual(xmlsitemap_switch_user($new_user->uid), TRUE);
|
||||
$this->assertEqual($user->uid, $new_user->uid);
|
||||
|
||||
// Test that after two switches the original user was restored.
|
||||
$this->assertEqual(xmlsitemap_restore_user(), TRUE);
|
||||
$this->assertEqual($user->uid, $original_user->uid);
|
||||
|
||||
// Attempt to switch to the same user.
|
||||
$this->assertEqual(xmlsitemap_switch_user($original_user->uid), FALSE);
|
||||
$this->assertEqual($user->uid, $original_user->uid);
|
||||
$this->assertEqual(xmlsitemap_restore_user(), FALSE);
|
||||
$this->assertEqual($user->uid, $original_user->uid);
|
||||
|
||||
// Attempt to switch to an invalid user ID.
|
||||
$invalid_uid = db_query("SELECT MAX(uid) FROM {users}")->fetchField() + 100;
|
||||
$this->assertEqual(xmlsitemap_switch_user($invalid_uid), FALSE);
|
||||
$this->assertEqual($user->uid, $original_user->uid);
|
||||
$this->assertEqual(xmlsitemap_restore_user(), FALSE);
|
||||
$this->assertEqual($user->uid, $original_user->uid);
|
||||
|
||||
// Attempt user switching when the original user is anonymous.
|
||||
$user = drupal_anonymous_user();
|
||||
$this->assertEqual(xmlsitemap_switch_user(0), FALSE);
|
||||
$this->assertEqual($user->uid, 0);
|
||||
$this->assertEqual(xmlsitemap_restore_user(), FALSE);
|
||||
$this->assertEqual($user->uid, 0);
|
||||
}
|
||||
|
||||
//function testLoadLink() {
|
||||
//}
|
||||
|
||||
/**
|
||||
* Tests for xmlsitemap_link_save().
|
||||
*/
|
||||
function testSaveLink() {
|
||||
$link = array('type' => 'testing', 'id' => 1, 'loc' => 'testing', 'status' => 1);
|
||||
xmlsitemap_link_save($link);
|
||||
$this->assertFlag('regenerate_needed', TRUE);
|
||||
|
||||
$link['status'] = 0;
|
||||
xmlsitemap_link_save($link);
|
||||
$this->assertFlag('regenerate_needed', TRUE);
|
||||
|
||||
$link['priority'] = 0.5;
|
||||
$link['loc'] = 'new_location';
|
||||
$link['status'] = 1;
|
||||
xmlsitemap_link_save($link);
|
||||
$this->assertFlag('regenerate_needed', TRUE);
|
||||
|
||||
$link['priority'] = 0.0;
|
||||
xmlsitemap_link_save($link);
|
||||
$this->assertFlag('regenerate_needed', TRUE);
|
||||
|
||||
$link['priority'] = 0.1;
|
||||
xmlsitemap_link_save($link);
|
||||
$this->assertFlag('regenerate_needed', TRUE);
|
||||
|
||||
$link['priority'] = 1.0;
|
||||
xmlsitemap_link_save($link);
|
||||
$this->assertFlag('regenerate_needed', TRUE);
|
||||
|
||||
$link['priority'] = 1;
|
||||
xmlsitemap_link_save($link);
|
||||
$this->assertFlag('regenerate_needed', FALSE);
|
||||
|
||||
$link['priority'] = 0;
|
||||
xmlsitemap_link_save($link);
|
||||
$this->assertFlag('regenerate_needed', TRUE);
|
||||
|
||||
$link['priority'] = 0.5;
|
||||
xmlsitemap_link_save($link);
|
||||
$this->assertFlag('regenerate_needed', TRUE);
|
||||
|
||||
$link['priority'] = 0.5;
|
||||
$link['priority_override'] = 0;
|
||||
$link['status'] = 1;
|
||||
xmlsitemap_link_save($link);
|
||||
$this->assertFlag('regenerate_needed', FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for xmlsitemap_link_delete().
|
||||
*/
|
||||
function testLinkDelete() {
|
||||
// Add our testing data.
|
||||
$link1 = $this->addSitemapLink(array('loc' => 'testing1', 'status' => 0));
|
||||
$link2 = $this->addSitemapLink(array('loc' => 'testing1', 'status' => 1));
|
||||
$link3 = $this->addSitemapLink(array('status' => 0));
|
||||
variable_set('xmlsitemap_regenerate_needed', FALSE);
|
||||
|
||||
// Test delete multiple links.
|
||||
// Test that the regenerate flag is set when visible links are deleted.
|
||||
$deleted = xmlsitemap_link_delete_multiple(array('loc' => 'testing1'));
|
||||
$this->assertEqual($deleted, 2);
|
||||
$this->assertFalse(xmlsitemap_link_load($link1['type'], $link1['id']));
|
||||
$this->assertFalse(xmlsitemap_link_load($link2['type'], $link2['id']));
|
||||
$this->assertTrue(xmlsitemap_link_load($link3['type'], $link3['id']));
|
||||
$this->assertFlag('regenerate_needed', TRUE);
|
||||
|
||||
$deleted = xmlsitemap_link_delete($link3['type'], $link3['id']);
|
||||
$this->assertEqual($deleted, 1);
|
||||
$this->assertFalse(xmlsitemap_link_load($link3['type'], $link3['id']));
|
||||
$this->assertFlag('regenerate_needed', FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for xmlsitemap_link_update_multiple().
|
||||
*/
|
||||
function testUpdateLinks() {
|
||||
// Add our testing data.
|
||||
$links = array();
|
||||
$links[1] = $this->addSitemapLink(array('subtype' => 'group1'));
|
||||
$links[2] = $this->addSitemapLink(array('subtype' => 'group1'));
|
||||
$links[3] = $this->addSitemapLink(array('subtype' => 'group2'));
|
||||
variable_set('xmlsitemap_regenerate_needed', FALSE);
|
||||
// id | type | subtype | language | access | status | priority
|
||||
// 1 | testing | group1 | '' | 1 | 1 | 0.5
|
||||
// 2 | testing | group1 | '' | 1 | 1 | 0.5
|
||||
// 3 | testing | group2 | '' | 1 | 1 | 0.5
|
||||
|
||||
$updated = xmlsitemap_link_update_multiple(array('status' => 0), array('type' => 'testing', 'subtype' => 'group1', 'status_override' => 0));
|
||||
$this->assertEqual($updated, 2);
|
||||
$this->assertFlag('regenerate_needed', TRUE);
|
||||
// id | type | subtype | language | status | priority
|
||||
// 1 | testing | group1 | '' | 0 | 0.5
|
||||
// 2 | testing | group1 | '' | 0 | 0.5
|
||||
// 3 | testing | group2 | '' | 1 | 0.5
|
||||
|
||||
$updated = xmlsitemap_link_update_multiple(array('priority' => 0.0), array('type' => 'testing', 'subtype' => 'group1', 'priority_override' => 0));
|
||||
$this->assertEqual($updated, 2);
|
||||
$this->assertFlag('regenerate_needed', FALSE);
|
||||
// id | type | subtype | language | status | priority
|
||||
// 1 | testing | group1 | '' | 0 | 0.0
|
||||
// 2 | testing | group1 | '' | 0 | 0.0
|
||||
// 3 | testing | group2 | '' | 1 | 0.5
|
||||
|
||||
$updated = xmlsitemap_link_update_multiple(array('subtype' => 'group2'), array('type' => 'testing', 'subtype' => 'group1'));
|
||||
$this->assertEqual($updated, 2);
|
||||
$this->assertFlag('regenerate_needed', FALSE);
|
||||
// id | type | subtype | language | status | priority
|
||||
// 1 | testing | group2 | '' | 0 | 0.0
|
||||
// 2 | testing | group2 | '' | 0 | 0.0
|
||||
// 3 | testing | group2 | '' | 1 | 0.5
|
||||
|
||||
$updated = xmlsitemap_link_update_multiple(array('status' => 1), array('type' => 'testing', 'subtype' => 'group2', 'status_override' => 0, 'status' => 0));
|
||||
$this->assertEqual($updated, 2);
|
||||
$this->assertFlag('regenerate_needed', TRUE);
|
||||
// id | type | subtype | language | status | priority
|
||||
// 1 | testing | group2 | '' | 1 | 0.0
|
||||
// 2 | testing | group2 | '' | 1 | 0.0
|
||||
// 3 | testing | group2 | '' | 1 | 0.5
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that duplicate paths are skipped during generation.
|
||||
*/
|
||||
function testDuplicatePaths() {
|
||||
$link1 = $this->addSitemapLink(array('loc' => 'duplicate'));
|
||||
$link2 = $this->addSitemapLink(array('loc' => 'duplicate'));
|
||||
$this->regenerateSitemap();
|
||||
$this->drupalGetSitemap();
|
||||
$this->assertUniqueText('duplicate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the sitemap will not be genereated before the lifetime expires.
|
||||
*/
|
||||
function testMinimumLifetime() {
|
||||
variable_set('xmlsitemap_minimum_lifetime', 300);
|
||||
$this->regenerateSitemap();
|
||||
|
||||
$link = $this->addSitemapLink(array('loc' => 'lifetime-test'));
|
||||
$this->cronRun();
|
||||
$this->drupalGetSitemap();
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoRaw('lifetime-test');
|
||||
|
||||
variable_set('xmlsitemap_generated_last', REQUEST_TIME - 400);
|
||||
$this->cronRun();
|
||||
$this->drupalGetSitemap();
|
||||
$this->assertRaw('lifetime-test');
|
||||
|
||||
xmlsitemap_link_delete($link['type'], $link['id']);
|
||||
$this->cronRun();
|
||||
$this->drupalGetSitemap();
|
||||
$this->assertRaw('lifetime-test');
|
||||
|
||||
$this->regenerateSitemap();
|
||||
$this->drupalGetSitemap();
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoRaw('lifetime-test');
|
||||
}
|
||||
}
|
||||
|
||||
class XMLSitemapFunctionalTest extends XMLSitemapTestHelper {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XML sitemap interface tests',
|
||||
'description' => 'Functional tests for the XML sitemap module.',
|
||||
'group' => 'XML sitemap',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp($modules = array()) {
|
||||
$modules[] = 'path';
|
||||
parent::setUp($modules);
|
||||
$this->admin_user = $this->drupalCreateUser(array('access content', 'administer site configuration', 'administer xmlsitemap'));
|
||||
$this->drupalLogin($this->admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the sitemap file caching.
|
||||
*/
|
||||
function testSitemapCaching() {
|
||||
$this->regenerateSitemap();
|
||||
$this->drupalGetSitemap();
|
||||
$this->assertResponse(200);
|
||||
$etag = $this->drupalGetHeader('etag');
|
||||
$last_modified = $this->drupalGetHeader('last-modified');
|
||||
$this->assertTrue($etag, t('Etag header found.'));
|
||||
$this->assertTrue($last_modified, t('Last-modified header found.'));
|
||||
|
||||
$this->drupalGetSitemap(array(), array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
|
||||
$this->assertResponse(304);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test base URL functionality.
|
||||
*/
|
||||
function testBaseURL() {
|
||||
$edit = array('xmlsitemap_base_url' => '');
|
||||
$this->drupalPost('admin/config/search/xmlsitemap/settings', $edit, t('Save configuration'));
|
||||
$this->assertText(t('Default base URL field is required.'));
|
||||
|
||||
$edit = array('xmlsitemap_base_url' => 'invalid');
|
||||
$this->drupalPost('admin/config/search/xmlsitemap/settings', $edit, t('Save configuration'));
|
||||
$this->assertText(t('Invalid base URL.'));
|
||||
|
||||
$edit = array('xmlsitemap_base_url' => 'http://example.com/ ');
|
||||
$this->drupalPost('admin/config/search/xmlsitemap/settings', $edit, t('Save configuration'));
|
||||
$this->assertText(t('Invalid base URL.'));
|
||||
|
||||
$edit = array('xmlsitemap_base_url' => 'http://example.com/');
|
||||
$this->drupalPost('admin/config/search/xmlsitemap/settings', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
|
||||
$this->regenerateSitemap();
|
||||
$this->drupalGetSitemap(array(), array('base_url' => NULL));
|
||||
$this->assertRaw('<loc>http://example.com/</loc>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that configuration problems are reported properly in the status report.
|
||||
*/
|
||||
function testStatusReport() {
|
||||
// Test the rebuild flag.
|
||||
// @todo Re-enable these tests once we get a xmlsitemap_test.module.
|
||||
//variable_set('xmlsitemap_generated_last', REQUEST_TIME);
|
||||
//variable_set('xmlsitemap_rebuild_needed', TRUE);
|
||||
//$this->assertXMLSitemapProblems(t('The XML sitemap data is out of sync and needs to be completely rebuilt.'));
|
||||
//$this->clickLink(t('completely rebuilt'));
|
||||
//$this->assertResponse(200);
|
||||
//variable_set('xmlsitemap_rebuild_needed', FALSE);
|
||||
//$this->assertNoXMLSitemapProblems();
|
||||
|
||||
// Test the regenerate flag (and cron hasn't run in a while).
|
||||
variable_set('xmlsitemap_regenerate_needed', TRUE);
|
||||
variable_set('xmlsitemap_generated_last', REQUEST_TIME - variable_get('cron_threshold_warning', 172800) - 100);
|
||||
$this->assertXMLSitemapProblems(t('The XML cached files are out of date and need to be regenerated. You can run cron manually to regenerate the sitemap files.'));
|
||||
$this->clickLink(t('run cron manually'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoXMLSitemapProblems();
|
||||
|
||||
// Test chunk count > 1000.
|
||||
// Test directory not writable.
|
||||
}
|
||||
}
|
||||
|
||||
class XMLSitemapRobotsTxtIntegrationTest extends XMLSitemapTestHelper {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XML sitemap robots.txt',
|
||||
'description' => 'Integration tests for the XML sitemap and robots.txt module.',
|
||||
'group' => 'XML sitemap',
|
||||
'dependencies' => array('robotstxt'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp($modules = array()) {
|
||||
$modules[] = 'robotstxt';
|
||||
parent::setUp($modules);
|
||||
}
|
||||
|
||||
function testRobotsTxt() {
|
||||
// Request the un-clean robots.txt path so this will work in case there is
|
||||
// still the robots.txt file in the root directory.
|
||||
$this->drupalGet('', array('query' => array('q' => 'robots.txt')));
|
||||
$this->assertRaw('Sitemap: ' . url('sitemap.xml', array('absolute' => TRUE)));
|
||||
}
|
||||
}
|
307
sites/all/modules/xmlsitemap/xmlsitemap.xmlsitemap.inc
Normal file
307
sites/all/modules/xmlsitemap/xmlsitemap.xmlsitemap.inc
Normal file
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* XML sitemap integration functions for xmlsitemap.module.
|
||||
*/
|
||||
|
||||
class XMLSitemapException extends Exception {}
|
||||
|
||||
class XMLSitemapGenerationException extends XMLSitemapException {}
|
||||
|
||||
/**
|
||||
* Extended class for writing XML sitemap files.
|
||||
*/
|
||||
class XMLSitemapWriter extends XMLWriter {
|
||||
protected $status = TRUE;
|
||||
protected $uri = NULL;
|
||||
protected $sitemapElementCount = 0;
|
||||
protected $linkCountFlush = 500;
|
||||
protected $sitemap = NULL;
|
||||
protected $sitemap_page = NULL;
|
||||
protected $rootElement = 'urlset';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param $sitemap
|
||||
* The sitemap array.
|
||||
* @param $page
|
||||
* The current page of the sitemap being generated.
|
||||
*/
|
||||
function __construct(stdClass $sitemap, $page) {
|
||||
$this->sitemap = $sitemap;
|
||||
$this->sitemap_page = $page;
|
||||
$this->uri = xmlsitemap_sitemap_get_file($sitemap, $page);
|
||||
$this->openUri($this->uri);
|
||||
}
|
||||
|
||||
public function openUri($uri) {
|
||||
$return = parent::openUri(drupal_realpath($uri));
|
||||
if (!$return) {
|
||||
throw new XMLSitemapGenerationException(t('Could not open file @file for writing.', array('@file' => $uri)));
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function startDocument($version = '1.0', $encoding = 'UTF-8', $standalone = NULL) {
|
||||
$this->setIndent(FALSE);
|
||||
parent::startDocument($version, $encoding);
|
||||
if (variable_get('xmlsitemap_xsl', 1)) {
|
||||
$this->writeXSL();
|
||||
}
|
||||
$this->startElement($this->rootElement, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the XML stylesheet to the XML page.
|
||||
*/
|
||||
public function writeXSL() {
|
||||
$this->writePi('xml-stylesheet', 'type="text/xsl" href="' . url('sitemap.xsl') . '"');
|
||||
$this->writeRaw(PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of attributes for the root element of the XML.
|
||||
*/
|
||||
public function getRootAttributes() {
|
||||
$attributes['xmlns'] = 'http://www.sitemaps.org/schemas/sitemap/0.9';
|
||||
if (variable_get('xmlsitemap_developer_mode', 0)) {
|
||||
$attributes['xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance';
|
||||
$attributes['xsi:schemaLocation'] = 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd';
|
||||
}
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
public function generateXML() {
|
||||
return xmlsitemap_generate_chunk($this->sitemap, $this, $this->sitemap_page);
|
||||
}
|
||||
|
||||
public function startElement($name, $root = FALSE) {
|
||||
parent::startElement($name);
|
||||
|
||||
if ($root) {
|
||||
foreach ($this->getRootAttributes() as $name => $value) {
|
||||
$this->writeAttribute($name, $value);
|
||||
}
|
||||
$this->writeRaw(PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an full XML sitemap element tag.
|
||||
*
|
||||
* @param $name
|
||||
* The element name.
|
||||
* @param $element
|
||||
* An array of the elements properties and values.
|
||||
*/
|
||||
public function writeSitemapElement($name, array &$element) {
|
||||
$this->writeElement($name, $element);
|
||||
$this->writeRaw(PHP_EOL);
|
||||
|
||||
// After a certain number of elements have been added, flush the buffer
|
||||
// to the output file.
|
||||
$this->sitemapElementCount++;
|
||||
if (($this->sitemapElementCount % $this->linkCountFlush) == 0) {
|
||||
$this->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write full element tag including support for nested elements.
|
||||
*
|
||||
* @param $name
|
||||
* The element name.
|
||||
* @param $content
|
||||
* The element contents or an array of the elements' sub-elements.
|
||||
*/
|
||||
public function writeElement($name, $content = '') {
|
||||
if (is_array($content)) {
|
||||
$this->startElement($name);
|
||||
foreach ($content as $sub_name => $sub_content) {
|
||||
$this->writeElement($sub_name, $sub_content);
|
||||
}
|
||||
$this->endElement();
|
||||
}
|
||||
else {
|
||||
parent::writeElement($name, $content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override of XMLWriter::flush() to track file writing status.
|
||||
*/
|
||||
public function flush($empty = TRUE) {
|
||||
$return = parent::flush($empty);
|
||||
$this->status &= (bool) $return;
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function getStatus() {
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
public function getSitemapElementCount() {
|
||||
return $this->sitemapElementCount;
|
||||
}
|
||||
|
||||
public function endDocument() {
|
||||
$return = parent::endDocument();
|
||||
|
||||
if (!$this->getStatus()) {
|
||||
throw new XMLSitemapGenerationException(t('Unknown error occurred while writing to file @file.', array('@file' => $this->uri)));
|
||||
}
|
||||
|
||||
//if (xmlsitemap_var('gz')) {
|
||||
// $file_gz = $file . '.gz';
|
||||
// file_put_contents($file_gz, gzencode(file_get_contents($file), 9));
|
||||
//}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
class XMLSitemapIndexWriter extends XMLSitemapWriter {
|
||||
protected $rootElement = 'sitemapindex';
|
||||
|
||||
function __construct(stdClass $sitemap, $page = 'index') {
|
||||
parent::__construct($sitemap, 'index');
|
||||
}
|
||||
|
||||
public function getRootAttributes() {
|
||||
$attributes['xmlns'] = 'http://www.sitemaps.org/schemas/sitemap/0.9';
|
||||
if (variable_get('xmlsitemap_developer_mode', 0)) {
|
||||
$attributes['xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance';
|
||||
$attributes['xsi:schemaLocation'] = 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd';
|
||||
}
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
public function generateXML() {
|
||||
$lastmod_format = variable_get('xmlsitemap_lastmod_format', XMLSITEMAP_LASTMOD_MEDIUM);
|
||||
|
||||
$url_options = $this->sitemap->uri['options'];
|
||||
$url_options += array(
|
||||
'absolute' => TRUE,
|
||||
'base_url' => variable_get('xmlsitemap_base_url', $GLOBALS['base_url']),
|
||||
'language' => language_default(),
|
||||
'alias' => TRUE,
|
||||
);
|
||||
|
||||
for ($i = 1; $i <= $this->sitemap->chunks; $i++) {
|
||||
$url_options['query']['page'] = $i;
|
||||
$element = array(
|
||||
'loc' => url('sitemap.xml', $url_options),
|
||||
// @todo Use the actual lastmod value of the chunk file.
|
||||
'lastmod' => gmdate($lastmod_format, REQUEST_TIME),
|
||||
);
|
||||
$this->writeSitemapElement('sitemap', $element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_link_info().
|
||||
*/
|
||||
function xmlsitemap_xmlsitemap_link_info() {
|
||||
return array(
|
||||
'frontpage' => array(
|
||||
'label' => t('Frontpage'),
|
||||
'xmlsitemap' => array(
|
||||
'settings callback' => 'xmlsitemap_link_frontpage_settings',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* XML sitemap link type settings callback for frontpage link entity.
|
||||
*/
|
||||
function xmlsitemap_link_frontpage_settings(&$form) {
|
||||
module_load_include('admin.inc', 'xmlsitemap');
|
||||
if (user_access('administer site configuration')) {
|
||||
$form['#description'] = t('The front page path can be changed in the <a href="@url-frontpage">site information configuration</a>.', array('@url-frontpage' => url('admin/config/system/site-information')));
|
||||
}
|
||||
$form['xmlsitemap_frontpage_priority'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Priority'),
|
||||
'#options' => xmlsitemap_get_priority_options(),
|
||||
'#default_value' => variable_get('xmlsitemap_frontpage_priority', 1.0),
|
||||
);
|
||||
$form['xmlsitemap_frontpage_changefreq'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Change frequency'),
|
||||
'#options' => xmlsitemap_get_changefreq_options(),
|
||||
'#default_value' => variable_get('xmlsitemap_frontpage_changefreq', XMLSITEMAP_FREQUENCY_DAILY),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_link_alter().
|
||||
*/
|
||||
function xmlsitemap_xmlsitemap_link_alter(&$link) {
|
||||
// Alter the frontpage priority.
|
||||
if ($link['type'] == 'frontpage' || $link['loc'] == '' || $link['loc'] == variable_get('site_frontpage', 'node')) {
|
||||
$link['priority'] = variable_get('xmlsitemap_frontpage_priority', 1.0);
|
||||
$link['changefreq'] = variable_get('xmlsitemap_frontpage_changefreq', XMLSITEMAP_FREQUENCY_DAILY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_links().
|
||||
*/
|
||||
function xmlsitemap_xmlsitemap_links() {
|
||||
// Frontpage link.
|
||||
$links[] = array(
|
||||
'type' => 'frontpage',
|
||||
'id' => 0,
|
||||
'loc' => '',
|
||||
);
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_sitemap_operations().
|
||||
*/
|
||||
function xmlsitemap_xmlsitemap_sitemap_operations() {
|
||||
$operations['update'] = array(
|
||||
'label' => t('Update cached files'),
|
||||
'action past' => t('Updated'),
|
||||
'callback' => 'xmlsitemap_sitemap_multiple_update',
|
||||
);
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* XML sitemap operation callback; regenerate sitemap files using the batch API.
|
||||
*
|
||||
* @param $smids
|
||||
* An array of XML sitemap IDs.
|
||||
*
|
||||
* @see xmlsitemap_regenerate_batch()
|
||||
*/
|
||||
function xmlsitemap_sitemap_multiple_update(array $smids) {
|
||||
module_load_include('generate.inc', 'xmlsitemap');
|
||||
$batch = xmlsitemap_regenerate_batch($smids);
|
||||
batch_set($batch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_query_TAG_alter().
|
||||
*/
|
||||
function xmlsitemap_query_xmlsitemap_link_bundle_access_alter(QueryAlterableInterface $query) {
|
||||
if ($query instanceof EntityFieldQuery && $entity = $query->getMetaData('entity')) {
|
||||
$info = $query->getMetaData('entity_info');
|
||||
$bundle = $query->getMetaData('bundle');
|
||||
if (empty($bundle)) {
|
||||
$bundle = xmlsitemap_get_link_type_enabled_bundles($entity);
|
||||
}
|
||||
$query->entityCondition('bundle', $bundle, is_array($bundle) ? 'IN' : '=');
|
||||
}
|
||||
}
|
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Administrative page callbacks for the xmlsitemap_custom module.
|
||||
*/
|
||||
|
||||
function xmlsitemap_custom_list_links() {
|
||||
$header = array(
|
||||
'loc' => array('data' => t('Location'), 'field' => 'loc', 'sort' => 'asc'),
|
||||
'priority' => array('data' => t('Priority'), 'field' => 'priority'),
|
||||
'changefreq' => array('data' => t('Change frequency'), 'field' => 'changefreq'),
|
||||
'language' => array('data' => t('Language'), 'field' => 'language'),
|
||||
'operations' => array('data' => t('Operations')),
|
||||
);
|
||||
// Do not include the language column if locale is disabled.
|
||||
if (!module_exists('locale')) {
|
||||
unset($header['language']);
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
$destination = drupal_get_destination();
|
||||
|
||||
$query = db_select('xmlsitemap');
|
||||
$query->fields('xmlsitemap');
|
||||
$query->condition('type', 'custom');
|
||||
$query->extend('PagerDefault')->limit(50);
|
||||
$query->extend('TableSort')->orderByHeader($header);
|
||||
$result = $query->execute();
|
||||
|
||||
foreach ($result as $link) {
|
||||
$row = array();
|
||||
$row['loc'] = l($link->loc, $link->loc);
|
||||
$row['priority'] = number_format($link->priority, 1);
|
||||
$row['changefreq'] = $link->changefreq ? drupal_ucfirst(xmlsitemap_get_changefreq($link->changefreq)) : t('None');
|
||||
if (isset($header['language'])) {
|
||||
$row['language'] = module_invoke('locale', 'language_name', $link->language);
|
||||
}
|
||||
$operations = array();
|
||||
$operations['edit'] = xmlsitemap_get_operation_link('admin/config/search/xmlsitemap/custom/edit/' . $link->id, array('title' => t('Edit'), 'modal' => TRUE));
|
||||
$operations['delete'] = xmlsitemap_get_operation_link('admin/config/search/xmlsitemap/custom/delete/' . $link->id, array('title' => t('Delete'), 'modal' => TRUE));
|
||||
$row['operations'] = array(
|
||||
'data' => array(
|
||||
'#theme' => 'links',
|
||||
'#links' => $operations,
|
||||
'#attributes' => array('class' => array('links', 'inline')),
|
||||
),
|
||||
);
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
// @todo Convert to tableselect
|
||||
$build['xmlsitemap_custom_table'] = array(
|
||||
'#theme' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#empty' => t('No custom links available.') . ' ' . l(t('Add custom link'), 'admin/config/search/xmlsitemap/custom/add', array('query' => $destination)),
|
||||
);
|
||||
$build['xmlsitemap_custom_pager'] = array('#theme' => 'pager');
|
||||
return $build;
|
||||
}
|
||||
|
||||
function xmlsitemap_custom_edit_link_form($form, &$form_state, $link = array()) {
|
||||
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
|
||||
_xmlsitemap_set_breadcrumb('admin/config/search/xmlsitemap/custom');
|
||||
|
||||
$link += array(
|
||||
'id' => db_query("SELECT MAX(id) FROM {xmlsitemap} WHERE type = 'custom'")->fetchField() + 1,
|
||||
'loc' => '',
|
||||
'priority' => XMLSITEMAP_PRIORITY_DEFAULT,
|
||||
'lastmod' => 0,
|
||||
'changefreq' => 0,
|
||||
'changecount' => 0,
|
||||
'language' => LANGUAGE_NONE,
|
||||
);
|
||||
|
||||
$form['type'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => 'custom',
|
||||
);
|
||||
$form['id'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $link['id'],
|
||||
);
|
||||
$form['loc'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Path to link'),
|
||||
'#field_prefix' => url('', array('absolute' => TRUE)),
|
||||
'#default_value' => $link['loc'] ? drupal_get_path_alias($link['loc'], $link['language']) : '',
|
||||
'#required' => TRUE,
|
||||
'#size' => 30,
|
||||
);
|
||||
$form['priority'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Priority'),
|
||||
'#options' => xmlsitemap_get_priority_options(),
|
||||
'#default_value' => number_format($link['priority'], 1),
|
||||
'#description' => t('The priority of this URL relative to other URLs on your site.'),
|
||||
);
|
||||
$form['changefreq'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Change frequency'),
|
||||
'#options' => array(0 => t('None')) + xmlsitemap_get_changefreq_options(),
|
||||
'#default_value' => $link['changefreq'],
|
||||
'#description' => t('How frequently the page is likely to change. This value provides general information to search engines and may not correlate exactly to how often they crawl the page.'),
|
||||
);
|
||||
$languages = module_exists('locale') ? locale_language_list() : array();
|
||||
$form['language'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Language'),
|
||||
'#default_value' => $link['language'],
|
||||
'#options' => array(LANGUAGE_NONE => t('Language neutral')) + $languages,
|
||||
'#access' => $languages,
|
||||
);
|
||||
|
||||
$form['actions'] = array(
|
||||
'#type' => 'actions'
|
||||
);
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save'),
|
||||
'#weight' => 5,
|
||||
);
|
||||
$form['actions']['cancel'] = array(
|
||||
'#markup' => l(t('Cancel'), 'admin/config/search/xmlsitemap/custom'),
|
||||
'#weight' => 10,
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
function xmlsitemap_custom_edit_link_form_validate($form, &$form_state) {
|
||||
$link = &$form_state['values'];
|
||||
|
||||
// Make sure we trim and normalize the path first.
|
||||
$link['loc'] = trim($link['loc']);
|
||||
$link['loc'] = drupal_get_normal_path($link['loc'], $link['language']);
|
||||
|
||||
// Test anonymous user access to the custom link paths.
|
||||
xmlsitemap_switch_user(0);
|
||||
$menu_item = menu_get_item($link['loc']);
|
||||
xmlsitemap_restore_user();
|
||||
|
||||
// Since the menu item access results are cached, manually check the current path.
|
||||
if ($menu_item && strpos($link['loc'], 'admin/config/search/xmlsitemap/custom') === 0 && !user_access('administer xmlsitemap', drupal_anonymous_user())) {
|
||||
$menu_item['access'] = FALSE;
|
||||
}
|
||||
|
||||
if (db_query_range("SELECT 1 FROM {xmlsitemap} WHERE type <> 'custom' AND loc = :loc AND status = 1 AND access = 1 AND language IN (:languages)", 0, 1, array(':loc' => $link['loc'], ':languages' => array(LANGUAGE_NONE, $link['language'])))->fetchField()) {
|
||||
form_set_error('loc', t('There is already an existing link in the sitemap with the path %link.', array('%link' => $link['loc'])));
|
||||
}
|
||||
elseif (empty($menu_item['access']) && !is_readable('./' . $link['loc'])) {
|
||||
// @todo Harden this file exists check to make sure we can't link to files
|
||||
// like .htaccess.
|
||||
form_set_error('loc', t('The custom link %link is either invalid or it cannot be accessed by anonymous users.', array('%link' => $link['loc'])));
|
||||
}
|
||||
}
|
||||
|
||||
function xmlsitemap_custom_edit_link_form_submit($form, &$form_state) {
|
||||
$link = $form_state['values'];
|
||||
xmlsitemap_link_save($link);
|
||||
drupal_set_message(t('The custom link for %loc was saved.', array('%loc' => $link['loc'])));
|
||||
$form_state['redirect'] = 'admin/config/search/xmlsitemap/custom';
|
||||
}
|
||||
|
||||
function xmlsitemap_custom_delete_link_form($form, &$form_state, array $link) {
|
||||
// @todo Remove when http://drupal.org/node/576290 is fixed.
|
||||
_xmlsitemap_set_breadcrumb('admin/config/search/xmlsitemap/custom');
|
||||
|
||||
$form['#link'] = $link;
|
||||
$form['id'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $link['id'],
|
||||
);
|
||||
$form['link'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $link,
|
||||
);
|
||||
|
||||
return confirm_form(
|
||||
$form,
|
||||
t('Are you sure you want to delete the custom link for %loc?', array('%loc' => $link['loc'])),
|
||||
'admin/config/search/xmlsitemap/custom',
|
||||
t('This action cannot be undone.'),
|
||||
t('Delete'),
|
||||
t('Cancel')
|
||||
);
|
||||
}
|
||||
|
||||
function xmlsitemap_custom_delete_link_form_submit($form, &$form_state) {
|
||||
$link = $form_state['values']['link'];
|
||||
xmlsitemap_link_delete('custom', $link['id']);
|
||||
drupal_set_message(t('The custom link for %loc has been deleted.', array('%loc' => $link['loc'])));
|
||||
watchdog('xmlsitemap', 'The custom link for %loc has been deleted.', array('%loc' => $link['loc']), WATCHDOG_NOTICE);
|
||||
$form_state['redirect'] = 'admin/config/search/xmlsitemap/custom';
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
name = XML sitemap custom
|
||||
description = Adds user configurable links to the sitemap.
|
||||
package = XML sitemap
|
||||
core = 7.x
|
||||
dependencies[] = xmlsitemap
|
||||
files[] = xmlsitemap_custom.module
|
||||
files[] = xmlsitemap_custom.admin.inc
|
||||
files[] = xmlsitemap_custom.install
|
||||
files[] = xmlsitemap_custom.test
|
||||
configure = admin/config/search/xmlsitemap/custom
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-08
|
||||
version = "7.x-2.0-rc2+0-dev"
|
||||
core = "7.x"
|
||||
project = "xmlsitemap"
|
||||
datestamp = "1354931808"
|
||||
|
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install and uninstall schema and functions for the xmlsitemap_custom module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function xmlsitemap_custom_uninstall() {
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
xmlsitemap_link_delete_multiple(array('type' => 'custom'));
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function xmlsitemap_custom_menu() {
|
||||
$items['admin/config/search/xmlsitemap/custom'] = array(
|
||||
'title' => 'Custom links',
|
||||
'page callback' => 'xmlsitemap_custom_list_links',
|
||||
'access arguments' => array('administer xmlsitemap'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'xmlsitemap_custom.admin.inc',
|
||||
);
|
||||
$items['admin/config/search/xmlsitemap/custom/add'] = array(
|
||||
'title' => 'Add custom link',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('xmlsitemap_custom_edit_link_form'),
|
||||
'access arguments' => array('administer xmlsitemap'),
|
||||
'type' => MENU_LOCAL_ACTION,
|
||||
'file' => 'xmlsitemap_custom.admin.inc',
|
||||
'modal' => TRUE,
|
||||
);
|
||||
$items['admin/config/search/xmlsitemap/custom/edit/%xmlsitemap_custom'] = array(
|
||||
'title' => 'Edit custom link',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('xmlsitemap_custom_edit_link_form', 6),
|
||||
'access arguments' => array('administer xmlsitemap'),
|
||||
'file' => 'xmlsitemap_custom.admin.inc',
|
||||
'modal' => TRUE,
|
||||
);
|
||||
$items['admin/config/search/xmlsitemap/custom/delete/%xmlsitemap_custom'] = array(
|
||||
'title' => 'Delete custom link',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('xmlsitemap_custom_delete_link_form', 6),
|
||||
'access arguments' => array('administer xmlsitemap'),
|
||||
'file' => 'xmlsitemap_custom.admin.inc',
|
||||
'modal' => TRUE,
|
||||
);
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu load callback; load a custom sitemap link from the {xmlsitemap} table.
|
||||
*
|
||||
* @param $id
|
||||
* The sitemap link ID of the custom link to load.
|
||||
*
|
||||
* @see xmlsitemap_link_load()
|
||||
*/
|
||||
function xmlsitemap_custom_load($id) {
|
||||
return xmlsitemap_link_load('custom', $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_link_info().
|
||||
*/
|
||||
function xmlsitemap_custom_xmlsitemap_link_info() {
|
||||
return array(
|
||||
'custom' => array(
|
||||
'label' => t('Custom links'),
|
||||
),
|
||||
);
|
||||
}
|
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Unit tests for the xmlsitemap_custom module.
|
||||
*/
|
||||
|
||||
class XMLSitemapCustomFunctionalTest extends XMLSitemapTestHelper {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XML sitemap custom interface tests',
|
||||
'description' => 'Functional tests for the XML sitemap custom module.',
|
||||
'group' => 'XML sitemap',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp($modules = array()) {
|
||||
$modules[] = 'xmlsitemap_custom';
|
||||
$modules[] = 'path';
|
||||
parent::setUp($modules);
|
||||
|
||||
$this->admin_user = $this->drupalCreateUser(array('access content', 'administer xmlsitemap'));
|
||||
$this->drupalLogin($this->admin_user);
|
||||
}
|
||||
|
||||
function testCustomLinks() {
|
||||
// Set a path alias for the node page.
|
||||
$alias = array('source' => 'system/files', 'alias' => 'public-files');
|
||||
path_save($alias);
|
||||
|
||||
$this->drupalGet('admin/config/search/xmlsitemap/custom');
|
||||
$this->clickLink(t('Add custom link'));
|
||||
|
||||
// Test an invalid path.
|
||||
$edit['loc'] = 'invalid-testing-path';
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('The custom link @link is either invalid or it cannot be accessed by anonymous users.', array('@link' => $edit['loc'])));
|
||||
$this->assertNoSitemapLink(array('type' => 'custom', 'loc' => $edit['loc']));
|
||||
|
||||
// Test a path not accessible to anonymous user.
|
||||
$edit['loc'] = 'admin/people/people';
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('The custom link @link is either invalid or it cannot be accessed by anonymous users.', array('@link' => $edit['loc'])));
|
||||
$this->assertNoSitemapLink(array('type' => 'custom', 'loc' => $edit['loc']));
|
||||
|
||||
// Test that the current page, which should not give a false positive for
|
||||
// $menu_item['access'] since the result has been cached already.
|
||||
$edit['loc'] = 'admin/config/search/xmlsitemap/custom/add';
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('The custom link @link is either invalid or it cannot be accessed by anonymous users.', array('@link' => $edit['loc'])));
|
||||
$this->assertNoSitemapLink(array('type' => 'custom', 'loc' => $edit['loc']));
|
||||
|
||||
// Add an aliased path with padded spaces.
|
||||
$edit['loc'] = ' public-files ';
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertText('The custom link for system/files was saved');
|
||||
$links = xmlsitemap_link_load_multiple(array('type' => 'custom', 'loc' => 'system/files'));
|
||||
$this->assertEqual(count($links), 1, t('Custom link saved in the database.'));
|
||||
$link = reset($links);
|
||||
$this->assertSitemapLinkValues('custom', $link['id'], array('priority' => 0.5, 'changefreq' => 0, 'access' => 1, 'status' => 1));
|
||||
|
||||
$this->clickLink('Edit');
|
||||
$edit = array(
|
||||
'priority' => 0.1,
|
||||
'changefreq' => XMLSITEMAP_FREQUENCY_ALWAYS,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertText('The custom link for system/files was saved');
|
||||
$this->assertSitemapLinkValues('custom', $link['id'], array('priority' => 0.1, 'changefreq' => XMLSITEMAP_FREQUENCY_ALWAYS, 'access' => 1, 'status' => 1));
|
||||
|
||||
$this->clickLink('Delete');
|
||||
$this->drupalPost(NULL, array(), t('Delete'));
|
||||
$this->assertText('The custom link for system/files has been deleted.');
|
||||
$this->assertNoSitemapLink(array('type' => 'custom', 'loc' => 'system/files'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding files as custom links.
|
||||
*/
|
||||
function testCustomFileLinks() {
|
||||
// Test an invalid file.
|
||||
$edit['loc'] = $this->randomName();
|
||||
$this->drupalPost('admin/config/search/xmlsitemap/custom/add', $edit, t('Save'));
|
||||
$this->assertText(t('The custom link @link is either invalid or it cannot be accessed by anonymous users.', array('@link' => $edit['loc'])));
|
||||
$this->assertNoSitemapLink(array('type' => 'custom', 'loc' => $edit['loc']));
|
||||
|
||||
// Test an unaccessible file .
|
||||
//$edit['loc'] = '.htaccess';
|
||||
//$this->drupalPost('admin/config/search/xmlsitemap/custom/add', $edit, t('Save'));
|
||||
//$this->assertText(t('The custom link @link is either invalid or it cannot be accessed by anonymous users.', array('@link' => $edit['loc'])));
|
||||
//$this->assertNoSitemapLink(array('type' => 'custom', 'loc' => $edit['loc']));
|
||||
|
||||
// Test a valid file.
|
||||
$edit['loc'] = 'misc/drupal.js';
|
||||
$this->drupalPost('admin/config/search/xmlsitemap/custom/add', $edit, t('Save'));
|
||||
$this->assertText('The custom link for ' . $edit['loc'] . ' was saved');
|
||||
$links = xmlsitemap_link_load_multiple(array('type' => 'custom', 'loc' => $edit['loc']));
|
||||
$this->assertEqual(count($links), 1, t('Custom link saved in the database.'));
|
||||
|
||||
// Test a valid folder.
|
||||
$edit['loc'] = 'misc';
|
||||
$this->drupalPost('admin/config/search/xmlsitemap/custom/add', $edit, t('Save'));
|
||||
$this->assertText('The custom link for ' . $edit['loc'] . ' was saved');
|
||||
$links = xmlsitemap_link_load_multiple(array('type' => 'custom', 'loc' => $edit['loc']));
|
||||
$this->assertEqual(count($links), 1, t('Custom link saved in the database.'));
|
||||
}
|
||||
}
|
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Tests for the xmlsitemap_engines module.
|
||||
*/
|
||||
|
||||
class XMLSitemapEnginesFunctionalTest extends XMLSitemapTestHelper {
|
||||
protected $submit_url;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XML sitemap engines functional tests',
|
||||
'description' => 'Functional tests for the XML sitemap engines module.',
|
||||
'group' => 'XML sitemap',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp($modules = array()) {
|
||||
$modules[] = 'xmlsitemap_engines';
|
||||
$modules[] = 'xmlsitemap_engines_test';
|
||||
parent::setUp($modules);
|
||||
|
||||
$this->admin_user = $this->drupalCreateUser(array('access content', 'administer xmlsitemap'));
|
||||
$this->drupalLogin($this->admin_user);
|
||||
|
||||
// @todo For some reason the test client does not have clean URLs while
|
||||
// the test runner does, so it causes mismatches in watchdog assertions
|
||||
// later.
|
||||
variable_set('clean_url', 0);
|
||||
|
||||
$this->submit_url = url('ping', array('absolute' => TRUE, 'query' => array('sitemap' => ''))) . '[sitemap]';
|
||||
}
|
||||
|
||||
function submitEngines() {
|
||||
variable_set('xmlsitemap_engines_submit_last', REQUEST_TIME - 10000);
|
||||
variable_set('xmlsitemap_generated_last', REQUEST_TIME - 100);
|
||||
variable_set('xmlsitemap_engines_minimum_lifetime', 0);
|
||||
xmlsitemap_engines_cron();
|
||||
$this->assertTrue(variable_get('xmlsitemap_engines_submit_last', 0) > (REQUEST_TIME - 100), 'Submitted the sitemaps to search engines.');
|
||||
}
|
||||
|
||||
function testPrepareURL() {
|
||||
$sitemap = 'http://example.com/sitemap.xml';
|
||||
$input = 'http://example.com/ping?sitemap=[sitemap]&foo=bar';
|
||||
$output = 'http://example.com/ping?sitemap=http://example.com/sitemap.xml&foo=bar';
|
||||
$this->assertEqual(xmlsitemap_engines_prepare_url($input, $sitemap), $output);
|
||||
}
|
||||
|
||||
function testSubmitSitemaps() {
|
||||
$sitemaps = array();
|
||||
$sitemap = new stdClass();
|
||||
$sitemap->uri = array(
|
||||
'path' => 'http://example.com/sitemap.xml',
|
||||
'options' => array(),
|
||||
);
|
||||
$sitemaps[] = $sitemap;
|
||||
$sitemap = new stdClass();
|
||||
$sitemap->uri = array(
|
||||
'path' => 'http://example.com/sitemap-2.xml',
|
||||
'options' => array(),
|
||||
);
|
||||
$sitemaps[] = $sitemap;
|
||||
xmlsitemap_engines_submit_sitemaps($this->submit_url, $sitemaps);
|
||||
|
||||
$this->assertWatchdogMessage(array('type' => 'xmlsitemap', 'message' => 'Recieved ping for @sitemap.', 'variables' => array('@sitemap' => 'http://example.com/sitemap.xml')));
|
||||
$this->assertWatchdogMessage(array('type' => 'xmlsitemap', 'message' => 'Recieved ping for @sitemap.', 'variables' => array('@sitemap' => 'http://example.com/sitemap-2.xml')));
|
||||
}
|
||||
|
||||
function testPing() {
|
||||
$edit = array('xmlsitemap_engines_engines[simpletest]' => TRUE);
|
||||
$this->drupalPost('admin/config/search/xmlsitemap/engines', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
|
||||
$this->submitEngines();
|
||||
$this->assertWatchdogMessage(array('type' => 'xmlsitemap', 'message' => 'Submitted the sitemap to %url and received response @code.'));
|
||||
$this->assertWatchdogMessage(array('type' => 'xmlsitemap', 'message' => 'Recieved ping for @sitemap.'));
|
||||
}
|
||||
|
||||
function testCustomURL() {
|
||||
$edit = array('xmlsitemap_engines_custom_urls' => 'an-invalid-url');
|
||||
$this->drupalPost('admin/config/search/xmlsitemap/engines', $edit, t('Save configuration'));
|
||||
$this->assertText('Invalid URL an-invalid-url.');
|
||||
$this->assertNoText('The configuration options have been saved.');
|
||||
|
||||
$url = url('ping', array('absolute' => TRUE));
|
||||
$edit = array('xmlsitemap_engines_custom_urls' => $url);
|
||||
$this->drupalPost('admin/config/search/xmlsitemap/engines', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
|
||||
$this->submitEngines();
|
||||
$this->assertWatchdogMessage(array('type' => 'xmlsitemap', 'message' => 'Submitted the sitemap to %url and received response @code.', 'variables' => array('%url' => $url, '@code' => '404')));
|
||||
$this->assertWatchdogMessage(array('type' => 'xmlsitemap', 'message' => 'No valid sitemap parameter provided.'));
|
||||
$this->assertWatchdogMessage(array('type' => 'page not found', 'message' => 'ping'));
|
||||
|
||||
$edit = array('xmlsitemap_engines_custom_urls' => $this->submit_url);
|
||||
$this->drupalPost('admin/config/search/xmlsitemap/engines', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
|
||||
$this->submitEngines();
|
||||
$url = xmlsitemap_engines_prepare_url($this->submit_url, url('sitemap.xml', array('absolute' => TRUE)));
|
||||
$this->assertWatchdogMessage(array('type' => 'xmlsitemap', 'message' => 'Submitted the sitemap to %url and received response @code.', 'variables' => array('%url' => $url, '@code' => '200')));
|
||||
$this->assertWatchdogMessage(array('type' => 'xmlsitemap', 'message' => 'Recieved ping for @sitemap.', 'variables' => array('@sitemap' => url('sitemap.xml', array('absolute' => TRUE)))));
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
name = XML sitemap engines test
|
||||
description = Support module for XML sitemap engines testing.
|
||||
package = Testing
|
||||
core = 7.x
|
||||
files[] = xmlsitemap_engines_test.module
|
||||
version = VERSION
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-08
|
||||
version = "7.x-2.0-rc2+0-dev"
|
||||
core = "7.x"
|
||||
project = "xmlsitemap"
|
||||
datestamp = "1354931808"
|
||||
|
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function xmlsitemap_engines_test_menu() {
|
||||
$items['ping'] = array(
|
||||
'page callback' => 'xmlsitemap_engines_test_pinged',
|
||||
'access callback' => TRUE,
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_engine_info().
|
||||
*/
|
||||
function xmlsitemap_engines_test_xmlsitemap_engine_info() {
|
||||
$engines['simpletest'] = array(
|
||||
'name' => t('SimpleTest'),
|
||||
'url' => 'http://example.com/',
|
||||
);
|
||||
return $engines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_engine_info_alter().
|
||||
*/
|
||||
function xmlsitemap_engines_test_xmlsitemap_engine_info_alter(&$engines) {
|
||||
$engines['simpletest']['url'] = url('ping', array('absolute' => TRUE, 'query' => array('sitemap' => ''))) . '[sitemap]';
|
||||
}
|
||||
|
||||
function xmlsitemap_engines_test_pinged() {
|
||||
if (empty($_GET['sitemap']) || !valid_url($_GET['sitemap'])) {
|
||||
watchdog('xmlsitemap', 'No valid sitemap parameter provided.', array(), WATCHDOG_WARNING);
|
||||
// @todo Remove this? Causes an extra watchdog error to be handled.
|
||||
return MENU_NOT_FOUND;
|
||||
}
|
||||
else {
|
||||
watchdog('xmlsitemap', 'Recieved ping for @sitemap.', array('@sitemap' => $_GET['sitemap']));
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Administrative page callbacks for the xmlsitemap_engines module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Form builder; Administration settings form.
|
||||
*/
|
||||
function xmlsitemap_engines_settings_form() {
|
||||
// Build the list of support engines for the checkboxes options.
|
||||
$engines = xmlsitemap_engines_get_engine_info();
|
||||
$engine_options = array();
|
||||
foreach ($engines as $engine => $engine_info) {
|
||||
$engine_options[$engine] = $engine_info['name'];
|
||||
}
|
||||
asort($engine_options);
|
||||
|
||||
$form['xmlsitemap_engines_engines'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Submit the sitemap to the following engines'),
|
||||
'#default_value' => variable_get('xmlsitemap_engines_engines', array()),
|
||||
'#options' => $engine_options,
|
||||
);
|
||||
$form['xmlsitemap_engines_minimum_lifetime'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Do not submit more often than every'),
|
||||
'#options' => drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 604800 * 2, 604800 * 4), 'format_interval'),
|
||||
'#default_value' => variable_get('xmlsitemap_engines_minimum_lifetime', 86400),
|
||||
);
|
||||
$form['xmlsitemap_engines_submit_updated'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Only submit if the sitemap has been updated since the last submission.'),
|
||||
'#default_value' => variable_get('xmlsitemap_engines_submit_updated', TRUE),
|
||||
);
|
||||
$form['xmlsitemap_engines_custom_urls'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Custom submission URLs'),
|
||||
'#description' => t('Enter one URL per line. The token [sitemap] will be replaced with the URL to your sitemap. For example: %example-before would become %example-after.', array('%example-before' => 'http://example.com/ping?[sitemap]', '%example-after' => xmlsitemap_engines_prepare_url('http://example.com/ping?[sitemap]', url('sitemap.xml', array('absolute' => TRUE))))),
|
||||
'#default_value' => variable_get('xmlsitemap_engines_custom_urls', ''),
|
||||
'#rows' => 2,
|
||||
'#wysiwyg' => FALSE,
|
||||
'#element_validate' => array('xmlsitemap_engines_validate_custom_urls'),
|
||||
);
|
||||
|
||||
// Ensure the xmlsitemap_engines variable gets filterd to a simple array.
|
||||
$form['array_filter'] = array('#type' => 'value', '#value' => TRUE);
|
||||
|
||||
return system_settings_form($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the custom submission URL element.
|
||||
*/
|
||||
function xmlsitemap_engines_validate_custom_urls($element, &$form_state) {
|
||||
$custom_urls = preg_split('/[\r\n]+/', $element['#value'], -1, PREG_SPLIT_NO_EMPTY);
|
||||
foreach ($custom_urls as $custom_url) {
|
||||
$url = xmlsitemap_engines_prepare_url($custom_url, '');
|
||||
if (!valid_url($url, TRUE)) {
|
||||
form_error($element, t('Invalid URL %url.', array('%url' => $custom_url)));
|
||||
}
|
||||
}
|
||||
$form_state['values']['xmlsitemap_engines_custom_urls'] = implode("\n", $custom_urls);
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the XML sitemap engines module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provide a list of supported sitemap engines.
|
||||
*/
|
||||
function hook_xmlsitemap_engine_info() {
|
||||
$engines['example'] = array(
|
||||
'name' => t('Example search engine'),
|
||||
'url' => 'http://example.com/ping?sitemap=[sitemap]'
|
||||
);
|
||||
return $engines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the list of sitemap engines.
|
||||
*/
|
||||
function hook_xmlsitemap_engine_info_alter(&$engines) {
|
||||
$engines['example']['name'] = t('Kitten Search');
|
||||
$engines['example']['url'] = 'http://kittens.com/ping?sitemap=[sitemap]';
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
@@ -0,0 +1,18 @@
|
||||
name = XML sitemap engines
|
||||
description = Submit the sitemap to search engines.
|
||||
package = XML sitemap
|
||||
core = 7.x
|
||||
dependencies[] = xmlsitemap
|
||||
files[] = xmlsitemap_engines.module
|
||||
files[] = xmlsitemap_engines.admin.inc
|
||||
files[] = xmlsitemap_engines.install
|
||||
files[] = tests/xmlsitemap_engines.test
|
||||
recommends[] = site_verify
|
||||
configure = admin/config/search/xmlsitemap/engines
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-08
|
||||
version = "7.x-2.0-rc2+0-dev"
|
||||
core = "7.x"
|
||||
project = "xmlsitemap"
|
||||
datestamp = "1354931808"
|
||||
|
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the xmlsitemap_engines module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function xmlsitemap_engines_install() {
|
||||
// Set this module's weight to 1 so xmlsitemap_engines_cron() runs after
|
||||
// the sitemap has been generated in xmlsitemap_cron().
|
||||
db_update('system')
|
||||
->fields(array('weight' => 2))
|
||||
->condition('type', 'module')
|
||||
->condition('name', 'xmlsitemap_engines')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function xmlsitemap_engines_uninstall() {
|
||||
variable_del('xmlsitemap_engines_engines');
|
||||
variable_del('xmlsitemap_engines_custom_urls');
|
||||
variable_del('xmlsitemap_engines_minimum_lifetime');
|
||||
variable_del('xmlsitemap_engines_submit_last');
|
||||
variable_del('xmlsitemap_engines_submit_updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecate support for Ask.com, Moreover, and Yahoo! search engines.
|
||||
*/
|
||||
function xmlsitemap_engines_update_6202() {
|
||||
$engines = variable_get('xmlsitemap_engines_engines', array());
|
||||
$removed = array(
|
||||
'ask' => 'Ask.com',
|
||||
'moreover' => 'Moreover',
|
||||
'yahoo' => 'Yahoo.com',
|
||||
);
|
||||
$engines = array_diff($engines, array_keys($removed));
|
||||
variable_set('xmlsitemap_engines_engines', $engines);
|
||||
return t('The following search engines have deprecated their XML sitemap ping services and have been disabled: !list.', array('!list' => implode(', ', $removed)));
|
||||
}
|
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_hook_info().
|
||||
*/
|
||||
function xmlsitemap_engines_hook_info() {
|
||||
$hooks['xmlsitemap_engine_info'] = array(
|
||||
'group' => 'xmlsitemap',
|
||||
);
|
||||
$hooks['xmlsitemap_engine_info_alter'] = array(
|
||||
'group' => 'xmlsitemap',
|
||||
);
|
||||
return $hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function xmlsitemap_engines_help($path, $arg) {
|
||||
$output = '';
|
||||
switch ($path) {
|
||||
case 'admin/config/search/xmlsitemap/engines':
|
||||
if (!module_exists('site_verify')) {
|
||||
$output .= '<p>' . t('In order to verify site ownership with the search engines listed below, it is highly recommended to download and install the <a href="@site-verify">Site verification module</a>.', array('@site-verify' => 'http://drupal.org/project/site_verify')) . '</p>';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function xmlsitemap_engines_menu() {
|
||||
$items['admin/config/search/xmlsitemap/engines'] = array(
|
||||
'title' => 'Search Engines',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('xmlsitemap_engines_settings_form'),
|
||||
'access arguments' => array('administer xmlsitemap'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'xmlsitemap_engines.admin.inc',
|
||||
);
|
||||
//$items['admin/config/search/xmlsitemap/engines/submit'] = array(
|
||||
// 'page callback' => 'xmlsitemap_engines_submit',
|
||||
// 'access callback' => 'xmlsitemap_engines_submit_access',
|
||||
// 'type' => MENU_CALLBACK,
|
||||
//);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_cron().
|
||||
*/
|
||||
function xmlsitemap_engines_cron() {
|
||||
if (xmlsitemap_engines_submit_access()) {
|
||||
xmlsitemap_engines_submit_engines();
|
||||
}
|
||||
}
|
||||
|
||||
function xmlsitemap_engines_can_submit() {
|
||||
// Skip if the site is offline since search engines will not be able to
|
||||
// access the site's content.
|
||||
if (variable_get('maintenance_mode', 0) || defined('MAINTENANCE_MODE')) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!variable_get('xmlsitemap_engines_engines', array()) && !variable_get('xmlsitemap_engines_custom_urls', '')) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
function xmlsitemap_engines_submit_access() {
|
||||
if (!xmlsitemap_engines_can_submit()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Allow manual submissions to run.
|
||||
//if ($_GET['q'] == 'admin/config/search/xmlsitemap/engines/submit' && user_access('administer xmlsitemap')) {
|
||||
// return TRUE;
|
||||
//}
|
||||
|
||||
$submit_updated = variable_get('xmlsitemap_engines_submit_updated', TRUE);
|
||||
$submitted_last = variable_get('xmlsitemap_engines_submit_last', 0);
|
||||
$minimum_lifetime = variable_get('xmlsitemap_engines_minimum_lifetime', 86400);
|
||||
|
||||
// Skip if sitemap data has not been updated since last submission.
|
||||
if ($submit_updated && variable_get('xmlsitemap_generated_last', 0) <= $submitted_last) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Skip if the time since last submission is less than the minimum lifetime.
|
||||
if ((REQUEST_TIME - $submitted_last) < $minimum_lifetime) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit the sitemaps to all the specified search engines.
|
||||
*
|
||||
* @param $smids
|
||||
* An optional array of XML sitemap IDs. If not provided, it will load all
|
||||
* existing XML sitemaps.
|
||||
*/
|
||||
function xmlsitemap_engines_submit_engines(array $smids = array()) {
|
||||
if (empty($smids)) {
|
||||
$smids = FALSE;
|
||||
}
|
||||
|
||||
$sitemaps = xmlsitemap_sitemap_load_multiple($smids);
|
||||
$engines = variable_get('xmlsitemap_engines_engines', array());
|
||||
$engine_info = xmlsitemap_engines_get_engine_info();
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
if (isset($engine_info[$engine]['url'])) {
|
||||
xmlsitemap_engines_submit_sitemaps($engine_info[$engine]['url'], $sitemaps);
|
||||
}
|
||||
}
|
||||
|
||||
$custom_urls = variable_get('xmlsitemap_engines_custom_urls', '');
|
||||
$custom_urls = preg_split('/[\r\n]+/', $custom_urls, -1, PREG_SPLIT_NO_EMPTY);
|
||||
foreach ($custom_urls as $custom_url) {
|
||||
xmlsitemap_engines_submit_sitemaps($custom_url, $sitemaps);
|
||||
}
|
||||
|
||||
variable_set('xmlsitemap_engines_submit_last', REQUEST_TIME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit the sitemaps to a specific URL.
|
||||
*
|
||||
* @param $url
|
||||
* The URL for sitemap submission.
|
||||
* @param $sitemaps
|
||||
* An array of URLs of the sitemaps to submit.
|
||||
*/
|
||||
function xmlsitemap_engines_submit_sitemaps($url, array $sitemaps) {
|
||||
foreach ($sitemaps as $sitemap) {
|
||||
$sitemap->url = url($sitemap->uri['path'], $sitemap->uri['options']);
|
||||
$submit_url = xmlsitemap_engines_prepare_url($url, $sitemap->url);
|
||||
$request = drupal_http_request($submit_url);
|
||||
watchdog('xmlsitemap', 'Submitted the sitemap to %url and received response @code.', array('%url' => $submit_url, '@code' => $request->code));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace valid tokens in the URL with their appropriate values.
|
||||
*
|
||||
* @param $url
|
||||
* An un-tokenized URL.
|
||||
* @return
|
||||
* A tokenized URL.
|
||||
*/
|
||||
function xmlsitemap_engines_prepare_url($url, $sitemap) {
|
||||
return str_replace('[sitemap]', $sitemap, $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about supported search engines.
|
||||
*
|
||||
* @param $engine
|
||||
* (optional) The engine to return information for. If omitted, information
|
||||
* for all engines is returned.
|
||||
* @param $reset
|
||||
* (optional) Boolean whether to reset the static cache and do nothing. Only
|
||||
* used for tests.
|
||||
*
|
||||
* @see hook_xmlsitemap_engines_info()
|
||||
* @see hook_xmlsitemap_engines_info_alter()
|
||||
*/
|
||||
function xmlsitemap_engines_get_engine_info($engine = NULL) {
|
||||
global $language;
|
||||
$engines = &drupal_static(__FUNCTION__);
|
||||
|
||||
if (!isset($engines)) {
|
||||
if ($cached = cache_get('xmlsitemap:engines:' . $language->language)) {
|
||||
$engines = $cached->data;
|
||||
}
|
||||
else {
|
||||
// Fetch the results of all hook_xmlsitemap_engine_info() implementations.
|
||||
$engines = module_invoke_all('xmlsitemap_engine_info');
|
||||
// Allow other modulse to alter the engine info.
|
||||
drupal_alter('xmlsitemap_engine_info', $engines);
|
||||
// Cache by language since engine names are translated.
|
||||
cache_set('xmlsitemap:engines:' . $language->language, $engines);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($engine)) {
|
||||
return isset($engines[$engine]) ? $engines[$engine] : NULL;
|
||||
}
|
||||
else {
|
||||
return $engines;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_engine_info().
|
||||
*/
|
||||
function xmlsitemap_engines_xmlsitemap_engine_info() {
|
||||
$engines['google'] = array(
|
||||
'name' => t('Google'),
|
||||
'url' => 'http://www.google.com/webmasters/tools/ping?sitemap=[sitemap]',
|
||||
'help url' => 'http://www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156184',
|
||||
);
|
||||
$engines['bing'] = array(
|
||||
'name' => t('Bing'),
|
||||
'url' => 'http://www.bing.com/webmaster/ping.aspx?siteMap=[sitemap]',
|
||||
'help url' => 'http://www.bing.com/webmaster',
|
||||
);
|
||||
return $engines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_variables().
|
||||
*/
|
||||
function xmlsitemap_engines_variables() {
|
||||
$variables = array(
|
||||
'xmlsitemap_engines_engines' => array(),
|
||||
'xmlsitemap_engines_custom_urls' => '',
|
||||
'xmlsitemap_engines_minimum_lifetime' => 86400,
|
||||
'xmlsitemap_engines_submit_last' => 0,
|
||||
'xmlsitemap_engines_submit_updated' => TRUE,
|
||||
);
|
||||
return $variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_sitemap_operations().
|
||||
*/
|
||||
function xmlsitemap_engines_xmlsitemap_sitemap_operations() {
|
||||
if (xmlsitemap_engines_can_submit()) {
|
||||
$operations['xmlsitemap_engines_submit'] = array(
|
||||
'label' => t('Submit to search engines'),
|
||||
'action past' => t('Submitted'),
|
||||
'callback' => 'xmlsitemap_engines_submit_engines',
|
||||
);
|
||||
return $operations;
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
name = XML sitemap internationalization
|
||||
description = Enables multilingual XML sitemaps.
|
||||
package = XML sitemap
|
||||
core = 7.x
|
||||
dependencies[] = xmlsitemap
|
||||
dependencies[] = i18n
|
||||
files[] = xmlsitemap_i18n.module
|
||||
files[] = xmlsitemap_i18n.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-08
|
||||
version = "7.x-2.0-rc2+0-dev"
|
||||
core = "7.x"
|
||||
project = "xmlsitemap"
|
||||
datestamp = "1354931808"
|
||||
|
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_context_info().
|
||||
*/
|
||||
function xmlsitemap_i18n_xmlsitemap_context_info() {
|
||||
$context['language'] = array(
|
||||
'label' => t('Language'),
|
||||
'summary callback' => 'locale_language_name',
|
||||
'default' => language_default('language'),
|
||||
);
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_context().
|
||||
*/
|
||||
function xmlsitemap_i18n_xmlsitemap_context() {
|
||||
$context['language'] = $GLOBALS['language']->language;
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements xmlsitemap_context_url_options().
|
||||
*/
|
||||
function xmlsitemap_i18n_xmlsitemap_context_url_options(array $context) {
|
||||
$options = array();
|
||||
if (isset($context['language'])) {
|
||||
$options['language'] = xmlsitemap_language_load($context['language']);
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*/
|
||||
function xmlsitemap_i18n_form_xmlsitemap_sitemap_edit_form_alter(&$form, $form_state) {
|
||||
$form['context']['language'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Language'),
|
||||
'#options' => locale_language_list(),
|
||||
'#default_value' => isset($form['#sitemap']->context['language']) ? $form['#sitemap']->context['language'] : LANGUAGE_NONE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*
|
||||
* Set the regeneration needed flag if settings are changed.
|
||||
*/
|
||||
function xmlsitemap_form_locale_languages_overview_form_alter(&$form, $form_state) {
|
||||
array_unshift($form['#submit'], 'xmlsitemap_form_submit_flag_regenerate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*
|
||||
* Set the regeneration needed flag if multilingual settings are changed.
|
||||
*/
|
||||
function xmlsitemap_i18n_form_i18n_admin_settings_alter(&$form, $form_state) {
|
||||
array_unshift($form['#submit'], 'xmlsitemap_form_submit_flag_regenerate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_query_TAG_alter().
|
||||
*
|
||||
* @see i18n_db_rewrite_where()
|
||||
*/
|
||||
function xmlsitemap_i18n_query_xmlsitemap_generate_alter(QueryAlterableInterface $query) {
|
||||
$mode = variable_get('i18n_selection_mode', 'simple');
|
||||
$sitemap = $query->getMetaData('sitemap');
|
||||
|
||||
if (!isset($sitemap->context['language']) || $mode == 'off') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get languages to simplify query building.
|
||||
$current = $sitemap->context['language'];
|
||||
$default = language_default();
|
||||
|
||||
if ($mode == 'mixed' && $current == $default) {
|
||||
// If mode is mixed but current = default, is the same as 'simple'.
|
||||
$mode = 'simple';
|
||||
}
|
||||
|
||||
switch ($mode) {
|
||||
case 'simple':
|
||||
// Current language and language neutral.
|
||||
$query->condition('language', array($current, LANGUAGE_NONE));
|
||||
break;
|
||||
case 'mixed':
|
||||
// Mixed current language (if available) or default language (if not) and language neutral.
|
||||
$query->condition('language', array($current, $default, LANGUAGE_NONE));
|
||||
break;
|
||||
case 'default':
|
||||
// Only default language and language neutral.
|
||||
$query->condition('language', array($default, LANGUAGE_NONE));
|
||||
break;
|
||||
case 'strict':
|
||||
// Only current language (for nodes), simple for all other types.
|
||||
$node_condition = db_and();
|
||||
$node_condition->condition('type', 'node');
|
||||
$node_condition->condition('language', $current);
|
||||
$normal_condition = db_and();
|
||||
$normal_condition->condition('type', 'node', '<>');
|
||||
$normal_condition->condition('language', array($current, LANGUAGE_NONE));
|
||||
$condition = db_or();
|
||||
$condition->condition($node_condition);
|
||||
$condition->condition($normal_condition);
|
||||
$query->condition($condition);
|
||||
break;
|
||||
case 'off':
|
||||
// All content. No language conditions apply.
|
||||
break;
|
||||
}
|
||||
}
|
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Unit tests for the xmlsitemap_i18n module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Common base test class for XML sitemap internationalization tests.
|
||||
*/
|
||||
class XMLSitemapI18nWebTestCase extends XMLSitemapTestHelper {
|
||||
protected $admin_user;
|
||||
|
||||
/**
|
||||
* Set up an administrative user account and testing keys.
|
||||
*/
|
||||
function setUp($modules = array()) {
|
||||
// Call parent::setUp() allowing test cases to pass further modules.
|
||||
$modules[] = 'locale';
|
||||
$modules[] = 'translation';
|
||||
$modules[] = 'i18n';
|
||||
$modules[] = 'xmlsitemap_i18n';
|
||||
parent::setUp($modules);
|
||||
|
||||
// Add predefined language and reset the locale cache.
|
||||
require_once DRUPAL_ROOT . '/includes/locale.inc';
|
||||
locale_add_language('fr', NULL, NULL, LANGUAGE_LTR, '', 'fr');
|
||||
drupal_language_initialize();
|
||||
variable_set('language_negotiation', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX);
|
||||
|
||||
// Create the two different language-context sitemaps.
|
||||
db_query("DELETE FROM {xmlsitemap_sitemap}");
|
||||
$sitemap = new stdClass();
|
||||
$sitemap->context = array('language' => 'en');
|
||||
xmlsitemap_sitemap_save($sitemap);
|
||||
$sitemap = new stdClass();
|
||||
$sitemap->context = array('language' => 'fr');
|
||||
xmlsitemap_sitemap_save($sitemap);
|
||||
}
|
||||
}
|
||||
|
||||
class XMLSitemapI18nTest extends XMLSitemapI18nWebTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XML sitemap i18n tests',
|
||||
'description' => 'Functional and integration tests for the XML sitemap and internationalization modules.',
|
||||
'group' => 'XML sitemap',
|
||||
'dependencies' => array('i18n', 'disable-this-test-for-now'),
|
||||
);
|
||||
}
|
||||
|
||||
function testLanguageSelection() {
|
||||
// Create our three different language nodes.
|
||||
$node = $this->addSitemapLink(array('type' => 'node', 'language' => LANGUAGE_NONE));
|
||||
$node_en = $this->addSitemapLink(array('type' => 'node', 'language' => 'en'));
|
||||
$node_fr = $this->addSitemapLink(array('type' => 'node', 'language' => 'fr'));
|
||||
|
||||
// Create three non-node language nodes.
|
||||
$link = $this->addSitemapLink(array('language' => LANGUAGE_NONE));
|
||||
$link_en = $this->addSitemapLink(array('language' => 'en'));
|
||||
$link_fr = $this->addSitemapLink(array('language' => 'fr'));
|
||||
|
||||
variable_set('i18n_selection_mode', 'off');
|
||||
$this->regenerateSitemap();
|
||||
$this->drupalGetSitemap(array('language' => 'en'));
|
||||
$this->assertRawSitemapLinks($node, $node_en, $node_fr, $link, $link_en, $link_fr);
|
||||
$this->drupalGetSitemap(array('language' => 'fr'));
|
||||
$this->assertRawSitemapLinks($node, $node_en, $node_fr, $link, $link_en, $link_fr);
|
||||
|
||||
variable_set('i18n_selection_mode', 'simple');
|
||||
$this->regenerateSitemap();
|
||||
$this->drupalGetSitemap(array('language' => 'en'));
|
||||
$this->assertRawSitemapLinks($node, $node_en, $link, $link_en);
|
||||
$this->assertNoRawSitemapLinks($node_fr, $link_fr);
|
||||
$this->drupalGetSitemap(array('language' => 'fr'));
|
||||
$this->assertRawSitemapLinks($node, $node_fr, $link, $link_fr);
|
||||
$this->assertNoRawSitemapLinks($node_en, $link_en);
|
||||
|
||||
variable_set('i18n_selection_mode', 'mixed');
|
||||
$this->regenerateSitemap();
|
||||
$this->drupalGetSitemap(array('language' => 'en'));
|
||||
$this->assertRawSitemapLinks($node, $node_en, $link, $link_en);
|
||||
$this->assertNoRawSitemapLinks($node_fr, $link_fr);
|
||||
$this->drupalGetSitemap(array('language' => 'fr'));
|
||||
$this->assertRawSitemapLinks($node, $node_en, $node_fr, $link, $link_en, $link_fr);
|
||||
|
||||
variable_set('i18n_selection_mode', 'default');
|
||||
$this->regenerateSitemap();
|
||||
$this->drupalGetSitemap(array('language' => 'en'));
|
||||
$this->assertRawSitemapLinks($node, $node_en, $link, $link_en);
|
||||
$this->assertNoRawSitemapLinks($node_fr, $link_fr);
|
||||
$this->drupalGetSitemap(array('language' => 'fr'));
|
||||
$this->assertRawSitemapLinks($node, $node_en, $link, $link_en);
|
||||
$this->assertNoRawSitemapLinks($node_fr, $link_fr);
|
||||
|
||||
// With strict mode, the language neutral node should not be found, but the
|
||||
// language neutral non-node should be.
|
||||
variable_set('i18n_selection_mode', 'strict');
|
||||
$this->regenerateSitemap();
|
||||
$this->drupalGetSitemap(array('language' => 'en'));
|
||||
$this->assertRawSitemapLinks($node_en, $link, $link_en);
|
||||
$this->assertNoRawSitemapLinks($node, $node_fr, $link_fr);
|
||||
$this->drupalGetSitemap(array('language' => 'fr'));
|
||||
$this->assertRawSitemapLinks($node_fr, $link, $link_fr);
|
||||
$this->assertNoRawSitemapLinks($node, $node_en, $link_en);
|
||||
}
|
||||
}
|
||||
|
||||
class XMLSitemapI18nNodeTest extends XMLSitemapI18nWebTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XML sitemap i18n node tests',
|
||||
'description' => 'Functional and integration tests for the XML sitemap node and internationalization modules.',
|
||||
'group' => 'XML sitemap',
|
||||
'dependencies' => array('i18n', 'disable-this-test-for-now'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp($modules = array()) {
|
||||
$modules[] = 'xmlsitemap_node';
|
||||
parent::setUp($modules);
|
||||
|
||||
variable_set('language_content_type_page', 1);
|
||||
$this->admin_user = $this->drupalCreateUser(array('administer nodes'));
|
||||
$this->drupalLogin($this->admin_user);
|
||||
}
|
||||
|
||||
function testNodeLanguageData() {
|
||||
$node = $this->drupalCreateNode(array());
|
||||
|
||||
$this->drupalPost('node/' . $node->nid . '/edit', array('language' => 'en'), t('Save'));
|
||||
$link = $this->assertSitemapLink('node', $node->nid);
|
||||
$this->assertIdentical($link['language'], 'en');
|
||||
|
||||
$this->drupalPost('node/' . $node->nid . '/edit', array('language' => 'fr'), t('Save'));
|
||||
$link = $this->assertSitemapLink('node', $node->nid);
|
||||
$this->assertIdentical($link['language'], 'fr');
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
name = XML sitemap menu
|
||||
description = Adds menu item links to the sitemap.
|
||||
package = XML sitemap
|
||||
core = 7.x
|
||||
dependencies[] = xmlsitemap
|
||||
dependencies[] = menu
|
||||
files[] = xmlsitemap_menu.module
|
||||
files[] = xmlsitemap_menu.install
|
||||
files[] = xmlsitemap_menu.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-08
|
||||
version = "7.x-2.0-rc2+0-dev"
|
||||
core = "7.x"
|
||||
project = "xmlsitemap"
|
||||
datestamp = "1354931808"
|
||||
|
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install and uninstall schema and functions for the xmlsitemap_menu module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function xmlsitemap_menu_uninstall() {
|
||||
drupal_load('module', 'menu');
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
$menus = array_keys(menu_get_menus());
|
||||
foreach ($menus as $menu) {
|
||||
xmlsitemap_link_bundle_delete('menu_link', $menu);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup variables.
|
||||
*/
|
||||
function xmlsitemap_menu_update_6200() {
|
||||
drupal_load('module', 'menu');
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
$menus = array_keys(menu_get_menus());
|
||||
foreach ($menus as $menu) {
|
||||
$settings = array(
|
||||
'status' => variable_get('xmlsitemap_menu_status_' . $menu, XMLSITEMAP_STATUS_DEFAULT),
|
||||
'priority' => variable_get('xmlsitemap_menu_priority_' . $menu, XMLSITEMAP_PRIORITY_DEFAULT),
|
||||
);
|
||||
variable_set('xmlsitemap_settings_menu_' . $menu, $settings);
|
||||
variable_del('xmlsitemap_menu_status_' . $menu);
|
||||
variable_del('xmlsitemap_menu_priority_' . $menu);
|
||||
variable_del('xmlsitemap_menu_calculate_priority_' . $menu);
|
||||
}
|
||||
variable_del('xmlsitemap_menu_menus');
|
||||
variable_del('xmlsitemap_menu_calculate_priority');
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename the menu type to 'menu_link'.
|
||||
*/
|
||||
function xmlsitemap_menu_update_6201() {
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
xmlsitemap_link_type_rename('menu', 'menu_link');
|
||||
}
|
@@ -0,0 +1,280 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_entity_info_alter().
|
||||
*
|
||||
* Adds support for the menu link entity if it doesn't already exist.
|
||||
*/
|
||||
function xmlsitemap_menu_entity_info_alter(&$info) {
|
||||
if (!isset($info['menu_link'])) {
|
||||
$info['menu_link'] = array(
|
||||
'label' => t('Menu link'),
|
||||
'controller class' => 'DrupalDefaultEntityController',
|
||||
'base table' => 'menu_links',
|
||||
'uri callback' => 'xmlsitemap_menu_menu_link_uri',
|
||||
'fieldable' => FALSE,
|
||||
'static cache' => TRUE,
|
||||
'field cache' => TRUE,
|
||||
'entity keys' => array(
|
||||
'id' => 'mlid',
|
||||
'bundle' => 'menu_name',
|
||||
'label' => 'link_title',
|
||||
'revision' => '',
|
||||
),
|
||||
'load hook' => NULL,
|
||||
'view modes' => array(),
|
||||
'translation' => array(),
|
||||
'schema_fields_sql' => array(
|
||||
'base table' => drupal_schema_fields_sql('menu_links'),
|
||||
),
|
||||
'xmlsitemap' => array(
|
||||
'process callback' => 'xmlsitemap_menu_xmlsitemap_process_menu_links',
|
||||
),
|
||||
'bundle label' => t('Menu'),
|
||||
);
|
||||
|
||||
foreach (menu_get_menus() as $type => $name) {
|
||||
$info['menu_link']['bundles'][$type] = array(
|
||||
'label' => $name,
|
||||
'admin' => array(
|
||||
'path' => 'admin/structure/menu/manage/%menu/edit',
|
||||
'bundle argument' => 4,
|
||||
'real path' => 'admin/structure/menu/manage/' . $type . '/edit',
|
||||
'access arguments' => array('administer menus'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity URI callback.
|
||||
*/
|
||||
function xmlsitemap_menu_menu_link_uri($menu_item) {
|
||||
return is_array($menu_item) ? $menu_item['href'] : $menu_item->href;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_cron().
|
||||
*
|
||||
* Process old menu links not found in the {xmlsitemap} table.
|
||||
*/
|
||||
function xmlsitemap_menu_cron() {
|
||||
xmlsitemap_menu_xmlsitemap_index_links(xmlsitemap_var('batch_limit'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_index_links().
|
||||
*/
|
||||
function xmlsitemap_menu_xmlsitemap_index_links($limit) {
|
||||
if ($menus = xmlsitemap_get_link_type_enabled_bundles('menu_link')) {
|
||||
$sql = "SELECT ml.mlid FROM {menu_links} ml LEFT JOIN {xmlsitemap} x ON x.type = 'menu' AND ml.mlid = x.id WHERE x.id IS NULL AND ml.menu_name IN (:menus) ORDER BY ml.mlid DESC";
|
||||
$mlids = db_query_range($sql, 0, $limit, array(':menus' => $menus))->fetchCol();
|
||||
xmlsitemap_menu_xmlsitemap_process_menu_links($mlids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process menu sitemap links.
|
||||
*
|
||||
* @param $mlids
|
||||
* An array of menu link IDs.
|
||||
*/
|
||||
function xmlsitemap_menu_xmlsitemap_process_menu_links(array $mlids, array $xmlsitemap = array()) {
|
||||
// Set the global user variable to the anonymous user.
|
||||
xmlsitemap_switch_user(0);
|
||||
|
||||
foreach ($mlids as $mlid) {
|
||||
$menu_item = menu_link_load($mlid);
|
||||
if (empty($menu_item)) {
|
||||
continue;
|
||||
}
|
||||
if (!empty($xmlsitemap)) {
|
||||
$menu_item['xmlsitemap'] = $xmlsitemap;
|
||||
}
|
||||
$link = xmlsitemap_menu_create_link($menu_item);
|
||||
xmlsitemap_link_save($link);
|
||||
}
|
||||
|
||||
// Set the global user variable back to the original user.
|
||||
xmlsitemap_restore_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*
|
||||
* @see menu_edit_menu()
|
||||
* @see xmlsitemap_add_link_bundle_settings()
|
||||
*/
|
||||
function xmlsitemap_menu_form_menu_edit_menu_alter(&$form, $form_state) {
|
||||
$menu = isset($form['menu_name']['#default_value']) ? $form['menu_name']['#default_value'] : '';
|
||||
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
|
||||
xmlsitemap_add_link_bundle_settings($form, $form_state, 'menu_link', $menu);
|
||||
}
|
||||
|
||||
//function xmlsitemap_menu_form_menu_overview_form_alter(&$form, $form_state) {
|
||||
// $form['#submit'][] = 'xmlsitemap_menu_menu_overview_form_submit';
|
||||
//}
|
||||
//
|
||||
//function xmlsitemap_menu_menu_overview_form_submit($form, $form_state) {
|
||||
// $mlids = array();
|
||||
// foreach (element_children($form) as $mlid) {
|
||||
// if (isset($form[$mlid]['#item'])) {
|
||||
// $mlids[] = $form[$mlid]['#item']['mlid'];
|
||||
// }
|
||||
// }
|
||||
// xmlsitemap_menu_xmlsitemap_process_menu_links($mlids);
|
||||
//}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*
|
||||
* @see menu_edit_item()
|
||||
*/
|
||||
function xmlsitemap_menu_form_menu_edit_item_alter(&$form, $form_state) {
|
||||
$menu_name = $form['parent']['#default_value'];
|
||||
$menu_name = substr($menu_name, 0, strpos($menu_name, ':'));
|
||||
|
||||
// Add the link options.
|
||||
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
|
||||
xmlsitemap_add_form_link_options($form, 'menu_link', $menu_name, $form['mlid']['#value']);
|
||||
$form['xmlsitemap']['#weight'] = 30;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_insert().
|
||||
*/
|
||||
function xmlsitemap_menu_menu_insert(array $menu) {
|
||||
if (isset($menu['xmlsitemap'])) {
|
||||
xmlsitemap_link_bundle_settings_save('menu_link', $menu['menu_name'], $menu['xmlsitemap']);
|
||||
}
|
||||
|
||||
// When menus change, the bundles we defined in
|
||||
// xmlsitemap_menu_entity_info_alter() change, so we need to clear the cache.
|
||||
entity_info_cache_clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_update().
|
||||
*/
|
||||
function xmlsitemap_menu_menu_update(array $menu) {
|
||||
if (isset($menu['xmlsitemap'])) {
|
||||
xmlsitemap_link_bundle_settings_save('menu_link', $menu['menu_name'], $menu['xmlsitemap']);
|
||||
}
|
||||
|
||||
// When menus change, the bundles we defined in
|
||||
// xmlsitemap_menu_entity_info_alter() change, so we need to clear the cache.
|
||||
entity_info_cache_clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_delete().
|
||||
*/
|
||||
function xmlsitemap_menu_menu_delete(array $menu) {
|
||||
xmlsitemap_link_bundle_delete('menu_link', $menu['menu_name']);
|
||||
|
||||
// When menus change, the bundles we defined in
|
||||
// xmlsitemap_menu_entity_info_alter() change, so we need to clear the cache.
|
||||
entity_info_cache_clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_link_insert().
|
||||
*/
|
||||
function xmlsitemap_menu_menu_link_insert(array $link) {
|
||||
$link += array('xmlsitemap' => array());
|
||||
xmlsitemap_menu_xmlsitemap_process_menu_links(array($link['mlid']), $link['xmlsitemap']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_link_update().
|
||||
*
|
||||
* @see hook_menu_link_alter()
|
||||
*/
|
||||
function xmlsitemap_menu_menu_link_update(array $link) {
|
||||
//$link += array('xmlsitemap' => array());
|
||||
//xmlsitemap_menu_xmlsitemap_process_menu_links(array($link['mlid']), $link['xmlsitemap']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_link_alter().
|
||||
*
|
||||
* We have to use this hook rather than hook_menu_link_update() because this
|
||||
* hook is not always called if the user does not edit the core menu item
|
||||
* fields.
|
||||
*
|
||||
* @see http://drupal.org/node/1013856
|
||||
*/
|
||||
function xmlsitemap_menu_menu_link_alter(array &$link) {
|
||||
if (!empty($link['mlid'])) {
|
||||
$link += array('xmlsitemap' => array());
|
||||
xmlsitemap_menu_xmlsitemap_process_menu_links(array($link['mlid']), $link['xmlsitemap']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_link_delete().
|
||||
*/
|
||||
function xmlsitemap_menu_menu_link_delete(array $link) {
|
||||
xmlsitemap_link_delete('menu_link', $link['mlid']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sitemap link from a menu item.
|
||||
*
|
||||
* @param $menu_item
|
||||
* A loaded menu item.
|
||||
*/
|
||||
function xmlsitemap_menu_create_link(array $menu_item) {
|
||||
if (!isset($menu_item['xmlsitemap'])) {
|
||||
$menu_item['xmlsitemap'] = array();
|
||||
if ($menu_item['mlid'] && $link = xmlsitemap_link_load('menu_link', $menu_item['mlid'])) {
|
||||
$menu_item['xmlsitemap'] = $link;
|
||||
}
|
||||
}
|
||||
|
||||
$settings = xmlsitemap_link_bundle_load('menu_link', $menu_item['menu_name']);
|
||||
|
||||
$menu_item['xmlsitemap'] += array(
|
||||
'type' => 'menu_link',
|
||||
'id' => $menu_item['mlid'],
|
||||
'status' => $settings['status'],
|
||||
'status_default' => $settings['status'],
|
||||
'status_override' => 0,
|
||||
'priority' => $settings['priority'],
|
||||
'priority_default' => $settings['priority'],
|
||||
'priority_override' => 0,
|
||||
);
|
||||
|
||||
// The following values must always be checked because they are volatile.
|
||||
$menu_item['xmlsitemap']['loc'] = $menu_item['href'];
|
||||
$menu_item['xmlsitemap']['subtype'] = $menu_item['menu_name'];
|
||||
$menu_item['xmlsitemap']['access'] = $menu_item['access'] && !$menu_item['external'] && !$menu_item['hidden'];
|
||||
$menu_item['xmlsitemap']['language'] = isset($menu_item['options']['langcode']) ? $menu_item['options']['langcode'] : LANGUAGE_NONE;
|
||||
|
||||
return $menu_item['xmlsitemap'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the priority of a menu link based on depth and weight.
|
||||
*/
|
||||
function xmlsitemap_menu_calculate_priority(array $menu_item) {
|
||||
$priority = (MENU_MAX_DEPTH - $menu_item['depth'] + 1) / MENU_MAX_DEPTH;
|
||||
$priority -= (50 + $menu_item['weight']) / (100 * (MENU_MAX_DEPTH + 1));
|
||||
return $priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal default variables for template_var().
|
||||
*/
|
||||
function xmlsitemap_menu_variables() {
|
||||
$defaults = array();
|
||||
$menus = array_keys(menu_get_menus());
|
||||
foreach ($menus as $menu) {
|
||||
$defaults['xmlsitemap_settings_menu_' . $menu] = array(
|
||||
'status' => XMLSITEMAP_STATUS_DEFAULT,
|
||||
'priority' => XMLSITEMAP_PRIORITY_DEFAULT,
|
||||
);
|
||||
}
|
||||
return $defaults;
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Unit tests for the xmlsitemap_menu module.
|
||||
*/
|
||||
|
||||
class XMLSitemapMenuFunctionalTest extends XMLSitemapTestHelper {
|
||||
protected $normal_user;
|
||||
protected $menu_items = array();
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XML sitemap menu',
|
||||
'description' => 'Functional tests for the XML sitemap menu module.',
|
||||
'group' => 'XML sitemap',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp($modules = array()) {
|
||||
$modules[] = 'xmlsitemap_menu';
|
||||
$modules[] = 'menu';
|
||||
parent::setUp($modules);
|
||||
|
||||
$this->admin_user = $this->drupalCreateUser(array('administer menu', 'administer xmlsitemap'));
|
||||
$this->normal_user = $this->drupalCreateUser(array('access content'));
|
||||
}
|
||||
|
||||
function testMenuSettings() {
|
||||
$this->drupalLogin($this->admin_user);
|
||||
|
||||
$edit = array(
|
||||
'title' => $this->randomName(),
|
||||
'menu_name' => drupal_strtolower($this->randomName()),
|
||||
'xmlsitemap[status]' => '1',
|
||||
'xmlsitemap[priority]' => '1.0',
|
||||
);
|
||||
$this->drupalPost('admin/structure/menu/add', $edit, 'Save');
|
||||
$menu = menu_load($edit['menu_name']);
|
||||
|
||||
$this->clickLink('Add link');
|
||||
$edit = array(
|
||||
'link_title' => $this->randomName(),
|
||||
'link_path' => 'node',
|
||||
'xmlsitemap[status]' => 'default',
|
||||
'xmlsitemap[priority]' => 'default',
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, 'Save');
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
name = XML sitemap modal UI
|
||||
description = "Provides an AJAX modal UI for common XML sitemap tasks."
|
||||
package = XML sitemap
|
||||
core = 7.x
|
||||
dependencies[] = xmlsitemap
|
||||
dependencies[] = ctools
|
||||
files[] = xmlsitemap_modal.module
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-08
|
||||
version = "7.x-2.0-rc2+0-dev"
|
||||
core = "7.x"
|
||||
project = "xmlsitemap"
|
||||
datestamp = "1354931808"
|
||||
|
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_menu_alter().
|
||||
*/
|
||||
function xmlsitemap_modal_menu_alter(&$items) {
|
||||
foreach ($items as $path => $item) {
|
||||
if (!empty($item['modal']) && strpos($path, '%ctools_js') === FALSE && $item['page callback'] ==='drupal_get_form') {
|
||||
$items["$path/%ctools_js"] = $item;
|
||||
$items["$path/%ctools_js"]['page callback'] = 'xmlsitemap_modal_get_form';
|
||||
$items["$path/%ctools_js"]['page arguments'][] = substr_count($path, '/') + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a Drupal form using CTools modal or normal page display.
|
||||
*/
|
||||
function xmlsitemap_modal_get_form() {
|
||||
$args = func_get_args();
|
||||
$form_id = array_shift($args);
|
||||
$ajax = array_pop($args);
|
||||
|
||||
if ($ajax) {
|
||||
ctools_include('ajax');
|
||||
ctools_include('modal');
|
||||
|
||||
$form_state = array(
|
||||
'ajax' => TRUE,
|
||||
'args' => $args,
|
||||
);
|
||||
$commands = ctools_modal_form_wrapper($form_id, $form_state);
|
||||
|
||||
if (empty($commands)) {
|
||||
$commands[] = ctools_modal_command_loading();
|
||||
if (!empty($_GET['destination'])) {
|
||||
$commands[] = ctools_ajax_command_redirect($_GET['destination']);
|
||||
}
|
||||
}
|
||||
return ajax_render($commands);
|
||||
//return ctools_ajax_render($commands);
|
||||
}
|
||||
else {
|
||||
array_unshift($args, $form_id);
|
||||
return call_user_func_array('drupal_get_form', $args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_operation_link_alter().
|
||||
*/
|
||||
function xmlsitemap_modal_xmlsitemap_operation_link_alter(array &$link) {
|
||||
static $ctools_modal_included;
|
||||
|
||||
// Process modal frame links.
|
||||
if (!empty($link['modal'])) {
|
||||
unset($link['modal']);
|
||||
|
||||
if (!isset($ctools_modal_included)) {
|
||||
// Only process a few includes once per request.
|
||||
ctools_include('ajax');
|
||||
ctools_include('modal');
|
||||
ctools_modal_add_js();
|
||||
drupal_add_css(drupal_get_path('module', 'xmlsitemap_modal') . '/xmlsitemap_modal.css');
|
||||
}
|
||||
|
||||
$link['attributes']['class'][] = 'ctools-use-modal';
|
||||
|
||||
if (strpos($link['href'], 'nojs') === FALSE) {
|
||||
$link['href'] .= '/nojs';
|
||||
}
|
||||
else {
|
||||
$link['href'] = trim($link['href'], '/');
|
||||
}
|
||||
|
||||
// @todo Remove when http://drupal.org/node/565808 is fixed.
|
||||
if (substr($link['href'], -4) === 'nojs') {
|
||||
$link['href'] .= '/';
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
name = XML sitemap node
|
||||
description = Adds content links to the sitemap.
|
||||
package = XML sitemap
|
||||
core = 7.x
|
||||
dependencies[] = xmlsitemap
|
||||
files[] = xmlsitemap_node.module
|
||||
files[] = xmlsitemap_node.install
|
||||
files[] = xmlsitemap_node.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-08
|
||||
version = "7.x-2.0-rc2+0-dev"
|
||||
core = "7.x"
|
||||
project = "xmlsitemap"
|
||||
datestamp = "1354931808"
|
||||
|
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install and uninstall schema and functions for the xmlsitemap_node module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function xmlsitemap_node_uninstall() {
|
||||
drupal_load('module', 'node');
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
$node_types = array_keys(node_type_get_names());
|
||||
foreach ($node_types as $node_type) {
|
||||
xmlsitemap_link_bundle_delete('node', $node_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup variables.
|
||||
*/
|
||||
function xmlsitemap_node_update_6200() {
|
||||
drupal_load('module', 'node');
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
$node_types = array_keys(node_type_get_names());
|
||||
foreach ($node_types as $node_type) {
|
||||
$settings = array(
|
||||
'status' => variable_get('xmlsitemap_node_status_' . $node_type, XMLSITEMAP_STATUS_DEFAULT),
|
||||
'priority' => variable_get('xmlsitemap_node_priority_' . $node_type, XMLSITEMAP_PRIORITY_DEFAULT),
|
||||
);
|
||||
variable_set('xmlsitemap_settings_node_' . $node_type, $settings);
|
||||
variable_del('xmlsitemap_node_status_' . $node_type);
|
||||
variable_del('xmlsitemap_node_priority_' . $node_type);
|
||||
variable_del('xmlsitemap_node_update_' . $node_type);
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty update.
|
||||
*/
|
||||
function xmlsitemap_node_update_6201() {
|
||||
}
|
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_entity_info_alter().
|
||||
*/
|
||||
function xmlsitemap_node_entity_info_alter(array &$entity_info) {
|
||||
$entity_info['node']['label'] = t('Content');
|
||||
$entity_info['node']['bundle label'] = t('Content type');
|
||||
$entity_info['node']['xmlsitemap'] = array(
|
||||
'process callback' => 'xmlsitemap_node_xmlsitemap_process_node_links',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_cron().
|
||||
*
|
||||
* Process old nodes not found in the {xmlsitemap} table.
|
||||
*/
|
||||
function xmlsitemap_node_cron() {
|
||||
xmlsitemap_node_xmlsitemap_index_links(xmlsitemap_var('batch_limit'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_index_links().
|
||||
*/
|
||||
function xmlsitemap_node_xmlsitemap_index_links($limit) {
|
||||
if ($types = xmlsitemap_get_link_type_enabled_bundles('node')) {
|
||||
$nids = db_query_range("SELECT n.nid FROM {node} n LEFT JOIN {xmlsitemap} x ON x.type = 'node' AND n.nid = x.id WHERE x.id IS NULL AND n.type IN (:types) ORDER BY n.nid DESC", 0, $limit, array(':types' => $types))->fetchCol();
|
||||
xmlsitemap_node_xmlsitemap_process_node_links($nids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process node sitemap links.
|
||||
*
|
||||
* @param $nids
|
||||
* An array of node IDs.
|
||||
*/
|
||||
function xmlsitemap_node_xmlsitemap_process_node_links(array $nids) {
|
||||
$nodes = node_load_multiple($nids);
|
||||
foreach ($nodes as $node) {
|
||||
$link = xmlsitemap_node_create_link($node);
|
||||
xmlsitemap_link_save($link);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_insert().
|
||||
*/
|
||||
function xmlsitemap_node_node_insert(stdClass $node) {
|
||||
xmlsitemap_node_node_update($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_update().
|
||||
*/
|
||||
function xmlsitemap_node_node_update(stdClass $node) {
|
||||
$link = xmlsitemap_node_create_link($node);
|
||||
xmlsitemap_link_save($link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_delete().
|
||||
*/
|
||||
function xmlsitemap_node_node_delete(stdClass $node) {
|
||||
xmlsitemap_link_delete('node', $node->nid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_comment_update().
|
||||
*/
|
||||
function xmlsitemap_node_comment_update(stdClass $comment) {
|
||||
if ($node = node_load($comment->nid, NULL, TRUE)) {
|
||||
xmlsitemap_node_node_update($node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_comment_publish().
|
||||
*/
|
||||
function xmlsitemap_node_comment_publish(stdClass $comment) {
|
||||
xmlsitemap_node_comment_update($comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_comment_unpublish().
|
||||
*/
|
||||
function xmlsitemap_node_comment_unpublish(stdClass $comment) {
|
||||
xmlsitemap_node_comment_update($comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_comment_delete().
|
||||
*/
|
||||
function xmlsitemap_node_comment_delete(stdClass $comment) {
|
||||
xmlsitemap_node_comment_update($comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_extra_fields().
|
||||
*/
|
||||
function xmlsitemap_node_field_extra_fields() {
|
||||
$extras = array();
|
||||
foreach (node_type_get_names() as $type => $name) {
|
||||
$extras['node'][$type]['form']['xmlsitemap'] = array(
|
||||
'label' => t('XML sitemap'),
|
||||
'description' => t('XML sitemap module element'),
|
||||
'weight' => 30,
|
||||
);
|
||||
}
|
||||
return $extras;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*
|
||||
* @see node_type_form()
|
||||
* @see xmlsitemap_add_link_bundle_settings()
|
||||
*/
|
||||
function xmlsitemap_node_form_node_type_form_alter(array &$form, array $form_state) {
|
||||
$node_type = isset($form['#node_type']->type) ? $form['#node_type']->type : '';
|
||||
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
|
||||
xmlsitemap_add_link_bundle_settings($form, $form_state, 'node', $node_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_alter().
|
||||
*
|
||||
* Add the XML sitemap individual link options for a node.
|
||||
*
|
||||
* @see xmlsitemap_add_form_link_options()
|
||||
*/
|
||||
function xmlsitemap_node_form_node_form_alter(array &$form, array &$form_state) {
|
||||
// Add the link options.
|
||||
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
|
||||
xmlsitemap_add_form_link_options($form, 'node', $form['type']['#value'], $form['nid']['#value']);
|
||||
$form['xmlsitemap']['#weight'] = 30;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all the timestamps for when a node was changed.
|
||||
*
|
||||
* @param $node
|
||||
* A node object.
|
||||
* @return
|
||||
* An array of UNIX timestamp integers.
|
||||
*/
|
||||
function xmlsitemap_node_get_timestamps(stdClass $node) {
|
||||
static $timestamps = array();
|
||||
|
||||
if (!isset($timestamps[$node->nid])) {
|
||||
$timestamps[$node->nid] = db_query("SELECT nr.timestamp FROM {node_revision} nr WHERE nr.nid = :nid", array(':nid' => $node->nid))->fetchCol();
|
||||
if (module_exists('comment')) {
|
||||
$comment_timestamps = db_query("SELECT c.created FROM {comment} c WHERE c.nid = :nid AND c.status = :status", array(':nid' => $node->nid, ':status' => COMMENT_PUBLISHED))->fetchCol();
|
||||
$timestamps[$node->nid] = array_merge($timestamps[$node->nid], $comment_timestamps);
|
||||
}
|
||||
}
|
||||
|
||||
return $timestamps[$node->nid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sitemap link from a node.
|
||||
*
|
||||
* The link will be saved as $node->xmlsitemap.
|
||||
*
|
||||
* @param $node
|
||||
* A node object.
|
||||
*/
|
||||
function xmlsitemap_node_create_link(stdClass $node) {
|
||||
if (!isset($node->xmlsitemap) || !is_array($node->xmlsitemap)) {
|
||||
$node->xmlsitemap = array();
|
||||
if ($node->nid && $link = xmlsitemap_link_load('node', $node->nid)) {
|
||||
$node->xmlsitemap = $link;
|
||||
}
|
||||
}
|
||||
|
||||
$settings = xmlsitemap_link_bundle_load('node', $node->type);
|
||||
$uri = entity_uri('node', $node);
|
||||
|
||||
$node->xmlsitemap += array(
|
||||
'type' => 'node',
|
||||
'id' => $node->nid,
|
||||
'subtype' => $node->type,
|
||||
'status' => $settings['status'],
|
||||
'status_default' => $settings['status'],
|
||||
'status_override' => 0,
|
||||
'priority' => $settings['priority'],
|
||||
'priority_default' => $settings['priority'],
|
||||
'priority_override' => 0,
|
||||
);
|
||||
|
||||
// Always recalculate changefreq and changecount.
|
||||
$timestamps = xmlsitemap_node_get_timestamps($node);
|
||||
$node->xmlsitemap['changefreq'] = $node->nid ? xmlsitemap_calculate_changefreq($timestamps) : 0;
|
||||
$node->xmlsitemap['changecount'] = $node->nid ? count($timestamps) - 1 : 0;
|
||||
|
||||
// Node access must be reset since it a user may have changed published status, etc.
|
||||
//$access = &drupal_static('node_access');
|
||||
//unset($access[0][$node->nid]);
|
||||
//node_access_acquire_grants($node);
|
||||
|
||||
// The following values must always be checked because they are volatile.
|
||||
$node->xmlsitemap['loc'] = $uri['path'];
|
||||
$node->xmlsitemap['lastmod'] = count($timestamps) ? max($timestamps) : 0;
|
||||
$node->xmlsitemap['access'] = $node->nid ? xmlsitemap_node_view_access($node, drupal_anonymous_user()) : 1;
|
||||
$node->xmlsitemap['language'] = isset($node->language) ? $node->language : LANGUAGE_NONE;
|
||||
|
||||
return $node->xmlsitemap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a user may view the specified node.
|
||||
*
|
||||
* @param $node
|
||||
* The node object on which the operation is to be performed, or node type
|
||||
* (e.g. 'forum') for "create" operation.
|
||||
* @param $account
|
||||
* Optional, a user object representing the user for whom the operation is to
|
||||
* be performed. Determines access for a user other than the current user.
|
||||
* @return
|
||||
* TRUE if the operation may be performed, FALSE otherwise.
|
||||
*
|
||||
* This is for all intesive purposes a copy of Drupal 7's node_access() function.
|
||||
*/
|
||||
function xmlsitemap_node_view_access($node, $account = NULL) {
|
||||
global $user;
|
||||
|
||||
$op = 'view';
|
||||
$rights = &drupal_static(__FUNCTION__, array());
|
||||
|
||||
if (!$node || !in_array($op, array('view', 'update', 'delete', 'create'), TRUE)) {
|
||||
// If there was no node to check against, or the $op was not one of the
|
||||
// supported ones, we return access denied.
|
||||
return FALSE;
|
||||
}
|
||||
// If no user object is supplied, the access check is for the current user.
|
||||
if (empty($account)) {
|
||||
$account = $user;
|
||||
}
|
||||
|
||||
// $node may be either an object or a node type. Since node types cannot be
|
||||
// an integer, use either nid or type as the static cache id.
|
||||
//$cid = is_object($node) ? $node->nid : $node;
|
||||
|
||||
// If we've already checked access for this node, user and op, return from
|
||||
// cache.
|
||||
if (isset($rights[$account->uid][$node->nid])) {
|
||||
return $rights[$account->uid][$node->nid];
|
||||
}
|
||||
|
||||
if (user_access('bypass node access', $account)) {
|
||||
$rights[$account->uid][$node->nid] = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!user_access('access content', $account)) {
|
||||
$rights[$account->uid][$node->nid] = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// We grant access to the node if both of the following conditions are met:
|
||||
// - No modules say to deny access.
|
||||
// - At least one module says to grant access.
|
||||
// If no module specified either allow or deny, we fall back to the
|
||||
// node_access table.
|
||||
$access = module_invoke_all('node_access', $node, $op, $account);
|
||||
if (in_array(NODE_ACCESS_DENY, $access, TRUE)) {
|
||||
$rights[$account->uid][$node->nid] = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
elseif (in_array(NODE_ACCESS_ALLOW, $access, TRUE)) {
|
||||
$rights[$account->uid][$node->nid] = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Check if authors can view their own unpublished nodes.
|
||||
if ($op == 'view' && !$node->status && user_access('view own unpublished content', $account) && $account->uid == $node->uid && $account->uid != 0) {
|
||||
$rights[$account->uid][$node->nid] = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// If the module did not override the access rights, use those set in the
|
||||
// node_access table.
|
||||
if ($op != 'create' && $node->nid) {
|
||||
if (module_implements('node_grants')) {
|
||||
$query = db_select('node_access');
|
||||
$query->addExpression('1');
|
||||
$query->condition('grant_' . $op, 1, '>=');
|
||||
$nids = db_or()->condition('nid', $node->nid);
|
||||
if ($node->status) {
|
||||
$nids->condition('nid', 0);
|
||||
}
|
||||
$query->condition($nids);
|
||||
$query->range(0, 1);
|
||||
|
||||
// Fetch the node grants and allow other modules to alter them (D7 backport).
|
||||
$grants = &drupal_static(__FUNCTION__ . ':grants', array());
|
||||
if (!isset($grants[$account->uid][$op])) {
|
||||
// Indicate that this is our special function in the grants.
|
||||
$account->xmlsitemap_node_access = TRUE;
|
||||
$grants[$account->uid][$op] = node_access_grants($op, $account);
|
||||
// Remove the special indicator.
|
||||
unset($account->xmlsitemap_node_access);
|
||||
}
|
||||
|
||||
$grant_condition = db_or();
|
||||
foreach ($grants[$account->uid][$op] as $realm => $gids) {
|
||||
foreach ($gids as $gid) {
|
||||
$grant_condition->condition(db_and()
|
||||
->condition('gid', $gid)
|
||||
->condition('realm', $realm)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (count($grant_condition) > 0) {
|
||||
$query->condition($grant_condition);
|
||||
}
|
||||
|
||||
$result = (bool) $query->execute()->fetchField();
|
||||
$rights[$account->uid][$node->nid] = $result;
|
||||
return $result;
|
||||
}
|
||||
elseif (is_object($node) && $op == 'view' && $node->status) {
|
||||
// If no modules implement hook_node_grants(), the default behaviour is to
|
||||
// allow all users to view published nodes, so reflect that here.
|
||||
$rights[$account->uid][$node->nid] = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Unit tests for the xmlsitemap_node module.
|
||||
*/
|
||||
|
||||
class XMLSitemapNodeFunctionalTest extends XMLSitemapTestHelper {
|
||||
protected $normal_user;
|
||||
protected $nodes = array();
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XML sitemap node',
|
||||
'description' => 'Functional tests for the XML sitemap node module.',
|
||||
'group' => 'XML sitemap',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp($modules = array()) {
|
||||
$modules[] = 'xmlsitemap_node';
|
||||
$modules[] = 'comment';
|
||||
parent::setUp($modules);
|
||||
|
||||
$this->admin_user = $this->drupalCreateUser(array('administer nodes', 'bypass node access', 'administer content types', 'administer xmlsitemap'));
|
||||
$this->normal_user = $this->drupalCreateUser(array('create page content', 'edit any page content', 'access content', 'view own unpublished content'));
|
||||
xmlsitemap_link_bundle_settings_save('node', 'page', array('status' => 1, 'priority' => 0.5));
|
||||
}
|
||||
|
||||
function testNodeSettings() {
|
||||
$body_field = 'body[' . LANGUAGE_NONE . '][0][value]';
|
||||
|
||||
$node = $this->drupalCreateNode(array('status' => FALSE, 'uid' => $this->normal_user->uid));
|
||||
$this->assertSitemapLinkValues('node', $node->nid, array('access' => 0, 'status' => 1, 'priority' => 0.5, 'status_override' => 0, 'priority_override' => 0));
|
||||
|
||||
$this->drupalLogin($this->normal_user);
|
||||
$this->drupalGet('node/' . $node->nid . '/edit');
|
||||
$this->assertNoField('xmlsitemap[status]');
|
||||
$this->assertNoField('xmlsitemap[priority]');
|
||||
|
||||
$edit = array(
|
||||
'title' => 'Test node title',
|
||||
$body_field => 'Test node body',
|
||||
);
|
||||
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
|
||||
$this->assertText('Basic page Test node title has been updated.');
|
||||
$this->assertSitemapLinkValues('node', $node->nid, array('access' => 0, 'status' => 1, 'priority' => 0.5, 'status_override' => 0, 'priority_override' => 0));
|
||||
|
||||
$this->drupalLogin($this->admin_user);
|
||||
$this->drupalGet('node/' . $node->nid . '/edit');
|
||||
$this->assertField('xmlsitemap[status]');
|
||||
$this->assertField('xmlsitemap[priority]');
|
||||
|
||||
$edit = array(
|
||||
'xmlsitemap[status]' => 0,
|
||||
'xmlsitemap[priority]' => 0.9,
|
||||
'status' => TRUE,
|
||||
);
|
||||
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
|
||||
$this->assertText('Basic page Test node title has been updated.');
|
||||
$this->assertSitemapLinkValues('node', $node->nid, array('access' => 1, 'status' => 0, 'priority' => 0.9, 'status_override' => 1, 'priority_override' => 1));
|
||||
|
||||
$edit = array(
|
||||
'xmlsitemap[status]' => 'default',
|
||||
'xmlsitemap[priority]' => 'default',
|
||||
'status' => FALSE,
|
||||
);
|
||||
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
|
||||
$this->assertText('Basic page Test node title has been updated.');
|
||||
$this->assertSitemapLinkValues('node', $node->nid, array('access' => 0, 'status' => 1, 'priority' => 0.5, 'status_override' => 0, 'priority_override' => 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the content type settings.
|
||||
*/
|
||||
function testTypeSettings() {
|
||||
$this->drupalLogin($this->admin_user);
|
||||
|
||||
$node_old = $this->drupalCreateNode();
|
||||
$this->assertSitemapLinkValues('node', $node_old->nid, array('status' => 1, 'priority' => 0.5));
|
||||
|
||||
$edit = array(
|
||||
'xmlsitemap[status]' => 0,
|
||||
'xmlsitemap[priority]' => '0.0',
|
||||
);
|
||||
$this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
$this->assertText('The content type Basic page has been updated.');
|
||||
|
||||
$node = $this->drupalCreateNode();
|
||||
$this->assertSitemapLinkValues('node', $node->nid, array('status' => 0, 'priority' => 0.0));
|
||||
$this->assertSitemapLinkValues('node', $node_old->nid, array('status' => 0, 'priority' => 0.0));
|
||||
|
||||
$edit = array(
|
||||
'type' => 'page2',
|
||||
'xmlsitemap[status]' => 1,
|
||||
'xmlsitemap[priority]' => '0.5',
|
||||
);
|
||||
$this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
$this->assertText('Changed the content type of 2 posts from page to page2.');
|
||||
$this->assertText('The content type Basic page has been updated.');
|
||||
|
||||
$this->assertSitemapLinkValues('node', $node->nid, array('subtype' => 'page2', 'status' => 1, 'priority' => 0.5));
|
||||
$this->assertSitemapLinkValues('node', $node_old->nid, array('subtype' => 'page2', 'status' => 1, 'priority' => 0.5));
|
||||
$this->assertEqual(count(xmlsitemap_link_load_multiple(array('type' => 'node', 'subtype' => 'page'))), 0);
|
||||
$this->assertEqual(count(xmlsitemap_link_load_multiple(array('type' => 'node', 'subtype' => 'page2'))), 2);
|
||||
|
||||
$this->drupalPost('admin/structure/types/manage/page2/delete', array(), t('Delete'));
|
||||
$this->assertText('The content type Basic page has been deleted.');
|
||||
$this->assertFalse(xmlsitemap_link_load_multiple(array('type' => 'node', 'subtype' => 'page2')), 'Nodes with deleted node type removed from {xmlsitemap}.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the import of old nodes via cron.
|
||||
*/
|
||||
function testCron() {
|
||||
$limit = 5;
|
||||
variable_set('xmlsitemap_batch_limit', $limit);
|
||||
|
||||
$nodes = array();
|
||||
for ($i = 1; $i <= ($limit + 1); $i++) {
|
||||
$node = $this->drupalCreateNode();
|
||||
array_push($nodes, $node);
|
||||
// Need to delay by one second so the nodes don't all have the same
|
||||
// timestamp.
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
// Clear all the node link data so we can emulate 'old' nodes.
|
||||
db_delete('xmlsitemap')
|
||||
->condition('type', 'node')
|
||||
->execute();
|
||||
|
||||
// Run cron to import old nodes.
|
||||
xmlsitemap_node_cron();
|
||||
|
||||
for ($i = 1; $i <= ($limit + 1); $i++) {
|
||||
$node = array_pop($nodes);
|
||||
if ($i <= $limit) {
|
||||
// The first $limit nodes should be inserted.
|
||||
$this->assertSitemapLinkValues('node', $node->nid, array('access' => 1, 'status' => 1, 'lastmod' => $node->changed));
|
||||
}
|
||||
else {
|
||||
// Any beyond $limit should not be in the sitemap.
|
||||
$this->assertNoSitemapLink(array('type' => 'node', 'id' => $node->nid));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
name = XML sitemap taxonomy
|
||||
description = Add taxonomy term links to the sitemap.
|
||||
package = XML sitemap
|
||||
core = 7.x
|
||||
dependencies[] = xmlsitemap
|
||||
dependencies[] = taxonomy
|
||||
files[] = xmlsitemap_taxonomy.module
|
||||
files[] = xmlsitemap_taxonomy.install
|
||||
files[] = xmlsitemap_taxonomy.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-08
|
||||
version = "7.x-2.0-rc2+0-dev"
|
||||
core = "7.x"
|
||||
project = "xmlsitemap"
|
||||
datestamp = "1354931808"
|
||||
|
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install and uninstall schema and functions for the xmlsitemap_taxonomy module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function xmlsitemap_taxonomy_uninstall() {
|
||||
drupal_load('module', 'taxonomy');
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
$vocabularies = taxonomy_get_vocabularies();
|
||||
foreach ($vocabularies as $vid => $vocabulary) {
|
||||
xmlsitemap_link_bundle_delete('taxonomy_term', $vocabulary->machine_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_update_last_removed().
|
||||
*/
|
||||
function xmlsitemap_taxonomy_update_last_removed() {
|
||||
return 6198;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup variables.
|
||||
*/
|
||||
function xmlsitemap_taxonomy_update_6200() {
|
||||
drupal_load('module', 'taxonomy');
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
$vids = array_keys(taxonomy_get_vocabularies());
|
||||
foreach ($vids as $vid) {
|
||||
$settings = array(
|
||||
'status' => variable_get('xmlsitemap_taxonomy_status_' . $vid, XMLSITEMAP_STATUS_DEFAULT),
|
||||
'priority' => variable_get('xmlsitemap_taxonomy_priority_' . $vid, XMLSITEMAP_PRIORITY_DEFAULT),
|
||||
);
|
||||
variable_set('xmlsitemap_settings_taxonomy_term_' . $vid, $settings);
|
||||
variable_del('xmlsitemap_taxonomy_status_' . $vid);
|
||||
variable_del('xmlsitemap_taxonomy_priority_' . $vid);
|
||||
variable_del('xmlsitemap_taxonomy_calculate_priority_' . $vid);
|
||||
variable_del('xmlsitemap_taxonomy_include_empty_terms_' . $vid);
|
||||
}
|
||||
variable_del('xmlsitemap_taxonomy_include_empty_terms');
|
||||
variable_del('xmlsitemap_taxonomy_calculate_priority');
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty update.
|
||||
*/
|
||||
function xmlsitemap_taxonomy_update_6201() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Change bundles on taxonomy terms from vid to $vocabulary->machine_name.
|
||||
*/
|
||||
function xmlsitemap_taxonomy_update_7200() {
|
||||
drupal_load('module', 'taxonomy');
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
|
||||
$vocabularies = taxonomy_get_vocabularies();
|
||||
foreach ($vocabularies as $vid => $vocabulary) {
|
||||
xmlsitemap_link_bundle_rename('taxonomy_term', $vid, $vocabulary->machine_name);
|
||||
}
|
||||
}
|
@@ -0,0 +1,265 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_entity_info_alter().
|
||||
*/
|
||||
function xmlsitemap_taxonomy_entity_info_alter(&$entity_info) {
|
||||
$entity_info['taxonomy_term']['bundle label'] = t('Vocabulary');
|
||||
$entity_info['taxonomy_term']['xmlsitemap'] = array(
|
||||
'process callback' => 'xmlsitemap_taxonomy_xmlsitemap_process_taxonomy_term_links',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_link_info_alter().
|
||||
*/
|
||||
function xmlsitemap_taxonomy_xmlsitemap_link_info_alter(&$link_info) {
|
||||
foreach (taxonomy_vocabulary_get_names() as $machine_name => $vocabulary) {
|
||||
// Adjust the edit path to the *real* edit path.
|
||||
$link_info['taxonomy_term']['bundles'][$machine_name]['admin']['path'] .= '/edit';
|
||||
$link_info['taxonomy_term']['bundles'][$machine_name]['admin']['real path'] .= '/edit';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_cron().
|
||||
*
|
||||
* Process old taxonomy terms not found in the {xmlsitemap} table.
|
||||
*/
|
||||
function xmlsitemap_taxonomy_cron() {
|
||||
xmlsitemap_taxonomy_xmlsitemap_index_links(xmlsitemap_var('batch_limit'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_index_links().
|
||||
*/
|
||||
function xmlsitemap_taxonomy_xmlsitemap_index_links($limit) {
|
||||
if ($bundles = xmlsitemap_get_link_type_enabled_bundles('taxonomy_term')) {
|
||||
$tids = db_query_range("SELECT t.tid FROM {taxonomy_term_data} t INNER JOIN {taxonomy_vocabulary} tv USING (vid) LEFT JOIN {xmlsitemap} x ON x.type = 'taxonomy_term' AND t.tid = x.id WHERE x.id IS NULL AND tv.machine_name IN (:bundles) ORDER BY t.tid DESC", 0, $limit, array(':bundles' => $bundles))->fetchCol();
|
||||
xmlsitemap_taxonomy_xmlsitemap_process_taxonomy_term_links($tids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process taxonomy term sitemap links.
|
||||
*
|
||||
* @param $tids
|
||||
* An array of taxonomy term IDs.
|
||||
*/
|
||||
function xmlsitemap_taxonomy_xmlsitemap_process_taxonomy_term_links(array $tids) {
|
||||
$terms = taxonomy_term_load_multiple($tids);
|
||||
foreach ($terms as $term) {
|
||||
$link = xmlsitemap_taxonomy_create_link($term);
|
||||
xmlsitemap_link_save($link);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*
|
||||
* @see taxonomy_form_vocabulary()
|
||||
* @see xmlsitemap_add_link_bundle_settings()
|
||||
*/
|
||||
function xmlsitemap_taxonomy_form_taxonomy_form_vocabulary_alter(&$form, $form_state) {
|
||||
if (in_array('taxonomy_vocabulary_confirm_delete_submit', $form['#submit'])) {
|
||||
// If this is the delete form, do not add our form elements.
|
||||
return;
|
||||
}
|
||||
|
||||
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
|
||||
xmlsitemap_add_link_bundle_settings($form, $form_state, 'taxonomy_term', $form['#vocabulary']->machine_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*/
|
||||
function xmlsitemap_taxonomy_form_taxonomy_form_term_alter(&$form, $form_state) {
|
||||
if ($form['name']['#type'] == 'value') {
|
||||
// If this is the delete form, do not add our form elements.
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the link options.
|
||||
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
|
||||
xmlsitemap_add_form_link_options($form, 'taxonomy_term', $form['#term']['vocabulary_machine_name'], $form['tid']['#value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_taxonomy_vocabulary_insert().
|
||||
*/
|
||||
function xmlsitemap_taxonomy_vocabulary_insert(stdClass $vocabulary) {
|
||||
if (isset($vocabulary->xmlsitemap)) {
|
||||
xmlsitemap_link_bundle_settings_save('taxonomy_term', $vocabulary->machine_name, $vocabulary->xmlsitemap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_taxonomy_vocabulary_update().
|
||||
*/
|
||||
function xmlsitemap_taxonomy_vocabulary_update(stdClass $vocabulary) {
|
||||
if (isset($vocabulary->xmlsitemap)) {
|
||||
xmlsitemap_link_bundle_settings_save('taxonomy_term', $vocabulary->machine_name, $vocabulary->xmlsitemap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_taxonomy_term_insert() {
|
||||
*/
|
||||
function xmlsitemap_taxonomy_term_insert(stdClass $term) {
|
||||
$link = xmlsitemap_taxonomy_create_link($term);
|
||||
xmlsitemap_link_save($link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_taxonomy_term_update() {
|
||||
*/
|
||||
function xmlsitemap_taxonomy_term_update(stdClass $term) {
|
||||
$link = xmlsitemap_taxonomy_create_link($term);
|
||||
xmlsitemap_link_save($link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_taxonomy_term_delete() {
|
||||
*/
|
||||
function xmlsitemap_taxonomy_term_delete(stdClass $term) {
|
||||
xmlsitemap_link_delete('taxonomy_term', $term->tid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_extra_fields().
|
||||
*/
|
||||
function xmlsitemap_taxonomy_field_extra_fields() {
|
||||
$extras = array();
|
||||
foreach (taxonomy_vocabulary_get_names() as $machine_name => $vocabulary) {
|
||||
$extras['taxonomy_term'][$machine_name]['form']['xmlsitemap'] = array(
|
||||
'label' => t('XML sitemap'),
|
||||
'description' => t('XML sitemap module element'),
|
||||
'weight' => 30,
|
||||
);
|
||||
}
|
||||
return $extras;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sitemap link from a taxonomy term.
|
||||
*
|
||||
* @param $term
|
||||
* A taxonomy term object.
|
||||
* @return
|
||||
* An array representing a sitemap link.
|
||||
*/
|
||||
function xmlsitemap_taxonomy_create_link(stdClass &$term) {
|
||||
if (!isset($term->xmlsitemap)) {
|
||||
$term->xmlsitemap = array();
|
||||
if ($term->tid && $link = xmlsitemap_link_load('taxonomy_term', $term->tid)) {
|
||||
$term->xmlsitemap = $link;
|
||||
}
|
||||
}
|
||||
|
||||
$settings = xmlsitemap_link_bundle_load('taxonomy_term', $term->vocabulary_machine_name);
|
||||
$uri = entity_uri('taxonomy_term', $term);
|
||||
|
||||
$term->xmlsitemap += array(
|
||||
'id' => $term->tid,
|
||||
'type' => 'taxonomy_term',
|
||||
'subtype' => $term->vocabulary_machine_name,
|
||||
'status' => $settings['status'],
|
||||
'status_default' => $settings['status'],
|
||||
'status_override' => 0,
|
||||
'priority' => $settings['priority'],
|
||||
'priority_default' => $settings['priority'],
|
||||
'priority_override' => 0,
|
||||
);
|
||||
|
||||
// The following values must always be checked because they are volatile.
|
||||
// @todo How can/should we check taxonomy term access?
|
||||
$term->xmlsitemap['loc'] = $uri['path'];
|
||||
$term->xmlsitemap['access'] = 1;
|
||||
$term->xmlsitemap['language'] = isset($term->language) ? $term->language : LANGUAGE_NONE;
|
||||
|
||||
return $term->xmlsitemap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the priority of a taxonomy term based on depth and weight.
|
||||
*/
|
||||
//function xmlsitemap_taxonomy_calculate_term_priority(stdClass $term) {
|
||||
// // Calculate priority.
|
||||
// // Min weight = -128
|
||||
// // Max weight = 127
|
||||
// // Max depth = ?
|
||||
//}
|
||||
|
||||
/**
|
||||
* Find the tree depth of a taxonomy term.
|
||||
*
|
||||
* @param $term
|
||||
* A taxonomy term object.
|
||||
* @return
|
||||
* The tree depth of the term.
|
||||
*/
|
||||
function xmlsitemap_taxonomy_get_term_depth(stdClass $term) {
|
||||
static $depths = array();
|
||||
|
||||
if (!isset($depths[$term->tid])) {
|
||||
if ($parent = db_query("SELECT parent FROM {taxonomy_term_hierarchy} WHERE tid = %d", $term->tid)->fetchField()) {
|
||||
// If the term has a parent, the term's depth is the parent's depth + 1.
|
||||
if (!isset($depths[$parent])) {
|
||||
$depths[$parent] = xmlsitemap_taxonomy_get_term_depth($parent);
|
||||
}
|
||||
$depths[$term->tid] = $depths[$parent] + 1;
|
||||
}
|
||||
else {
|
||||
// Term has no parents, so depth is 0.
|
||||
$depths[$term->tid] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $depths[$term->tid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the number of nodes that are associated with a taxonomy term.
|
||||
*
|
||||
* @param $term
|
||||
* A taxonomy term object.
|
||||
* @return
|
||||
* The number of nodes associated with the term.
|
||||
*/
|
||||
function xmlsitemap_taxonomy_get_node_count(stdClass $term) {
|
||||
// @todo Use db_rewrite_sql() w/ switch user.
|
||||
return db_query_range("SELECT COUNT(ti.nid) FROM {taxonomy_index} ti LEFT JOIN {node n} USING (nid) WHERE ti.tid = :tid AND n.status = 1", 0, 1, array(':tid' => $term->tid))->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_query_alter().
|
||||
*
|
||||
* @todo Remove when http://drupal.org/node/1054162 is fixed.
|
||||
*/
|
||||
function xmlsitemap_taxonomy_entity_query_alter($query) {
|
||||
$conditions = &$query->entityConditions;
|
||||
|
||||
// Alter taxonomy term queries only.
|
||||
if (isset($conditions['entity_type']) && $conditions['entity_type']['value'] == 'taxonomy_term' && isset($conditions['bundle'])) {
|
||||
|
||||
// We can only support the operators that are explicit in values.
|
||||
if (in_array($conditions['bundle']['operator'], array(NULL, '=', '!=', 'IN', 'NOT IN'))) {
|
||||
$vids = array();
|
||||
|
||||
// Convert vocabulary machine names to vocabulary IDs.
|
||||
if (is_array($conditions['bundle']['value'])) {
|
||||
foreach ($conditions['bundle']['value'] as $vocabulary_machine_name) {
|
||||
$vocabulary = taxonomy_vocabulary_machine_name_load($vocabulary_machine_name);
|
||||
$vids[] = $vocabulary->vid;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$vocabulary = taxonomy_vocabulary_machine_name_load($conditions['bundle']['value']);
|
||||
$vids = $vocabulary->vid;
|
||||
}
|
||||
|
||||
$query->propertyCondition('vid', $vids, $conditions['bundle']['operator']);
|
||||
unset($conditions['bundle']);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Unit tests for the xmlsitemap_taxonomy module.
|
||||
*/
|
||||
|
||||
class XMLSitemapTaxonomyFunctionalTest extends XMLSitemapTestHelper {
|
||||
protected $normal_user;
|
||||
protected $terms = array();
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XML sitemap taxonomy',
|
||||
'description' => 'Functional tests for the XML sitemap taxonomy module.',
|
||||
'group' => 'XML sitemap',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp($modules = array()) {
|
||||
$modules[] = 'xmlsitemap_taxonomy';
|
||||
$modules[] = 'taxonomy';
|
||||
parent::setUp($modules);
|
||||
|
||||
$this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'administer xmlsitemap'));
|
||||
$this->normal_user = $this->drupalCreateUser(array('access content'));
|
||||
}
|
||||
|
||||
function testTaxonomySettings() {
|
||||
$this->drupalLogin($this->admin_user);
|
||||
|
||||
$edit = array(
|
||||
'name' => $this->randomName(),
|
||||
'machine_name' => drupal_strtolower($this->randomName()),
|
||||
'xmlsitemap[status]' => '1',
|
||||
'xmlsitemap[priority]' => '1.0',
|
||||
);
|
||||
$this->drupalPost('admin/structure/taxonomy/add', $edit, 'Save');
|
||||
$this->assertText("Created new vocabulary {$edit['name']}.");
|
||||
$vocabulary = taxonomy_vocabulary_machine_name_load($edit['machine_name']);
|
||||
|
||||
$edit = array(
|
||||
'name' => $this->randomName(),
|
||||
'xmlsitemap[status]' => 'default',
|
||||
'xmlsitemap[priority]' => 'default',
|
||||
);
|
||||
$this->drupalPost("admin/structure/taxonomy/{$vocabulary->machine_name}/add", $edit, 'Save');
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
name = XML sitemap user
|
||||
description = Adds user profile links to the sitemap.
|
||||
package = XML sitemap
|
||||
dependencies[] = xmlsitemap
|
||||
core = 7.x
|
||||
files[] = xmlsitemap_user.module
|
||||
files[] = xmlsitemap_user.install
|
||||
files[] = xmlsitemap_user.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-08
|
||||
version = "7.x-2.0-rc2+0-dev"
|
||||
core = "7.x"
|
||||
project = "xmlsitemap"
|
||||
datestamp = "1354931808"
|
||||
|
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install and uninstall schema and functions for the xmlsitemap_user module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_requirements().
|
||||
*/
|
||||
function xmlsitemap_user_requirements($phase) {
|
||||
$requirements = array();
|
||||
$t = get_t();
|
||||
|
||||
if ($phase == 'runtime') {
|
||||
if (!user_access('access user profiles', drupal_anonymous_user())) {
|
||||
$requirements['xmlsitemap_user_anonymous_permission'] = array(
|
||||
'title' => $t('XML sitemap user'),
|
||||
'value' => $t('Anonymous access to user profiles'),
|
||||
'description' => $t('In order to list user profile links in the sitemap, the anonymous user must have the <a href="@perm-link"><em>View user profiles</em> permission</a>.', array('@perm-link' => url('admin/people/permissions/' . DRUPAL_ANONYMOUS_RID, array('fragment' => 'module-user')))),
|
||||
'severity' => REQUIREMENT_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function xmlsitemap_user_uninstall() {
|
||||
drupal_load('module', 'user');
|
||||
drupal_load('module', 'xmlsitemap');
|
||||
xmlsitemap_link_bundle_delete('user', 'user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_update_last_removed().
|
||||
*/
|
||||
function xmlsitemap_user_update_last_removed() {
|
||||
return 6202;
|
||||
}
|
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_entity_info_alter().
|
||||
*/
|
||||
function xmlsitemap_user_entity_info_alter(&$entity_info) {
|
||||
$entity_info['user']['bundle label'] = t('User');
|
||||
$entity_info['user']['xmlsitemap'] = array(
|
||||
'process callback' => 'xmlsitemap_user_xmlsitemap_process_user_links',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_cron().
|
||||
*
|
||||
* Process old users not found in the {xmlsitemap} table.
|
||||
*/
|
||||
function xmlsitemap_user_cron() {
|
||||
xmlsitemap_user_xmlsitemap_index_links(xmlsitemap_var('batch_limit'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xmlsitemap_index_links().
|
||||
*/
|
||||
function xmlsitemap_user_xmlsitemap_index_links($limit) {
|
||||
$uids = db_query_range("SELECT u.uid FROM {users} u LEFT JOIN {xmlsitemap} x ON x.type = 'user' AND u.uid = x.id WHERE x.id IS NULL AND u.uid > 0 ORDER BY u.uid DESC", 0, $limit)->fetchCol();
|
||||
xmlsitemap_user_xmlsitemap_process_user_links($uids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process user sitemap links.
|
||||
*
|
||||
* @param $uids
|
||||
* An array of user IDs.
|
||||
*/
|
||||
function xmlsitemap_user_xmlsitemap_process_user_links(array $uids) {
|
||||
$accounts = user_load_multiple($uids);
|
||||
foreach ($accounts as $account) {
|
||||
$link = xmlsitemap_user_create_link($account);
|
||||
xmlsitemap_link_save($link);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_user_presave().
|
||||
*/
|
||||
function xmlsitemap_user_user_presave(&$edit, $account, $category) {
|
||||
if (!empty($account->uid)) {
|
||||
$link = xmlsitemap_user_create_link($account);
|
||||
if (isset($edit['xmlsitemap'])) {
|
||||
$link = $edit['xmlsitemap'] + $link;
|
||||
unset($edit['xmlsitemap']);
|
||||
}
|
||||
xmlsitemap_link_save($link);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_user_insert().
|
||||
*/
|
||||
function xmlsitemap_user_user_insert(&$edit, $account, $category) {
|
||||
$link = xmlsitemap_user_create_link($account);
|
||||
xmlsitemap_link_save($link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_user_update().
|
||||
*/
|
||||
function xmlsitemap_user_user_update(&$edit, $account, $category) {
|
||||
$link = xmlsitemap_user_create_link($account);
|
||||
xmlsitemap_link_save($link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_user_delete().
|
||||
*/
|
||||
function xmlsitemap_user_user_delete($account) {
|
||||
xmlsitemap_link_delete('user', $account->uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_extra_fields().
|
||||
*/
|
||||
function xmlsitemap_user_field_extra_fields() {
|
||||
$extras['user']['user']['form']['xmlsitemap'] = array(
|
||||
'label' => t('XML sitemap'),
|
||||
'description' => t('XML sitemap module element'),
|
||||
'weight' => 30,
|
||||
);
|
||||
return $extras;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*
|
||||
* @see user_admin_settings()
|
||||
* @see xmlsitemap_add_link_bundle_settings()
|
||||
*/
|
||||
function xmlsitemap_user_form_user_profile_form_alter(&$form, $form_state) {
|
||||
// Add the link options.
|
||||
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
|
||||
xmlsitemap_add_form_link_options($form, 'user', 'user', $form['#user']->uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*
|
||||
* @see user_admin_settings()
|
||||
* @see xmlsitemap_add_link_bundle_settings()
|
||||
*/
|
||||
function xmlsitemap_user_form_user_admin_settings_alter(&$form, $form_state) {
|
||||
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
|
||||
xmlsitemap_add_link_bundle_settings($form, $form_state, 'user', 'user');
|
||||
$form['email_title'] += array('#weight' => 10);
|
||||
$form['email'] += array('#weight' => 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sitemap link from a user.
|
||||
*
|
||||
* The link will be saved as $account->xmlsitemap.
|
||||
*
|
||||
* @param $account
|
||||
* A user object.
|
||||
*/
|
||||
function xmlsitemap_user_create_link(stdClass &$account) {
|
||||
if (!isset($account->xmlsitemap)) {
|
||||
$account->xmlsitemap = array();
|
||||
if ($account->uid && $link = xmlsitemap_link_load('user', $account->uid)) {
|
||||
$account->xmlsitemap = $link;
|
||||
}
|
||||
}
|
||||
|
||||
$settings = xmlsitemap_link_bundle_load('user', 'user');
|
||||
$uri = entity_uri('user', $account);
|
||||
|
||||
$account->xmlsitemap += array(
|
||||
'type' => 'user',
|
||||
'id' => $account->uid,
|
||||
'subtype' => 'user',
|
||||
'status' => $settings['status'],
|
||||
'status_default' => $settings['status'],
|
||||
'status_override' => 0,
|
||||
'priority' => $settings['priority'],
|
||||
'priority_default' => $settings['priority'],
|
||||
'priority_override' => 0,
|
||||
);
|
||||
|
||||
// The following values must always be checked because they are volatile.
|
||||
$account->xmlsitemap['loc'] = $uri['path'];
|
||||
$account->xmlsitemap['access'] = $account->uid && $account->status;
|
||||
$account->xmlsitemap['language'] = !empty($account->language) ? $account->language : LANGUAGE_NONE;
|
||||
|
||||
return $account->xmlsitemap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_variables().
|
||||
*/
|
||||
function xmlsitemap_user_variables() {
|
||||
$defaults = array();
|
||||
return $defaults;
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Unit tests for the xmlsitemap_user module.
|
||||
*/
|
||||
|
||||
class XMLSitemapUserFunctionalTest extends XMLSitemapTestHelper {
|
||||
protected $normal_user;
|
||||
protected $accounts = array();
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XML sitemap user',
|
||||
'description' => 'Functional tests for the XML sitemap user module.',
|
||||
'group' => 'XML sitemap',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp($modules = array()) {
|
||||
$modules[] = 'xmlsitemap_user';
|
||||
parent::setUp($modules);
|
||||
|
||||
// Save the user settings before creating the users.
|
||||
xmlsitemap_link_bundle_settings_save('user', 'user', array('status' => 1, 'priority' => 0.5));
|
||||
|
||||
// Create the users
|
||||
$this->admin_user = $this->drupalCreateUser(array('administer users', 'administer permissions', 'administer xmlsitemap'));
|
||||
$this->normal_user = $this->drupalCreateUser(array('access content'));
|
||||
|
||||
// Update the normal user to make its sitemap link visible.
|
||||
$account = clone $this->normal_user;
|
||||
user_save($account, array('access' => 1, 'login' => 1));
|
||||
}
|
||||
|
||||
function testBlockedUser() {
|
||||
$this->drupalLogin($this->admin_user);
|
||||
$this->assertSitemapLinkVisible('user', $this->normal_user->uid);
|
||||
|
||||
// Mark the user as blocked.
|
||||
$edit = array(
|
||||
'status' => 0,
|
||||
);
|
||||
|
||||
// This will pass when http://drupal.org/node/360925 is fixed.
|
||||
$this->drupalPost('user/' . $this->normal_user->uid . '/edit', $edit, t('Save'));
|
||||
$this->assertText('The changes have been saved.');
|
||||
$this->assertSitemapLinkNotVisible('user', $this->normal_user->uid);
|
||||
}
|
||||
}
|
852
sites/all/modules/xmlsitemap/xsl/jquery.tablesorter.js
Normal file
852
sites/all/modules/xmlsitemap/xsl/jquery.tablesorter.js
Normal file
@@ -0,0 +1,852 @@
|
||||
/*
|
||||
*
|
||||
* TableSorter 2.0 - Client-side table sorting with ease!
|
||||
* Version 2.0.3
|
||||
* @requires jQuery v1.2.3
|
||||
*
|
||||
* Copyright (c) 2007 Christian Bach
|
||||
* Examples and docs at: http://tablesorter.com
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*
|
||||
*/
|
||||
/**
|
||||
*
|
||||
* @description Create a sortable table with multi-column sorting capabilitys
|
||||
*
|
||||
* @example $('table').tablesorter();
|
||||
* @desc Create a simple tablesorter interface.
|
||||
*
|
||||
* @example $('table').tablesorter({ sortList:[[0,0],[1,0]] });
|
||||
* @desc Create a tablesorter interface and sort on the first and secound column in ascending order.
|
||||
*
|
||||
* @example $('table').tablesorter({ headers: { 0: { sorter: false}, 1: {sorter: false} } });
|
||||
* @desc Create a tablesorter interface and disableing the first and secound column headers.
|
||||
*
|
||||
* @example $('table').tablesorter({ 0: {sorter:"integer"}, 1: {sorter:"currency"} });
|
||||
* @desc Create a tablesorter interface and set a column parser for the first and secound column.
|
||||
*
|
||||
*
|
||||
* @param Object settings An object literal containing key/value pairs to provide optional settings.
|
||||
*
|
||||
* @option String cssHeader (optional) A string of the class name to be appended to sortable tr elements in the thead of the table.
|
||||
* Default value: "header"
|
||||
*
|
||||
* @option String cssAsc (optional) A string of the class name to be appended to sortable tr elements in the thead on a ascending sort.
|
||||
* Default value: "headerSortUp"
|
||||
*
|
||||
* @option String cssDesc (optional) A string of the class name to be appended to sortable tr elements in the thead on a descending sort.
|
||||
* Default value: "headerSortDown"
|
||||
*
|
||||
* @option String sortInitialOrder (optional) A string of the inital sorting order can be asc or desc.
|
||||
* Default value: "asc"
|
||||
*
|
||||
* @option String sortMultisortKey (optional) A string of the multi-column sort key.
|
||||
* Default value: "shiftKey"
|
||||
*
|
||||
* @option String textExtraction (optional) A string of the text-extraction method to use.
|
||||
* For complex html structures inside td cell set this option to "complex",
|
||||
* on large tables the complex option can be slow.
|
||||
* Default value: "simple"
|
||||
*
|
||||
* @option Object headers (optional) An array containing the forces sorting rules.
|
||||
* This option let's you specify a default sorting rule.
|
||||
* Default value: null
|
||||
*
|
||||
* @option Array sortList (optional) An array containing the forces sorting rules.
|
||||
* This option let's you specify a default sorting rule.
|
||||
* Default value: null
|
||||
*
|
||||
* @option Array sortForce (optional) An array containing forced sorting rules.
|
||||
* This option let's you specify a default sorting rule, which is prepended to user-selected rules.
|
||||
* Default value: null
|
||||
*
|
||||
* @option Array sortAppend (optional) An array containing forced sorting rules.
|
||||
* This option let's you specify a default sorting rule, which is appended to user-selected rules.
|
||||
* Default value: null
|
||||
*
|
||||
* @option Boolean widthFixed (optional) Boolean flag indicating if tablesorter should apply fixed widths to the table columns.
|
||||
* This is usefull when using the pager companion plugin.
|
||||
* This options requires the dimension jquery plugin.
|
||||
* Default value: false
|
||||
*
|
||||
* @option Boolean cancelSelection (optional) Boolean flag indicating if tablesorter should cancel selection of the table headers text.
|
||||
* Default value: true
|
||||
*
|
||||
* @option Boolean debug (optional) Boolean flag indicating if tablesorter should display debuging information usefull for development.
|
||||
*
|
||||
* @type jQuery
|
||||
*
|
||||
* @name tablesorter
|
||||
*
|
||||
* @cat Plugins/Tablesorter
|
||||
*
|
||||
* @author Christian Bach/christian.bach@polyester.se
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
$.extend({
|
||||
tablesorter: new function() {
|
||||
|
||||
var parsers = [], widgets = [];
|
||||
|
||||
this.defaults = {
|
||||
cssHeader: "header",
|
||||
cssAsc: "headerSortUp",
|
||||
cssDesc: "headerSortDown",
|
||||
sortInitialOrder: "asc",
|
||||
sortMultiSortKey: "shiftKey",
|
||||
sortForce: null,
|
||||
sortAppend: null,
|
||||
textExtraction: "simple",
|
||||
parsers: {},
|
||||
widgets: [],
|
||||
widgetZebra: {css: ["even","odd"]},
|
||||
headers: {},
|
||||
widthFixed: false,
|
||||
cancelSelection: true,
|
||||
sortList: [],
|
||||
headerList: [],
|
||||
dateFormat: "us",
|
||||
decimal: '.',
|
||||
debug: false
|
||||
};
|
||||
|
||||
/* debuging utils */
|
||||
function benchmark(s,d) {
|
||||
log(s + "," + (new Date().getTime() - d.getTime()) + "ms");
|
||||
}
|
||||
|
||||
this.benchmark = benchmark;
|
||||
|
||||
function log(s) {
|
||||
if (typeof console != "undefined" && typeof console.debug != "undefined") {
|
||||
console.log(s);
|
||||
} else {
|
||||
alert(s);
|
||||
}
|
||||
}
|
||||
|
||||
/* parsers utils */
|
||||
function buildParserCache(table,$headers) {
|
||||
|
||||
if(table.config.debug) { var parsersDebug = ""; }
|
||||
|
||||
var rows = table.tBodies[0].rows;
|
||||
|
||||
if(table.tBodies[0].rows[0]) {
|
||||
|
||||
var list = [], cells = rows[0].cells, l = cells.length;
|
||||
|
||||
for (var i=0;i < l; i++) {
|
||||
var p = false;
|
||||
|
||||
if($.metadata && ($($headers[i]).metadata() && $($headers[i]).metadata().sorter) ) {
|
||||
|
||||
p = getParserById($($headers[i]).metadata().sorter);
|
||||
|
||||
} else if((table.config.headers[i] && table.config.headers[i].sorter)) {
|
||||
|
||||
p = getParserById(table.config.headers[i].sorter);
|
||||
}
|
||||
if(!p) {
|
||||
p = detectParserForColumn(table,cells[i]);
|
||||
}
|
||||
|
||||
if(table.config.debug) { parsersDebug += "column:" + i + " parser:" +p.id + "\n"; }
|
||||
|
||||
list.push(p);
|
||||
}
|
||||
}
|
||||
|
||||
if(table.config.debug) { log(parsersDebug); }
|
||||
|
||||
return list;
|
||||
};
|
||||
|
||||
function detectParserForColumn(table,node) {
|
||||
var l = parsers.length;
|
||||
for(var i=1; i < l; i++) {
|
||||
if(parsers[i].is($.trim(getElementText(table.config,node)),table,node)) {
|
||||
return parsers[i];
|
||||
}
|
||||
}
|
||||
// 0 is always the generic parser (text)
|
||||
return parsers[0];
|
||||
}
|
||||
|
||||
function getParserById(name) {
|
||||
var l = parsers.length;
|
||||
for(var i=0; i < l; i++) {
|
||||
if(parsers[i].id.toLowerCase() == name.toLowerCase()) {
|
||||
return parsers[i];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* utils */
|
||||
function buildCache(table) {
|
||||
|
||||
if(table.config.debug) { var cacheTime = new Date(); }
|
||||
|
||||
|
||||
var totalRows = (table.tBodies[0] && table.tBodies[0].rows.length) || 0,
|
||||
totalCells = (table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length) || 0,
|
||||
parsers = table.config.parsers,
|
||||
cache = {row: [], normalized: []};
|
||||
|
||||
for (var i=0;i < totalRows; ++i) {
|
||||
|
||||
/** Add the table data to main data array */
|
||||
var c = table.tBodies[0].rows[i], cols = [];
|
||||
|
||||
cache.row.push($(c));
|
||||
|
||||
for(var j=0; j < totalCells; ++j) {
|
||||
cols.push(parsers[j].format(getElementText(table.config,c.cells[j]),table,c.cells[j]));
|
||||
}
|
||||
|
||||
cols.push(i); // add position for rowCache
|
||||
cache.normalized.push(cols);
|
||||
cols = null;
|
||||
};
|
||||
|
||||
if(table.config.debug) { benchmark("Building cache for " + totalRows + " rows:", cacheTime); }
|
||||
|
||||
return cache;
|
||||
};
|
||||
|
||||
function getElementText(config,node) {
|
||||
|
||||
if(!node) return "";
|
||||
|
||||
var t = "";
|
||||
|
||||
if(config.textExtraction == "simple") {
|
||||
if(node.childNodes[0] && node.childNodes[0].hasChildNodes()) {
|
||||
t = node.childNodes[0].innerHTML;
|
||||
} else {
|
||||
t = node.innerHTML;
|
||||
}
|
||||
} else {
|
||||
if(typeof(config.textExtraction) == "function") {
|
||||
t = config.textExtraction(node);
|
||||
} else {
|
||||
t = $(node).text();
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
function appendToTable(table,cache) {
|
||||
|
||||
if(table.config.debug) {var appendTime = new Date()}
|
||||
|
||||
var c = cache,
|
||||
r = c.row,
|
||||
n= c.normalized,
|
||||
totalRows = n.length,
|
||||
checkCell = (n[0].length-1),
|
||||
tableBody = $(table.tBodies[0]),
|
||||
rows = [];
|
||||
|
||||
for (var i=0;i < totalRows; i++) {
|
||||
rows.push(r[n[i][checkCell]]);
|
||||
if(!table.config.appender) {
|
||||
|
||||
var o = r[n[i][checkCell]];
|
||||
var l = o.length;
|
||||
for(var j=0; j < l; j++) {
|
||||
|
||||
tableBody[0].appendChild(o[j]);
|
||||
|
||||
}
|
||||
|
||||
//tableBody.append(r[n[i][checkCell]]);
|
||||
}
|
||||
}
|
||||
|
||||
if(table.config.appender) {
|
||||
|
||||
table.config.appender(table,rows);
|
||||
}
|
||||
|
||||
rows = null;
|
||||
|
||||
if(table.config.debug) { benchmark("Rebuilt table:", appendTime); }
|
||||
|
||||
//apply table widgets
|
||||
applyWidget(table);
|
||||
|
||||
// trigger sortend
|
||||
setTimeout(function() {
|
||||
$(table).trigger("sortEnd");
|
||||
},0);
|
||||
|
||||
};
|
||||
|
||||
function buildHeaders(table) {
|
||||
|
||||
if(table.config.debug) { var time = new Date(); }
|
||||
|
||||
var meta = ($.metadata) ? true : false, tableHeadersRows = [];
|
||||
|
||||
for(var i = 0; i < table.tHead.rows.length; i++) { tableHeadersRows[i]=0; };
|
||||
|
||||
$tableHeaders = $("thead th",table);
|
||||
|
||||
$tableHeaders.each(function(index) {
|
||||
|
||||
this.count = 0;
|
||||
this.column = index;
|
||||
this.order = formatSortingOrder(table.config.sortInitialOrder);
|
||||
|
||||
if(checkHeaderMetadata(this) || checkHeaderOptions(table,index)) this.sortDisabled = true;
|
||||
|
||||
if(!this.sortDisabled) {
|
||||
$(this).addClass(table.config.cssHeader);
|
||||
}
|
||||
|
||||
// add cell to headerList
|
||||
table.config.headerList[index]= this;
|
||||
});
|
||||
|
||||
if(table.config.debug) { benchmark("Built headers:", time); log($tableHeaders); }
|
||||
|
||||
return $tableHeaders;
|
||||
|
||||
};
|
||||
|
||||
function checkCellColSpan(table, rows, row) {
|
||||
var arr = [], r = table.tHead.rows, c = r[row].cells;
|
||||
|
||||
for(var i=0; i < c.length; i++) {
|
||||
var cell = c[i];
|
||||
|
||||
if ( cell.colSpan > 1) {
|
||||
arr = arr.concat(checkCellColSpan(table, headerArr,row++));
|
||||
} else {
|
||||
if(table.tHead.length == 1 || (cell.rowSpan > 1 || !r[row+1])) {
|
||||
arr.push(cell);
|
||||
}
|
||||
//headerArr[row] = (i+row);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
function checkHeaderMetadata(cell) {
|
||||
if(($.metadata) && ($(cell).metadata().sorter === false)) { return true; };
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkHeaderOptions(table,i) {
|
||||
if((table.config.headers[i]) && (table.config.headers[i].sorter === false)) { return true; };
|
||||
return false;
|
||||
}
|
||||
|
||||
function applyWidget(table) {
|
||||
var c = table.config.widgets;
|
||||
var l = c.length;
|
||||
for(var i=0; i < l; i++) {
|
||||
|
||||
getWidgetById(c[i]).format(table);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getWidgetById(name) {
|
||||
var l = widgets.length;
|
||||
for(var i=0; i < l; i++) {
|
||||
if(widgets[i].id.toLowerCase() == name.toLowerCase() ) {
|
||||
return widgets[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function formatSortingOrder(v) {
|
||||
|
||||
if(typeof(v) != "Number") {
|
||||
i = (v.toLowerCase() == "desc") ? 1 : 0;
|
||||
} else {
|
||||
i = (v == (0 || 1)) ? v : 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
function isValueInArray(v, a) {
|
||||
var l = a.length;
|
||||
for(var i=0; i < l; i++) {
|
||||
if(a[i][0] == v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setHeadersCss(table,$headers, list, css) {
|
||||
// remove all header information
|
||||
$headers.removeClass(css[0]).removeClass(css[1]);
|
||||
|
||||
var h = [];
|
||||
$headers.each(function(offset) {
|
||||
if(!this.sortDisabled) {
|
||||
h[this.column] = $(this);
|
||||
}
|
||||
});
|
||||
|
||||
var l = list.length;
|
||||
for(var i=0; i < l; i++) {
|
||||
h[list[i][0]].addClass(css[list[i][1]]);
|
||||
}
|
||||
}
|
||||
|
||||
function fixColumnWidth(table,$headers) {
|
||||
var c = table.config;
|
||||
if(c.widthFixed) {
|
||||
var colgroup = $('<colgroup>');
|
||||
$("tr:first td",table.tBodies[0]).each(function() {
|
||||
colgroup.append($('<col>').css('width',$(this).width()));
|
||||
});
|
||||
$(table).prepend(colgroup);
|
||||
};
|
||||
}
|
||||
|
||||
function updateHeaderSortCount(table,sortList) {
|
||||
var c = table.config, l = sortList.length;
|
||||
for(var i=0; i < l; i++) {
|
||||
var s = sortList[i], o = c.headerList[s[0]];
|
||||
o.count = s[1];
|
||||
o.count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* sorting methods */
|
||||
function multisort(table,sortList,cache) {
|
||||
|
||||
if(table.config.debug) { var sortTime = new Date(); }
|
||||
|
||||
var dynamicExp = "var sortWrapper = function(a,b) {", l = sortList.length;
|
||||
|
||||
for(var i=0; i < l; i++) {
|
||||
|
||||
var c = sortList[i][0];
|
||||
var order = sortList[i][1];
|
||||
var s = (getCachedSortType(table.config.parsers,c) == "text") ? ((order == 0) ? "sortText" : "sortTextDesc") : ((order == 0) ? "sortNumeric" : "sortNumericDesc");
|
||||
|
||||
var e = "e" + i;
|
||||
|
||||
dynamicExp += "var " + e + " = " + s + "(a[" + c + "],b[" + c + "]); ";
|
||||
dynamicExp += "if(" + e + ") { return " + e + "; } ";
|
||||
dynamicExp += "else { ";
|
||||
}
|
||||
|
||||
// if value is the same keep orignal order
|
||||
var orgOrderCol = cache.normalized[0].length - 1;
|
||||
dynamicExp += "return a[" + orgOrderCol + "]-b[" + orgOrderCol + "];";
|
||||
|
||||
for(var i=0; i < l; i++) {
|
||||
dynamicExp += "}; ";
|
||||
}
|
||||
|
||||
dynamicExp += "return 0; ";
|
||||
dynamicExp += "}; ";
|
||||
|
||||
eval(dynamicExp);
|
||||
|
||||
cache.normalized.sort(sortWrapper);
|
||||
|
||||
if(table.config.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order+ " time:", sortTime); }
|
||||
|
||||
return cache;
|
||||
};
|
||||
|
||||
function sortText(a,b) {
|
||||
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
|
||||
};
|
||||
|
||||
function sortTextDesc(a,b) {
|
||||
return ((b < a) ? -1 : ((b > a) ? 1 : 0));
|
||||
};
|
||||
|
||||
function sortNumeric(a,b) {
|
||||
return a-b;
|
||||
};
|
||||
|
||||
function sortNumericDesc(a,b) {
|
||||
return b-a;
|
||||
};
|
||||
|
||||
function getCachedSortType(parsers,i) {
|
||||
return parsers[i].type;
|
||||
};
|
||||
|
||||
/* public methods */
|
||||
this.construct = function(settings) {
|
||||
|
||||
return this.each(function() {
|
||||
|
||||
if(!this.tHead || !this.tBodies) return;
|
||||
|
||||
var $this, $document,$headers, cache, config, shiftDown = 0, sortOrder;
|
||||
|
||||
this.config = {};
|
||||
|
||||
config = $.extend(this.config, $.tablesorter.defaults, settings);
|
||||
|
||||
// store common expression for speed
|
||||
$this = $(this);
|
||||
|
||||
// build headers
|
||||
$headers = buildHeaders(this);
|
||||
|
||||
// try to auto detect column type, and store in tables config
|
||||
this.config.parsers = buildParserCache(this,$headers);
|
||||
|
||||
|
||||
// build the cache for the tbody cells
|
||||
cache = buildCache(this);
|
||||
|
||||
// get the css class names, could be done else where.
|
||||
var sortCSS = [config.cssDesc,config.cssAsc];
|
||||
|
||||
// fixate columns if the users supplies the fixedWidth option
|
||||
fixColumnWidth(this);
|
||||
|
||||
// apply event handling to headers
|
||||
// this is to big, perhaps break it out?
|
||||
$headers.click(function(e) {
|
||||
|
||||
$this.trigger("sortStart");
|
||||
|
||||
var totalRows = ($this[0].tBodies[0] && $this[0].tBodies[0].rows.length) || 0;
|
||||
|
||||
if(!this.sortDisabled && totalRows > 0) {
|
||||
|
||||
|
||||
// store exp, for speed
|
||||
var $cell = $(this);
|
||||
|
||||
// get current column index
|
||||
var i = this.column;
|
||||
|
||||
// get current column sort order
|
||||
this.order = this.count++ % 2;
|
||||
|
||||
// user only whants to sort on one column
|
||||
if(!e[config.sortMultiSortKey]) {
|
||||
|
||||
// flush the sort list
|
||||
config.sortList = [];
|
||||
|
||||
if(config.sortForce != null) {
|
||||
var a = config.sortForce;
|
||||
for(var j=0; j < a.length; j++) {
|
||||
if(a[j][0] != i) {
|
||||
config.sortList.push(a[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add column to sort list
|
||||
config.sortList.push([i,this.order]);
|
||||
|
||||
// multi column sorting
|
||||
} else {
|
||||
// the user has clicked on an all ready sortet column.
|
||||
if(isValueInArray(i,config.sortList)) {
|
||||
|
||||
// revers the sorting direction for all tables.
|
||||
for(var j=0; j < config.sortList.length; j++) {
|
||||
var s = config.sortList[j], o = config.headerList[s[0]];
|
||||
if(s[0] == i) {
|
||||
o.count = s[1];
|
||||
o.count++;
|
||||
s[1] = o.count % 2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// add column to sort list array
|
||||
config.sortList.push([i,this.order]);
|
||||
}
|
||||
};
|
||||
setTimeout(function() {
|
||||
//set css for headers
|
||||
setHeadersCss($this[0],$headers,config.sortList,sortCSS);
|
||||
appendToTable($this[0],multisort($this[0],config.sortList,cache));
|
||||
},1);
|
||||
// stop normal event by returning false
|
||||
return false;
|
||||
}
|
||||
// cancel selection
|
||||
}).mousedown(function() {
|
||||
if(config.cancelSelection) {
|
||||
this.onselectstart = function() {return false};
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// apply easy methods that trigger binded events
|
||||
$this.bind("update",function() {
|
||||
|
||||
// rebuild parsers.
|
||||
this.config.parsers = buildParserCache(this,$headers);
|
||||
|
||||
// rebuild the cache map
|
||||
cache = buildCache(this);
|
||||
|
||||
}).bind("sorton",function(e,list) {
|
||||
|
||||
$(this).trigger("sortStart");
|
||||
|
||||
config.sortList = list;
|
||||
|
||||
// update and store the sortlist
|
||||
var sortList = config.sortList;
|
||||
|
||||
// update header count index
|
||||
updateHeaderSortCount(this,sortList);
|
||||
|
||||
//set css for headers
|
||||
setHeadersCss(this,$headers,sortList,sortCSS);
|
||||
|
||||
|
||||
// sort the table and append it to the dom
|
||||
appendToTable(this,multisort(this,sortList,cache));
|
||||
|
||||
}).bind("appendCache",function() {
|
||||
|
||||
appendToTable(this,cache);
|
||||
|
||||
}).bind("applyWidgetId",function(e,id) {
|
||||
|
||||
getWidgetById(id).format(this);
|
||||
|
||||
}).bind("applyWidgets",function() {
|
||||
// apply widgets
|
||||
applyWidget(this);
|
||||
});
|
||||
|
||||
if($.metadata && ($(this).metadata() && $(this).metadata().sortlist)) {
|
||||
config.sortList = $(this).metadata().sortlist;
|
||||
}
|
||||
// if user has supplied a sort list to constructor.
|
||||
if(config.sortList.length > 0) {
|
||||
$this.trigger("sorton",[config.sortList]);
|
||||
}
|
||||
|
||||
// apply widgets
|
||||
applyWidget(this);
|
||||
});
|
||||
};
|
||||
|
||||
this.addParser = function(parser) {
|
||||
var l = parsers.length, a = true;
|
||||
for(var i=0; i < l; i++) {
|
||||
if(parsers[i].id.toLowerCase() == parser.id.toLowerCase()) {
|
||||
a = false;
|
||||
}
|
||||
}
|
||||
if(a) { parsers.push(parser); };
|
||||
};
|
||||
|
||||
this.addWidget = function(widget) {
|
||||
widgets.push(widget);
|
||||
};
|
||||
|
||||
this.formatFloat = function(s) {
|
||||
var i = parseFloat(s);
|
||||
return (isNaN(i)) ? 0 : i;
|
||||
};
|
||||
this.formatInt = function(s) {
|
||||
var i = parseInt(s);
|
||||
return (isNaN(i)) ? 0 : i;
|
||||
};
|
||||
|
||||
this.isDigit = function(s,config) {
|
||||
var DECIMAL = '\\' + config.decimal;
|
||||
var exp = '/(^[+]?0(' + DECIMAL +'0+)?$)|(^([-+]?[1-9][0-9]*)$)|(^([-+]?((0?|[1-9][0-9]*)' + DECIMAL +'(0*[1-9][0-9]*)))$)|(^[-+]?[1-9]+[0-9]*' + DECIMAL +'0+$)/';
|
||||
return RegExp(exp).test($.trim(s));
|
||||
};
|
||||
|
||||
this.clearTableBody = function(table) {
|
||||
if($.browser.msie) {
|
||||
function empty() {
|
||||
while ( this.firstChild ) this.removeChild( this.firstChild );
|
||||
}
|
||||
empty.apply(table.tBodies[0]);
|
||||
} else {
|
||||
table.tBodies[0].innerHTML = "";
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// extend plugin scope
|
||||
$.fn.extend({
|
||||
tablesorter: $.tablesorter.construct
|
||||
});
|
||||
|
||||
var ts = $.tablesorter;
|
||||
|
||||
// add default parsers
|
||||
ts.addParser({
|
||||
id: "text",
|
||||
is: function(s) {
|
||||
return true;
|
||||
},
|
||||
format: function(s) {
|
||||
return $.trim(s.toLowerCase());
|
||||
},
|
||||
type: "text"
|
||||
});
|
||||
|
||||
ts.addParser({
|
||||
id: "digit",
|
||||
is: function(s,table) {
|
||||
var c = table.config;
|
||||
return $.tablesorter.isDigit(s,c);
|
||||
},
|
||||
format: function(s) {
|
||||
return $.tablesorter.formatFloat(s);
|
||||
},
|
||||
type: "numeric"
|
||||
});
|
||||
|
||||
ts.addParser({
|
||||
id: "currency",
|
||||
is: function(s) {
|
||||
return /^[£$€?.]/.test(s);
|
||||
},
|
||||
format: function(s) {
|
||||
return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.]/g),""));
|
||||
},
|
||||
type: "numeric"
|
||||
});
|
||||
|
||||
ts.addParser({
|
||||
id: "ipAddress",
|
||||
is: function(s) {
|
||||
return /^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);
|
||||
},
|
||||
format: function(s) {
|
||||
var a = s.split("."), r = "", l = a.length;
|
||||
for(var i = 0; i < l; i++) {
|
||||
var item = a[i];
|
||||
if(item.length == 2) {
|
||||
r += "0" + item;
|
||||
} else {
|
||||
r += item;
|
||||
}
|
||||
}
|
||||
return $.tablesorter.formatFloat(r);
|
||||
},
|
||||
type: "numeric"
|
||||
});
|
||||
|
||||
ts.addParser({
|
||||
id: "url",
|
||||
is: function(s) {
|
||||
return /^(https?|ftp|file):\/\/$/.test(s);
|
||||
},
|
||||
format: function(s) {
|
||||
return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));
|
||||
},
|
||||
type: "text"
|
||||
});
|
||||
|
||||
ts.addParser({
|
||||
id: "isoDate",
|
||||
is: function(s) {
|
||||
return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);
|
||||
},
|
||||
format: function(s) {
|
||||
return $.tablesorter.formatFloat((s != "") ? new Date(s.replace(new RegExp(/-/g),"/")).getTime() : "0");
|
||||
},
|
||||
type: "numeric"
|
||||
});
|
||||
|
||||
ts.addParser({
|
||||
id: "percent",
|
||||
is: function(s) {
|
||||
return /\%$/.test($.trim(s));
|
||||
},
|
||||
format: function(s) {
|
||||
return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));
|
||||
},
|
||||
type: "numeric"
|
||||
});
|
||||
|
||||
ts.addParser({
|
||||
id: "usLongDate",
|
||||
is: function(s) {
|
||||
return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));
|
||||
},
|
||||
format: function(s) {
|
||||
return $.tablesorter.formatFloat(new Date(s).getTime());
|
||||
},
|
||||
type: "numeric"
|
||||
});
|
||||
|
||||
ts.addParser({
|
||||
id: "shortDate",
|
||||
is: function(s) {
|
||||
return /\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);
|
||||
},
|
||||
format: function(s,table) {
|
||||
var c = table.config;
|
||||
s = s.replace(/\-/g,"/");
|
||||
if(c.dateFormat == "us") {
|
||||
// reformat the string in ISO format
|
||||
s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$1/$2");
|
||||
} else if(c.dateFormat == "uk") {
|
||||
//reformat the string in ISO format
|
||||
s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1");
|
||||
} else if(c.dateFormat == "dd/mm/yy" || c.dateFormat == "dd-mm-yy") {
|
||||
s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, "$1/$2/$3");
|
||||
}
|
||||
return $.tablesorter.formatFloat(new Date(s).getTime());
|
||||
},
|
||||
type: "numeric"
|
||||
});
|
||||
|
||||
ts.addParser({
|
||||
id: "time",
|
||||
is: function(s) {
|
||||
return /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);
|
||||
},
|
||||
format: function(s) {
|
||||
return $.tablesorter.formatFloat(new Date("2000/01/01 " + s).getTime());
|
||||
},
|
||||
type: "numeric"
|
||||
});
|
||||
|
||||
|
||||
ts.addParser({
|
||||
id: "metadata",
|
||||
is: function(s) {
|
||||
return false;
|
||||
},
|
||||
format: function(s,table,cell) {
|
||||
var c = table.config, p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName;
|
||||
return $(cell).metadata()[p];
|
||||
},
|
||||
type: "numeric"
|
||||
});
|
||||
|
||||
// add default widgets
|
||||
ts.addWidget({
|
||||
id: "zebra",
|
||||
format: function(table) {
|
||||
if(table.config.debug) { var time = new Date(); }
|
||||
$("tr:visible",table.tBodies[0])
|
||||
.filter(':even')
|
||||
.removeClass(table.config.widgetZebra.css[1]).addClass(table.config.widgetZebra.css[0])
|
||||
.end().filter(':odd')
|
||||
.removeClass(table.config.widgetZebra.css[0]).addClass(table.config.widgetZebra.css[1]);
|
||||
if(table.config.debug) { $.tablesorter.benchmark("Applying Zebra widget", time); }
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
2
sites/all/modules/xmlsitemap/xsl/jquery.tablesorter.min.js
vendored
Normal file
2
sites/all/modules/xmlsitemap/xsl/jquery.tablesorter.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
137
sites/all/modules/xmlsitemap/xsl/xmlsitemap.xsl
Normal file
137
sites/all/modules/xmlsitemap/xsl/xmlsitemap.xsl
Normal file
@@ -0,0 +1,137 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- Copyright (c) 2010 Dave Reid <http://drupal.org/user/53892>
|
||||
|
||||
This file is free software: you may copy, redistribute 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 file 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
|
||||
Google Sitmaps Stylesheets (GSStylesheets)
|
||||
Project Home: http://sourceforge.net/projects/gstoolbox
|
||||
Copyright (c) 2005 Baccou Bonneville SARL (http://www.baccoubonneville.com)
|
||||
License http://www.gnu.org/copyleft/lesser.html GNU/LGPL -->
|
||||
|
||||
<xsl:stylesheet version="2.0"
|
||||
xmlns:html="http://www.w3.org/TR/REC-html40"
|
||||
xmlns:sitemap="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||
<xsl:output method="html" version="1.0" encoding="utf-8" indent="yes"/>
|
||||
<!-- Root template -->
|
||||
<xsl:template match="/">
|
||||
<html>
|
||||
<head>
|
||||
<title>Sitemap file</title>
|
||||
<script type="text/javascript" src="[jquery]"></script>
|
||||
<script type="text/javascript" src="[jquery-tablesort]"></script>
|
||||
<script type="text/javascript" src="[xsl-js]"></script>
|
||||
<link href="[xsl-css]" type="text/css" rel="stylesheet"/>
|
||||
</head>
|
||||
|
||||
<!-- Store in $fileType if we are in a sitemap or in a siteindex -->
|
||||
<xsl:variable name="fileType">
|
||||
<xsl:choose>
|
||||
<xsl:when test="//sitemap:url">sitemap</xsl:when>
|
||||
<xsl:otherwise>siteindex</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:variable>
|
||||
|
||||
<body>
|
||||
<h1>Sitemap file</h1>
|
||||
<xsl:choose>
|
||||
<xsl:when test="$fileType='sitemap'"><xsl:call-template name="sitemapTable"/></xsl:when>
|
||||
<xsl:otherwise><xsl:call-template name="siteindexTable"/></xsl:otherwise>
|
||||
</xsl:choose>
|
||||
|
||||
<div id="footer">
|
||||
<p>Generated by the <a href="http://drupal.org/project/xmlsitemap">Drupal XML sitemap module</a>.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</xsl:template>
|
||||
|
||||
<!-- siteindexTable template -->
|
||||
<xsl:template name="siteindexTable">
|
||||
<div id="information">
|
||||
<p>Number of sitemaps in this index: <xsl:value-of select="count(sitemap:sitemapindex/sitemap:sitemap)"></xsl:value-of></p>
|
||||
</div>
|
||||
<table class="tablesorter siteindex">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Sitemap URL</th>
|
||||
<th>Last modification date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<xsl:apply-templates select="sitemap:sitemapindex/sitemap:sitemap">
|
||||
<xsl:sort select="sitemap:lastmod" order="descending"/>
|
||||
</xsl:apply-templates>
|
||||
</tbody>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
<!-- sitemapTable template -->
|
||||
<xsl:template name="sitemapTable">
|
||||
<div id="information">
|
||||
<p>Number of URLs in this sitemap: <xsl:value-of select="count(sitemap:urlset/sitemap:url)"></xsl:value-of></p>
|
||||
</div>
|
||||
<table class="tablesorter sitemap">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>URL location</th>
|
||||
<th>Last modification date</th>
|
||||
<th>Change frequency</th>
|
||||
<th>Priority</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<xsl:apply-templates select="sitemap:urlset/sitemap:url">
|
||||
<xsl:sort select="sitemap:priority" order="descending"/>
|
||||
</xsl:apply-templates>
|
||||
</tbody>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
<!-- sitemap:url template -->
|
||||
<xsl:template match="sitemap:url">
|
||||
<tr>
|
||||
<td>
|
||||
<xsl:variable name="sitemapURL"><xsl:value-of select="sitemap:loc"/></xsl:variable>
|
||||
<a href="{$sitemapURL}" ref="nofollow"><xsl:value-of select="$sitemapURL"></xsl:value-of></a>
|
||||
</td>
|
||||
<td><xsl:value-of select="sitemap:lastmod"/></td>
|
||||
<td><xsl:value-of select="sitemap:changefreq"/></td>
|
||||
<td>
|
||||
<xsl:choose>
|
||||
<!-- If priority is not defined, show the default value of 0.5 -->
|
||||
<xsl:when test="sitemap:priority">
|
||||
<xsl:value-of select="sitemap:priority"/>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>0.5</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</td>
|
||||
</tr>
|
||||
</xsl:template>
|
||||
|
||||
<!-- sitemap:sitemap template -->
|
||||
<xsl:template match="sitemap:sitemap">
|
||||
<tr>
|
||||
<td>
|
||||
<xsl:variable name="sitemapURL"><xsl:value-of select="sitemap:loc"/></xsl:variable>
|
||||
<a href="{$sitemapURL}"><xsl:value-of select="$sitemapURL"></xsl:value-of></a>
|
||||
</td>
|
||||
<td><xsl:value-of select="sitemap:lastmod"/></td>
|
||||
</tr>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
45
sites/all/modules/xmlsitemap/xsl/xmlsitemap.xsl.css
Normal file
45
sites/all/modules/xmlsitemap/xsl/xmlsitemap.xsl.css
Normal file
@@ -0,0 +1,45 @@
|
||||
|
||||
body {
|
||||
background-color: #FFF;
|
||||
font-family: Verdana,sans-serif;
|
||||
font-size: 10pt;
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
table.tablesorter {
|
||||
background-color: #CDCDCD;
|
||||
margin:10px 0pt 15px;
|
||||
font-size: 8pt;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
table.tablesorter thead tr th, table.tablesorter tfoot tr th {
|
||||
background-color: #E6EEEE;
|
||||
border: 1px solid #FFF;
|
||||
font-size: 8pt;
|
||||
padding: 3px;
|
||||
}
|
||||
table.tablesorter thead tr .header {
|
||||
cursor: pointer;
|
||||
}
|
||||
table.tablesorter tbody td {
|
||||
color: #3D3D3D;
|
||||
padding: 3px;
|
||||
background-color: #FFF;
|
||||
vertical-align: top;
|
||||
}
|
||||
table.tablesorter tbody tr.odd td {
|
||||
background-color: #EFEFEF;
|
||||
}
|
||||
table.tablesorter thead tr .headerSortUp {
|
||||
background: url(/misc/arrow-asc.png) no-repeat center right;
|
||||
}
|
||||
table.tablesorter thead tr .headerSortDown {
|
||||
background: url(/misc/arrow-desc.png) no-repeat center right;
|
||||
}
|
||||
table.tablesorter thead tr .headerSortDown, table.tablesorter thead tr .headerSortUp {
|
||||
background-color: #5050D3;
|
||||
color: #FFF;
|
||||
font-style: italic;
|
||||
}
|
44
sites/all/modules/xmlsitemap/xsl/xmlsitemap.xsl.js
Normal file
44
sites/all/modules/xmlsitemap/xsl/xmlsitemap.xsl.js
Normal file
@@ -0,0 +1,44 @@
|
||||
(function($){
|
||||
|
||||
$.tablesorter.addParser({
|
||||
// set a unique id
|
||||
id: 'changefreq',
|
||||
is: function(s) {
|
||||
return false;
|
||||
},
|
||||
format: function(s) {
|
||||
switch (s) {
|
||||
case 'always':
|
||||
return 0;
|
||||
case 'hourly':
|
||||
return 1;
|
||||
case 'daily':
|
||||
return 2;
|
||||
case 'weekly':
|
||||
return 3;
|
||||
case 'monthly':
|
||||
return 4;
|
||||
case 'yearly':
|
||||
return 5;
|
||||
default:
|
||||
return 6;
|
||||
}
|
||||
},
|
||||
type: 'numeric'
|
||||
});
|
||||
|
||||
$(document).ready(function() {
|
||||
// Set some location variales.
|
||||
$('h1').append(': ' + location);
|
||||
document.title += ': ' + location;
|
||||
|
||||
$('table').tablesorter({
|
||||
sortList: [[0,0]],
|
||||
headers: {
|
||||
2: { sorter: 'changefreq' }
|
||||
},
|
||||
widgets: ['zebra']
|
||||
});
|
||||
});
|
||||
|
||||
})(jQuery);
|
Reference in New Issue
Block a user