FINAL suepr merge step : added all modules to this super repos

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-19 16:46:59 +02:00
7585 changed files with 1723356 additions and 18 deletions

View File

@@ -0,0 +1,5 @@
$Id: CHANGELOG.txt,v 1.2 2009/12/22 23:38:53 davereid Exp $
XML Sitemap 7.x-2.x-dev
-----------------------

View File

@@ -0,0 +1,274 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
Cambridge, MA 02139, 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 Library 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

View File

@@ -0,0 +1,87 @@
$Id: README.txt,v 1.6 2009/12/23 07:34:46 davereid Exp $
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/

View File

@@ -0,0 +1,486 @@
<?php
// $Id: xmlsitemap.admin.inc,v 1.12 2010/01/24 04:10:46 davereid Exp $
/**
* @file
* Administrative page callbacks for the xmlsitemap module.
*
* @ingroup xmlsitemap
*/
/**
* Form builder; Administration settings form.
*
* @see system_settings_form()
* @see xmlsitemap_settings_form_validate()
*/
function xmlsitemap_settings_form() {
$form['xmlsitemap'] = array(
'#type' => 'fieldset',
'#title' => t('Settings'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['xmlsitemap']['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, 32400, 43200, 86400, 172800, 259200, 604800), 'format_interval'),
'#default_value' => xmlsitemap_var('minimum_lifetime')
);
$form['xmlsitemap']['xmlsitemap_xsl'] = array(
'#type' => 'checkbox',
'#title' => t('Include a stylesheet in the sitemaps for humans.'),
'#default_value' => xmlsitemap_var('xsl'),
'#description' => t('Using the stylesheet 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 any formatting.')
);
$form['xmlsitemap']['xmlsitemap_languages'] = array(
'#type' => 'checkboxes',
'#title' => t('Generate sitemaps for the following languages'),
'#options' => array(language_default('language') => language_default('name')),
'#default_value' => xmlsitemap_var('languages'),
'#process' => array('form_process_checkboxes', '_xmlsitemap_process_language_checkboxes'),
'#description' => !module_exists('xmlsitemap_i18n') ? t('To enable multilingual features, enable the XML sitemap internationalization module.') : '',
);
$form['advanced'] = array(
'#type' => 'fieldset',
'#title' => t('Advanced settings'),
'#collapsible' => TRUE,
'#collapsed' => !variable_get('xmlsitemap_developer_mode', FALSE),
'#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, 50000)),
'#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 50,000 links, an index with multiple sitemap pages will be generated. There is a maximum of 1000 sitemap pages.'),
);
$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.'),
);
$path = variable_get('xmlsitemap_path', 'xmlsitemap');
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' => $path,
'#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_directory_path() . '/',
'#required' => TRUE,
);
$form['advanced']['xmlsitemap_base_url'] = array(
'#type' => 'textfield',
'#title' => t('Base URL'),
'#default_value' => xmlsitemap_var('base_url'),
'#size' => 30,
'#description' => t('This is the base URL for links generated in the sitemap.'),
'#required' => TRUE,
);
$form['advanced']['xmlsitemap_developer_mode'] = array(
'#type' => 'checkbox',
'#title' => t('Enable developer mode.'),
'#default_value' => variable_get('xmlsitemap_developer_mode', FALSE),
'#description' => t('Exposes additional settings intended for development.'),
);
$form['xmlsitemap_settings'] = array(
'#type' => 'vertical_tabs',
'#weight' => 20,
);
$form['frontpage'] = array(
'#type' => 'fieldset',
'#title' => t('Front page'),
'#description' => t('The front page path can be changed at <a href="@url-frontpage">@url-frontpage</a>.', array('@url-frontpage' => url('admin/settings/site-information'))),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => 20,
'#group' => 'xmlsitemap_settings',
);
$form['frontpage']['xmlsitemap_frontpage_priority'] = array(
'#type' => 'select',
'#title' => t('Priority'),
'#options' => xmlsitemap_get_priority_options(),
'#default_value' => xmlsitemap_var('frontpage_priority'),
);
$form['frontpage']['xmlsitemap_frontpage_changefreq'] = array(
'#type' => 'select',
'#title' => t('Change frequency'),
'#options' => xmlsitemap_get_changefreq_options(),
'#default_value' => xmlsitemap_var('frontpage_changefreq'),
);
$form['#pre_render'][] = 'vertical_tabs_form_pre_render';
$form['#validate'][] = 'xmlsitemap_settings_form_validate';
$form['#submit'][] = 'xmlsitemap_settings_form_submit';
$form['array_filter'] = array('#type' => 'value', '#value' => TRUE);
$form = system_settings_form($form);
$form['actions']['#weight'] = 100;
return $form;
}
/**
* Show a link to each languages' sitemap and disable the default language
* checkbox.
*/
function _xmlsitemap_process_language_checkboxes($element) {
foreach (element_children($element) as $key) {
if ($key == language_default('language')) {
$element[$key]['#disabled'] = TRUE;
$element[$key]['#default_value'] = TRUE;
$element[$key]['#weight'] = -1;
}
$link = url('sitemap.xml', array('absolute' => TRUE, 'language' => xmlsitemap_language_load($key)));
$element[$key]['#description'] = l($link, $link);
}
return $element;
}
/**
* 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_save_link(array('type' => 'frontpage', 'id' => 0, 'loc' => ''));
}
/**
* Menu callback; Confirm rebuilding of the sitemap.
*
* @see xmlsitemap_rebuild_form_submit()
*/
function xmlsitemap_rebuild_form() {
if (!$_POST && !xmlsitemap_var('rebuild_needed')) {
if (!xmlsitemap_var('regenerate_needed')) {
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');
}
}
// Show only the modules that implement the 6.x-2.x hooks.
$modules = module_implements('xmlsitemap_link_info', TRUE);
$form['modules'] = array(
'#type' => 'select',
'#title' => t("Select which modules' links you would like to rebuild"),
'#description' => t('If no modules are selected, the sitemap files will just be regenerated.'),
'#multiple' => TRUE,
'#options' => drupal_map_assoc($modules),
'#default_value' => xmlsitemap_var('rebuild_needed') || !xmlsitemap_var('developer_mode') ? $modules : array(),
'#access' => xmlsitemap_var('developer_mode'),
);
$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 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) {
// Set the rebuild flag incase something fails during the rebuild.
variable_set('xmlsitemap_rebuild_needed', TRUE);
module_load_include('inc', 'xmlsitemap');
$batch = xmlsitemap_rebuild_batch($form_state['values']['modules'], $form_state['values']['save_custom']);
batch_set($batch);
$form_state['redirect'] = 'admin/config/search/xmlsitemap';
}
/**
* Create a list of items that can be included in the sitemap.
*
* @param $form
* An array with the form to add the type summary.
* @param $type
* An array of details about the type with the following key/value pairs:
* type: The {xmlsitemap}.type value
* title: The title of the type (usually plural form)
* item_title: The title of each individual item of this type (singular)
* access: An optional boolean that if TRUE will create links to each
* item's edit page as specified by the link key in $items.
* @param $items
* An array of items with the following keys:
* 'name': The translated name of the item.
* 'link': The path to edit the item. If $type['access'] is not TRUE, just
the item's name will be shown instead of a link.
* 'status': The item's default inclusion status.
* 'priority': The items default priority.
*/
function xmlsitemap_add_form_type_summary(&$form, $type, $items) {
$form[$type['type']] = array(
'#type' => 'fieldset',
'#title' => $type['title'],
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'xmlsitemap_settings',
);
$header = array(
$type['item_title'],
t('Inclusion'),
t('Priority'),
);
$priorities = xmlsitemap_get_priority_options(NULL, FALSE);
$statuses = xmlsitemap_get_status_options(NULL);
$rows = array();
foreach ($items as $item_type => $item) {
$rows[] = array(
!empty($type['access']) ? l($item['name'], $item['link'], array('query' => drupal_get_destination())) : check_plain($item['name']),
$statuses[$item['status'] ? 1 : 0],
$priorities[number_format($item['priority'], 1)],
);
}
$form[$type['type']]['list'] = array(
'#value' => theme('table', $header, $rows),
);
}
/**
* Add the link type XML sitemap options to the link type's form.
*
* @todo Abstract the submission and link updating for this submit?
*/
function xmlsitemap_add_form_type_options(&$form, $module, $options) {
$form['xmlsitemap'] = array(
'#type' => 'fieldset',
'#title' => t('XML sitemap'),
'#description' => t('Changing these type settings will affect any items of this type that have either inclusion or priority set to default.'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#access' => user_access('administer xmlsitemap'),
'#group' => 'additional_settings',
);
$form['xmlsitemap']['xmlsitemap_' . $module . '_status'] = array(
'#type' => 'select',
'#title' => t('Inclusion'),
'#options' => xmlsitemap_get_status_options(),
'#default_value' => $options['status'],
);
$form['xmlsitemap']['xmlsitemap_' . $module . '_priority'] = array(
'#type' => 'select',
'#title' => t('Default priority'),
'#options' => xmlsitemap_get_priority_options(),
'#default_value' => $options['priority'],
'#states' => array(
'invisible' => array(
'input[name="xmlsitemap_' . $module . '_status"]' => array('value' => 0),
),
),
);
}
/**
* Add a link's XML sitemap options to the link's form.
*/
function xmlsitemap_add_form_link_options(&$form, $link) {
$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'),
'#group' => 'additional_settings',
'#attached' => array(
'js' => array(
'vertical-tabs' => drupal_get_path('module', 'xmlsitemap') . '/xmlsitemap.js',
),
),
);
// Show a warning if the link is not accessible and will not be included in
// the sitemap.
if (!$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(
'input[name="xmlsitemap[status]"]' => array('value' => $link['status_default'] ? array('default', 1) : array(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.
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;
}

View File

@@ -0,0 +1,92 @@
<?php
// $Id: xmlsitemap.api.php,v 1.4 2009/12/23 22:29:16 davereid Exp $
/**
* @file
* Hooks provided by the XML sitemap module.
*
* @ingroup xmlsitemap
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Provide information on the type of links this module provides.
*/
function hook_xmlsitemap_link_info() {
return array(
'mymodule' => array(
'purge' => TRUE, // A boolean if this link type can be purged during a rebuild.
),
);
}
/**
* Retrieve a array of links to include in the sitemap.
*
* @return
* An array of link arrays with the following keys and values:
* - 'type' => The type of link (node, user, kitten, etc.).
* - 'id' => The ID of the link ($node->nid, $user->uid, etc.).
* - 'loc' => The un-aliased Drupal path to the item.
* - 'lastmod' => The UNIX timestmap of when the item was last modified.
* - 'changefreq' => The interval, in seconds, between the last set of changes.
* - 'priority' => An optional priority value between 0.0 and 1.0.
*/
function hook_xmlsitemap_links() {
$links = array();
$links[] = array(
'type' => 'mymodule',
'id' => 1,
'loc' => 'mymodule/menu/path',
'lastmod' => 346245692,
'changefreq' => 4600,
);
return $links;
}
/**
* Provide batch information for hook_xmlsitemap_links().
*
* It is highly recommended that if your module has a lot of items that could
* be sitemap links, that you implement this hook.
*
* All you need to do to implement this hook is add the required $context
* information.
*
* The optional current value will provide the offset parameter to
* hook_xmlsitemap_links() and should get records that are greater than this
* value. The default value is 0.
*
* The max (count) value will allow the batch to know when it is finished. This
* value is required.
*/
function hook_xmlsitemap_links_batch_info() {
return array(
'current' => 0,
// This value is used to start selecting items (WHERE id > current).
'max' => db_query("SELECT COUNT(id) FROM {mymodule}")->fetchField(),
// This should be the total number of items to process.
);
}
/**
* 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;
}
}
/**
* @} End of "addtogroup hooks".
*/

View File

@@ -0,0 +1,84 @@
<?php
// $Id: xmlsitemap.drush.inc,v 1.8 2010/01/24 05:54:40 davereid Exp $
/**
* @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.',
'drupal dependencies' => array('xmlsitemap'),
);
$items['xmlsitemap-rebuild'] = array(
'description' => 'Dump and re-process all possible XML sitemap data, and then regenerate the files.',
'drupal dependencies' => array('xmlsitemap'),
);
$items['xmlsitemap-index'] = array(
'description' => 'Process un-indexed XML sitemap links.',
'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('inc', 'xmlsitemap');
xmlsitemap_regenerate();
$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('inc', 'xmlsitemap');
timer_start('xmlsitemap_rebuild');
// Set the rebuild flag incase something fails during the rebuild.
variable_set('xmlsitemap_rebuild_needed', TRUE);
// Build the batch array.
$modules = module_implements('xmlsitemap_link_info');
$batch = xmlsitemap_rebuild_batch($modules, TRUE);
$batch['progressive'] = FALSE;
batch_set($batch);
// We need to manually set the progressive variable again.
// @todo Remove when http://drupal.org/node/638712 is fixed.
$batch =& batch_get();
$batch['progressive'] = FALSE;
// Run the batch process.
batch_process();
$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));
module_invoke_all('xmlsitemap_index_links', $limit);
}

View File

@@ -0,0 +1,513 @@
<?php
// $Id: xmlsitemap.inc,v 1.14 2010/01/23 23:19:08 davereid Exp $
/**
* @file
* Miscellaneous 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 look 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;
}
}
/**
* Delete and regenerate the sitemap files.
*/
function xmlsitemap_regenerate() {
_xmlsitemap_regenerate_before();
// Generate the sitemap pages.
$chunk_count = xmlsitemap_get_chunk_count(TRUE);
if ($chunk_count > 1) {
// If we have more than one chunk, we need to increment this value by one
// since the index page (chunk 0) will also need to be generated.
$chunk_count++;
}
foreach (xmlsitemap_var('languages') as $language) {
for ($i = 0; $i < $chunk_count; $i++) {
xmlsitemap_generate($i, xmlsitemap_language_load($language));
}
}
_xmlsitemap_regenerate_after();
}
/**
* Perform operations before rebuilding the sitemap.
*/
function _xmlsitemap_regenerate_before() {
// Attempt to increase the available processing time and memory limit.
drupal_set_time_limit(240);
_xmlsitemap_set_memory_limit();
// Set a timer so we can track how long this takes.
timer_start('xmlsitemap_regenerate');
// Get the current memory usage so we can track how much memory is used.
_xmlsitemap_get_memory_usage(TRUE);
// Clear all cached sitemap files.
xmlsitemap_clear_directory();
xmlsitemap_check_directory();
}
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;
}
function _xmlsitemap_get_optimal_memory_limit() {
static $optimal_limit;
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.
$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.
*/
function _xmlsitemap_set_memory_limit() {
$memory_limit = @ini_get('memory_limit');
if ($memory_limit && $memory_limit != -1) {
$optimal_limit = _xmlsitemap_get_optimal_memory_limit();
if (parse_size($memory_limit) < $optimal_limit) {
@ini_set('memory_limit', $optimal_limit);
}
}
}
/**
* Perform operations after rebuilding the sitemap.
*/
function _xmlsitemap_regenerate_after() {
// Show a watchdog message that the sitemap was regenerated.
watchdog('xmlsitemap',
'XML sitemap files regenerated in @timer ms. Peak memory usage: @memory-peak.',
array(
'@timer' => timer_read('xmlsitemap_regenerate'),
'@memory-peak' => format_size(memory_get_peak_usage(TRUE)),
),
WATCHDOG_NOTICE,
l(t('View sitemap'), 'sitemap.xml')
);
// Unset the regenerate flag.
variable_set('xmlsitemap_regenerate_needed', FALSE);
// If the chunk count has changed, we will need to rebuild the menu.
variable_set('menu_rebuild_needed', TRUE);
variable_set('xmlsitemap_generated_last', REQUEST_TIME);
}
/**
* Fetch the data from {xmlsitemap}, generates the sitemap, then caches it.
*
* @param $chunk
* An integer representing the integer of the sitemap page chunk.
* @param $language
* A language object, defaults to the default language.
* @return
* TRUE on success; otherwise FALSE
*
* @todo Revise/simplify or remove the function.
*/
function xmlsitemap_generate($chunk = 0, $language = NULL) {
if (!is_numeric($chunk) || $chunk > xmlsitemap_get_chunk_count()) {
// Don't bother translating this string.
trigger_error('Improper condition hit in xmlsitemap_generate(). Chunk: ' . $chunk . ', Chunk Count: ' . xmlsitemap_get_chunk_count());
return FALSE;
}
if (!isset($language)) {
$language = language_default();
}
$file = xmlsitemap_get_chunk_file($chunk, $language->language);
if (!$handle = fopen($file, 'wb')) {
trigger_error(t('Could not open file @file for writing.', array('@file' => $file)));
return FALSE;
}
$status = TRUE;
if (xmlsitemap_get_chunk_count() > 1 && !$chunk) {
xmlsitemap_generate_index($handle, $status, $language);
}
else {
xmlsitemap_generate_chunk($handle, $status, $chunk, $language);
}
fclose($handle);
if (!$status) {
trigger_error(t('Unknown error occurred while writing to file @file.', array('@file' => $file)));
}
elseif (xmlsitemap_var('gz')) {
$file_gz = xmlsitemap_get_chunk_file($chunk, $language->language, 'gz');
file_put_contents($file_gz, gzencode(file_get_contents($file), 9));
}
return $status;
}
//function xmlsitemap_fwrite($handle, &$status, $string) {
// $status &= (bool) fwrite($handle, $string);
//}
/**
* Write the proper XML sitemap header.
*
* @param $type
* @param $handle
* A file system pointer resource that is typically created using fopen().
* @param $status
* @param $language
*/
function xmlsitemap_generate_chunk_header($type, $handle, &$status, $language) {
$output = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL;
// Add the stylesheet link.
if (xmlsitemap_var('xsl')) {
$xsl_url = url('sitemap.xsl', array('language' => $language, 'base_url' => xmlsitemap_var('base_url')));
$output .= '<?xml-stylesheet type="text/xsl" href="' . $xsl_url . '"?>' . PHP_EOL;
}
$output .= '<' . $type . ' xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . PHP_EOL;
// This is the full XML header required for schema validation.
//$schemas = array('sitemapindex' => 'siteindex.xsd', 'urlset' => 'sitemap.xsd');
//$output .= '<' . $type . ' xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"' . PHP_EOL;
//$output .= ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' . PHP_EOL;
//$output .= ' xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9' . PHP_EOL;
//$output .= ' http://www.sitemaps.org/schemas/sitemap/0.9/' . $schemas[$type] . '">' . PHP_EOL;
$status &= (bool) fwrite($handle, $output);
return $status;
}
/**
* Generate one page (chunk) of the sitemap.
*
* @param $handle
* A file system pointer resource that is typically created using fopen().
* @param $status
* A boolean that will be altered by reference with the success status of
* writing to $handle.
* @param $chunk
* An integer representing the integer of the sitemap page chunk.
* @param $language
* A language object for the sitemap chunk.
*/
function xmlsitemap_generate_chunk($handle, &$status, $chunk, $language) {
$last_url = '';
$url_options = xmlsitemap_get_url_options(array('alias' => TRUE));
$query = db_select('xmlsitemap', 'x');
$query->fields('x', array('loc', 'lastmod', 'changefreq', 'changecount', 'priority', 'language', 'access', 'status'));
$query->condition('access', 1);
$query->condition('status', 1);
$query->orderBy('language', 'DESC');
$query->orderBy('loc');
$query->addTag('xmlsitemap');
$query->addMetaData('language', $language);
$offset = max($chunk - 1, 0) * xmlsitemap_get_chunk_size();
$limit = xmlsitemap_get_chunk_size();
$query->range($offset, $limit);
$links = $query->execute();
// Add the XML header and XSL if desired.
xmlsitemap_generate_chunk_header('urlset', $handle, $status, $language);
while ($link = $links->fetchAssoc()) {
$url_options['language'] = ($link['language'] != LANGUAGE_NONE ? xmlsitemap_language_load($link['language']) : $language);
$link['alias'] = xmlsitemap_get_path_alias($link['loc'], $url_options['language']->language);
$link_url = url($link['alias'], $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;
}
$link_output = '<url><loc>' . $link_url . '</loc>';
if ($link['lastmod']) {
$link_output .= '<lastmod>' . gmdate(DATE_W3C, $link['lastmod']) . '</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']) {
$link_output .= '<changefreq>' . xmlsitemap_get_changefreq($link['changefreq']) . '</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.
$link_output .= '<priority>' . number_format($link['priority'], 1) . '</priority>';
}
$link_output .= '</url>' . PHP_EOL;
$status &= (bool) fwrite($handle, $link_output);
}
// Close the XML file.
$status &= (bool) fwrite($handle, '</urlset>' . PHP_EOL);
return $status;
}
/**
* Generate the index sitemap.
*
* @param $handle
* A file system pointer resource that is typically created using fopen().
* @param $status
* @param $language
* A language object, defaults to the default language.
*/
function xmlsitemap_generate_index($handle, &$status, $language) {
$url_options = xmlsitemap_get_url_options(array('language' => $language, 'alias' => TRUE));
$chunk_count = xmlsitemap_get_chunk_count(TRUE);
// Add the XML header and XSL if desired.
xmlsitemap_generate_chunk_header('sitemapindex', $handle, $status, $language);
for ($i = 1; $i <= $chunk_count; $i++) {
$output = '<sitemap>';
$output .= '<loc>' . url('sitemap-' . $i . '.xml', $url_options) . '</loc>';
// @todo Use the actual lastmod value of the chunk file.
$output .= '<lastmod>' . gmdate(DATE_W3C, REQUEST_TIME) . '</lastmod>';
$output .= '</sitemap>' . PHP_EOL;
$status &= (bool) fwrite($handle, $output);
}
// Close the XML file.
$status &= (bool) fwrite($handle, '</sitemapindex>' . PHP_EOL);
return $status;
}
/**
* Batch information callback.
*/
function xmlsitemap_rebuild_batch($modules = array(), $save_custom = FALSE) {
$batch = array(
'operations' => array(),
'finished' => 'xmlsitemap_rebuild_batch_finished',
'title' => t('Rebuilding Sitemap'),
'file' => drupal_get_path('module', 'xmlsitemap') . '/xmlsitemap.inc',
);
// Purge any links first.
$batch['operations'][] = array('xmlsitemap_rebuild_batch_clear', array($modules, $save_custom));
// Fetch all the sitemap links and save them to the {xmlsitemap} table.
foreach ($modules as $module) {
if (module_hook($module, 'xmlsitemap_links')) {
$batch['operations'][] = array('xmlsitemap_rebuild_batch_fetch', array($module));
}
}
// Generate all the sitemap pages.
$batch['operations'][] = array('_xmlsitemap_regenerate_before', array());
foreach (xmlsitemap_var('languages') as $language) {
$batch['operations'][] = array('xmlsitemap_rebuild_batch_generate', array(xmlsitemap_language_load($language)));
}
$batch['operations'][] = array('_xmlsitemap_regenerate_after', array());
return $batch;
}
/**
* Batch callback; clear sitemap links for modules.
*/
function xmlsitemap_rebuild_batch_clear($modules, $save_custom, &$context) {
$purge = array();
foreach ($modules as $module) {
$types = module_invoke($module, 'xmlsitemap_link_info');
foreach ($types as $type => $info) {
if ($info['purge']) {
$purge[] = $type;
}
}
}
if ($purge) {
$query = db_delete('xmlsitemap');
$query->condition('type', $purge);
// 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 module.
*/
function xmlsitemap_rebuild_batch_fetch($module, &$context) {
if (!isset($context['sandbox']['progress'])) {
$context['sandbox']['batch'] = module_hook($module, 'xmlsitemap_links_batch_info');
if ($context['sandbox']['batch']) {
$context['sandbox'] += module_invoke($module, 'xmlsitemap_links_batch_info');
}
else {
$context['sandbox']['links'] = module_invoke($module, 'xmlsitemap_links');
$context['sandbox']['max'] = count($context['sandbox']['links']);
}
$context['sandbox'] += array(
'progress' => 0,
'current' => 0,
);
}
if ($context['sandbox']['batch']) {
$links = module_invoke($module, 'xmlsitemap_links', $context['sandbox']['current'], xmlsitemap_var('batch_limit'));
}
else {
$links = array_splice($context['sandbox']['links'], 0, xmlsitemap_var('batch_limit'));
}
foreach ($links as $link) {
xmlsitemap_save_link($link);
$context['sandbox']['progress']++;
$context['sandbox']['current'] = $link['id'];
$context['message'] = t('Now processing %module link @count.', array('%module' => $module, '@count' => $context['sandbox']['progress']));
}
if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
}
}
/**
* Batch callback; generate the sitemap chunks for a language.
*/
function xmlsitemap_rebuild_batch_generate($language, &$context) {
if (!isset($context['sandbox']['progress'])) {
$context['sandbox']['progress'] = 0;
$context['sandbox']['max'] = xmlsitemap_get_chunk_count(TRUE);
// If we have more than one chunk, we need to increment this value by one
// since the index page (chunk 0) will also need to be generated.
if ($context['sandbox']['max'] > 1) {
$context['sandbox']['max']++;
}
}
xmlsitemap_generate($context['sandbox']['progress'], $language);
$context['sandbox']['progress']++;
$context['message'] = t('Now generating @language sitemap page @chunk.', array('@language' => $language->name, '@chunk' => $context['sandbox']['progress']));
if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
}
}
/**
* Batch callback; sitemap rebuild finished.
*/
function xmlsitemap_rebuild_batch_finished($success, $results, $operations) {
if ($success) {
// Reset the rebuild flag since it was successful.
variable_set('xmlsitemap_rebuild_needed', FALSE);
drupal_set_message(t('The sitemap was rebuilt.'));
}
else {
drupal_set_message(t('The sitemap was not successfully rebuilt.'), 'error');
}
}
/**
* 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 indivduals 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;
}

View File

@@ -0,0 +1,22 @@
; $Id: xmlsitemap.info,v 1.4 2009/12/22 23:56:59 davereid Exp $
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.pages.inc
files[] = xmlsitemap.install
files[] = xmlsitemap.test
recommends[] = robotstxt
recommends[] = vertical_tabs
configure = admin/config/search/xmlsitemap
; Information added by drupal.org packaging script on 2010-01-24
version = "7.x-2.x-dev"
core = "7.x"
project = "xmlsitemap"
datestamp = "1264335489"

View File

@@ -0,0 +1,491 @@
<?php
// $Id: xmlsitemap.install,v 1.22 2010/01/24 03:57:17 davereid Exp $
/**
* @file
* Install, update and uninstall functions for the xmlsitemap module.
*
* @ingroup xmlsitemap
*/
/**
* Implements hook_requirements().
*/
function xmlsitemap_requirements($phase) {
$requirements = array();
$t = get_t();
if (variable_get('clean_url', 0) && file_exists('./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.'),
);
}
if ($phase == 'runtime') {
if (!user_access('access content', drupal_anonymous_user())) {
$requirements['xmlsitemap_access'] = array(
'title' => $t('XML sitemap anonymous content access'),
'value' => $t('Access denied'),
'description' => $t('In order to allow search engines to view the XML sitemap and content on your site, the anonymous user role must have the <a href="@perm-link">%permission</a> permission.', array('@perm-link' => url('admin/config/people/permissions/' . DRUPAL_ANONYMOUS_RID, array('fragment' => 'module-node')), '%permission' => 'access content')),
'severity' => REQUIREMENT_ERROR,
);
}
if (!xmlsitemap_check_directory()) {
$requirements['xmlsitemap_directory'] = array(
'title' => $t('XML sitemap cache directory'),
'value' => $t('Not found or not writable'),
'severity' => REQUIREMENT_ERROR,
'description' => xmlsitemap_get_directory(),
);
}
$max_links = XMLSITEMAP_MAX_SITEMAP_LINKS * XMLSITEMAP_MAX_SITEMAP_CHUNKS;
if (xmlsitemap_get_link_count() > $max_links) {
$requirements['xmlsitemap_link_count'] = array(
'title' => $t('XML sitemap link count'),
'value' => xmlsitemap_get_link_count(),
'description' => $t('You have exceeded the number of links that your sitemap can contain (@num).', array('@num' => number_format($max_links))),
'severity' => REQUIREMENT_ERROR,
);
}
if (xmlsitemap_get_chunk_count() > XMLSITEMAP_MAX_SITEMAP_CHUNKS) {
$requirements['xmlsitemap_chunk_count'] = array(
'title' => $t('XML sitemap page count'),
'value' => xmlsitemap_get_chunk_count(),
'description' => $t('You have exceeded the number of sitemap pages (1,000).'),
'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 when the cached files were last generated.
$generated_last = xmlsitemap_var('generated_last');
$generated_ago = REQUEST_TIME - $generated_last;
$requirements['xmlsitemap_generated'] = array(
'title' => $t('XML sitemap'),
'value' => $generated_last ? $t('Last generated 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 (xmlsitemap_var('rebuild_needed')) {
$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 (xmlsitemap_var('regenerate_needed')) {
if ($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;
}
/**
* Check the status of all hook_requirements() from 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:status:modules')) {
$modules = $cache->data;
}
else {
$modules = array();
foreach (module_implements('requirements') as $module) {
if (strpos($module, 'xmlsitemap') !== FALSE) {
$modules[] = $module;
}
}
cache_set('xmlsitemap:status:modules', $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);
}
/**
* 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 so we can use xmlsitemap_var().
drupal_load('module', 'xmlsitemap');
// Add files directory.
xmlsitemap_check_directory();
// Insert the homepage link into the {xmlsitemap} table so we do not show an
// empty sitemap after install.
db_query("INSERT INTO {xmlsitemap} (type, id, loc, priority, changefreq, language) VALUES ('frontpage', 0, '', :priority, :changefreq, :language)", array(':priority' => xmlsitemap_var('frontpage_priority'), 'changefreq' => xmlsitemap_var('frontpage_changefreq'), ':language' => LANGUAGE_NONE));
// @todo Does the sitemap show up on first install or is it a 404 page?
}
/**
* Implements hook_enable().
*/
function xmlsitemap_enable() {
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 files directory.
xmlsitemap_clear_directory(TRUE);
}
/**
* Implements hook_schema().
*/
function xmlsitemap_schema() {
$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'),
),
);
return $schema;
}
// @todo Remove these update functions before alpha.
function xmlsitemap_update_1() {
db_drop_unique_key('xmlsitemap', 'loc');
}
function xmlsitemap_update_2() {
if (!variable_get('xmlsitemap_base_url', '')) {
variable_set('xmlsitemap_base_url', $GLOBALS['base_url']);
}
}
function xmlsitemap_update_3() {
db_add_index('xmlsitemap', 'loc', array('loc'));
}
function xmlsitemap_update_4() {
$field = array(
'type' => 'int',
'size' => 'tiny',
'default' => 1,
'unsigned' => TRUE,
);
db_change_field('xmlsitemap', 'status', 'status', $field);
db_add_index('xmlsitemap', 'status', array('status'));
}
function xmlsitemap_update_5() {
db_drop_primary_key('xmlsitemap');
db_add_primary_key('xmlsitemap', array('id', 'type'));
}
function xmlsitemap_update_6() {
if (db_column_exists('xmlsitemap', 'language')) {
db_drop_index('xmlsitemap', 'language');
db_drop_field('xmlsitemap', 'language');
}
variable_set('xmlsitemap_generated_last', variable_get('xmlsitemap_regenerate_last', 0));
variable_del('xmlsitemap_regenerate_last');
}
function xmlsitemap_update_7() {
if (xmlsitemap_load_link(array('type' => 'custom'))) {
drupal_install_modules(array('xmlsitemap_custom'));
}
variable_del('xmlsitemap_custom_links');
}
function xmlsitemap_update_8() {
$field = array(
'type' => 'float',
'default' => NULL,
);
db_add_field('xmlsitemap', 'priority_override', $field);
}
function xmlsitemap_update_9() {
$field = array(
'type' => 'int',
'default' => 0,
'unsigned' => TRUE,
'not null' => TRUE,
);
db_change_field('xmlsitemap', 'lastmod', 'lastmod', $field);
}
function xmlsitemap_update_10() {
db_update('system')
->fields(array('weight' => 1))
->condition('type', 'module')
->condition('name', 'xmlsitemap')
->execute();
}
function xmlsitemap_update_11() {
// Delete any items with NULL status and drop the status index.
db_delete('xmlsitemap')
->isNull('status')
->execute();
db_drop_index('xmlsitemap', 'status');
// Rename status to access.
$field = array(
'type' => 'int',
'size' => 'tiny',
'default' => 1,
'unsigned' => TRUE,
'not null' => TRUE,
);
db_change_field('xmlsitemap', 'status', 'access', $field);
// Add a status field.
$field = array(
'type' => 'int',
'size' => 'tiny',
'default' => 1,
);
db_add_field('xmlsitemap', 'status', $field);
db_add_index('xmlsitemap', 'access_status', array('access', 'status'));
}
function xmlsitemap_update_12() {
db_drop_index('xmlsitemap', 'access_status');
db_add_index('xmlsitemap', 'access_status_loc', array('access', 'status', 'loc'));
}
function xmlsitemap_update_13() {
db_change_field('xmlsitemap', 'priority_override', 'priority_override_value', array('type' => 'float', 'default' => NULL));
$override_field = array(
'type' => 'int',
'size' => 'tiny',
'default' => 0,
'not null' => TRUE,
);
db_add_field('xmlsitemap', 'status_override', $override_field);
db_add_field('xmlsitemap', 'priority_override', $override_field);
db_update('xmlsitemap')
->fields(array('priority_override' => 1))
->expression('priority', 'priority_override_value')
->isNotNull('priority_override_value')
->execute();
db_update('xmlsitemap')
->fields(array('priority' => 0.5))
->isNull('priority_override_value')
->execute();
db_drop_field('xmlsitemap', 'priority_override_value');
}
function xmlsitemap_update_14() {
db_add_field('xmlsitemap', 'subtype', array('type' => 'varchar', 'length' => 32, 'default' => NULL));
$fields = array('node' => 'node_type', 'menu' => 'menu_name', 'taxonomy' => 'term_vid');
foreach ($fields as $type => $field) {
if (db_column_exists('xmlsitemap', $field)) {
db_update('xmlsitemap')
->fields(array('subtype', $field))
->condition('type', $type)
->execute();
db_drop_index('xmlsitemap', $field);
db_drop_field('xmlsitemap', $field);
}
}
db_add_index('xmlsitemap', 'type_subtype', array('type', 'subtype'));
}
function xmlsitemap_update_15() {
$or = db_or();
$or->isNull('status');
$or->isNull('access');
db_delete('xmlsitemap')
->condition($or)
->execute();
$field = array(
'type' => 'int',
'size' => 'tiny',
'default' => 1,
'not null' => TRUE,
);
db_change_field('xmlsitemap', 'access', 'access', $field);
db_change_field('xmlsitemap', 'status', 'status', $field);
}
function xmlsitemap_update_16() {
$field = array(
'type' => 'varchar',
'length' => 12,
'not null' => TRUE,
'default' => '',
);
db_add_field('xmlsitemap', 'language', $field);
db_add_index('xmlsitemap', 'language', array('language'));
}
function xmlsitemap_update_17() {
db_update('xmlsitemap')
->fields(array('access' => 1))
->condition('access', 1, '>')
->execute();
db_update('xmlsitemap')
->fields(array('status' => 1))
->condition('status', 1, '>')
->execute();
}
function xmlsitemap_update_18() {
$field = array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
);
db_change_field('xmlsitemap', 'type', 'type', $field);
$field['length'] = 128;
db_change_field('xmlsitemap', 'subtype', 'subtype', $field);
}
//@todo Update {xmlsitemap}.language from '' to LANGUAGE_NONE.

View File

@@ -0,0 +1,17 @@
// $Id: xmlsitemap.js,v 1.2 2010/01/08 21:42:10 davereid Exp $
Drupal.verticalTabs = Drupal.verticalTabs || {};
Drupal.verticalTabs.xmlsitemap = function() {
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 />');
}

View File

@@ -0,0 +1,863 @@
<?php
// $Id: xmlsitemap.module,v 1.43 2010/01/24 03:57:17 davereid Exp $
/**
* @defgroup xmlsitemap XML sitemap: create sitemaps.org sitemaps.
*/
/**
* @file
* Main file for the xmlsitemap module.
*/
/**
* The maximum number of links in one sitemap chunk file.
*/
define('XMLSITEMAP_MAX_SITEMAP_LINKS', 50000);
/**
* The maximum filesize of a sitemap chunk file.
*/
define('XMLSITEMAP_MAX_SITEMAP_FILESIZE', 10485760);
/**
* The maximum number of links in one sitemap index file.
*/
define('XMLSITEMAP_MAX_SITEMAP_CHUNKS', 1000);
define('XMLSITEMAP_FREQUENCY_YEARLY', 31449600); // 60 * 60 * 24 * 7 * 52
define('XMLSITEMAP_FREQUENCY_MONTHLY', 2419200); // 60 * 60 * 24 * 7 * 4
define('XMLSITEMAP_FREQUENCY_WEEKLY', 604800); // 60 * 60 * 24 * 7
define('XMLSITEMAP_FREQUENCY_DAILY', 86400); // 60 * 60 * 24
define('XMLSITEMAP_FREQUENCY_HOURLY', 3600); // 60 * 60
define('XMLSITEMAP_FREQUENCY_ALWAYS', 60);
/**
* Implements hook_help().
*/
function xmlsitemap_help($path, $arg) {
$output = '';
switch ($path) {
case 'admin/help/xmlsitemap':
return;
case 'admin/help#xmlsitemap':
$sitemaps = xmlsitemap_get_sitemaps(TRUE);
$output .= '<p>' . format_plural(count($sitemaps), 'Your sitemap is located at !sitemap.', 'Your sitemaps are located at: !sitemaps', array('!sitemaps' => theme('item_list', array('items' => $sitemaps)), '!sitemap' => current($sitemaps))) . '</p>';
break;
case 'admin/config/search/xmlsitemap':
break;
case 'admin/config/search/xmlsitemap/rebuild':
$output .= '<p>' . t("This action rebuilds your site's XML sitemap and regenerates the cached files, and may be a lengthy process. If you just installed XML sitemap, this can be helpful to import all your site's content into the sitemap. Otherwise, this should only be used in emergencies.") . '</p>';
}
if (arg(0) == 'admin' && strpos($path, 'xmlsitemap') !== FALSE) {
if ($arg[1] == 'config' && user_access('administer xmlsitemap')) {
module_load_install('xmlsitemap');
xmlsitemap_check_status();
}
module_load_include('inc', 'xmlsitemap');
if ($blurb = _xmlsitemap_get_blurb()) {
$output .= $blurb;
}
}
return $output;
}
/**
* Implements hook_perm().
*/
function xmlsitemap_permission() {
return array(
'administer xmlsitemap' => array(
'title' => t('Administer XML sitemap settings.'),
),
);
}
/**
* Implements hook_menu().
*/
function xmlsitemap_menu() {
$items['admin/config/search/xmlsitemap'] = array(
'title' => 'XML sitemap',
'description' => 'Configure the XML sitemap.',
'page callback' => 'drupal_get_form',
'page arguments' => array('xmlsitemap_settings_form'),
'access arguments' => array('administer xmlsitemap'),
'file' => 'xmlsitemap.admin.inc',
);
$items['admin/config/search/xmlsitemap/settings'] = array(
'title' => 'Settings',
'access arguments' => array('administer xmlsitemap'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'file' => 'xmlsitemap.admin.inc',
'weight' => -10,
);
$items['admin/config/search/xmlsitemap/rebuild'] = array(
'title' => 'Rebuild',
'description' => 'Rebuild the site map.',
'page callback' => 'drupal_get_form',
'page arguments' => array('xmlsitemap_rebuild_form'),
'access arguments' => array('administer xmlsitemap'),
'type' => MENU_LOCAL_TASK,
'file' => 'xmlsitemap.admin.inc',
'weight' => 10,
);
$items['sitemap.xml'] = array(
'page callback' => 'xmlsitemap_output_chunk',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
'file' => 'xmlsitemap.pages.inc',
);
$chunks = xmlsitemap_get_chunk_count();
if ($chunks > 1) {
for ($i = 1; $i <= $chunks; $i++) {
$items['sitemap-' . $i . '.xml'] = array(
'page callback' => 'xmlsitemap_output_chunk',
'page arguments' => array((string) $i),
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
'file' => 'xmlsitemap.pages.inc',
);
}
}
$items['sitemap.xsl'] = array(
'page callback' => 'xmlsitemap_output_xsl',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
'file' => 'xmlsitemap.pages.inc',
);
return $items;
}
/**
* Implements hook_cron().
*/
function xmlsitemap_cron() {
// If there were no new or changed links, skip.
if (!variable_get('xmlsitemap_regenerate_needed', FALSE)) {
return;
}
// If the minimum sitemap lifetime hasn't been passed, skip.
$lifetime = REQUEST_TIME - variable_get('xmlsitemap_generated_last', 0);
if ($lifetime < variable_get('xmlsitemap_minimum_lifetime', 0)) {
return;
}
// Regenerate the sitemap XML files.
module_load_include('inc', 'xmlsitemap');
xmlsitemap_regenerate();
}
/**
* Implements hook_xmlsitemap_links().
*/
function xmlsitemap_xmlsitemap_links() {
// Frontpage link.
$links[] = array(
'type' => 'frontpage',
'id' => 0,
'loc' => '',
);
return $links;
}
/**
* 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'] = xmlsitemap_var('frontpage_priority');
$link['changefreq'] = xmlsitemap_var('frontpage_changefreq');
}
}
/**
* Implements hook_robotstxt().
*/
function xmlsitemap_robotstxt() {
module_load_include('inc', 'xmlsitemap');
$sitemaps = xmlsitemap_get_sitemaps();
foreach ($sitemaps as $index => $sitemap) {
$sitemaps[$index] = 'Sitemap: ' . $sitemap;
}
return $sitemaps;
}
/**
* Get an array of the current site's sitemaps.
*
* @param $links
* A boolean if TRUE, the array elements will be HTML links.
* @return
* An array of sitemaps.
*/
function xmlsitemap_get_sitemaps($links = FALSE) {
static $sitemaps = array();
if (!$sitemaps) {
$url_options = xmlsitemap_get_url_options();
$sitemap_languages = xmlsitemap_var('languages');
natsort($sitemap_languages);
foreach ($sitemap_languages as $language) {
$url_options['language'] = xmlsitemap_language_load($language);
$sitemap = url('sitemap.xml', $url_options);
$sitemaps[$language] = $links ? l($sitemap, $sitemap) : $sitemap;
}
}
return $sitemaps;
}
/**
* Return a list of commonly used parameters for url() used by XML sitemap.
*
* @see url()
*/
function xmlsitemap_get_url_options($options = array()) {
return $options + array(
'absolute' => TRUE,
'base_url' => xmlsitemap_var('base_url'),
);
}
/**
* Determine the frequency of updates to a link.
*
* @param $interval
* An interval value in seconds.
* @return
* A string representing the update frequency according to the sitemaps.org
* protocol.
*/
function xmlsitemap_get_changefreq($interval) {
if ($interval <= 0 || !is_numeric($interval)) {
return FALSE;
}
foreach (xmlsitemap_get_changefreq_options() as $value => $frequency) {
if ($interval <= $value) {
return $frequency;
}
}
return 'never';
}
/**
* Get the current number of sitemap chunks.
*/
function xmlsitemap_get_chunk_count($reset = FALSE) {
static $chunks;
if (!isset($chunks) || $reset) {
$count = max(xmlsitemap_get_link_count($reset), 1);
$chunks = ceil($count / xmlsitemap_get_chunk_size($reset));
}
return $chunks;
}
/**
* Get the current number of sitemap links.
*/
function xmlsitemap_get_link_count($reset = FALSE) {
static $count;
if (!isset($count) || $reset) {
$count = db_query("SELECT COUNT(id) FROM {xmlsitemap} WHERE access = 1 AND status = 1")->fetchField();
}
return $count;
}
/**
* Get the sitemap chunk size.
*
* This function is useful with the chunk size is set to automatic as it will
* calculate the appropriate value. Use this function instead of @code
* xmlsitemap_var('chunk_size') @endcode when the actual value is needed.
*
* @param $reset
* A boolean to reset the saved, static result. Defaults to FALSE.
* @return
* An integer with the number of links in each sitemap page.
*/
function xmlsitemap_get_chunk_size($reset = FALSE) {
static $size;
if (!isset($size) || $reset) {
$size = xmlsitemap_var('chunk_size');
if ($size === 'auto') {
$count = max(xmlsitemap_get_link_count($reset), 1); // Prevent divide by zero.
$size = min(ceil($count / 10000) * 5000, XMLSITEMAP_MAX_SITEMAP_LINKS);
}
}
return $size;
}
/**
* Recalculate the changefreq of a sitemap link.
*
* @param $link
* A sitemap link array.
*/
function xmlsitemap_recalculate_changefreq(&$link) {
$link['changefreq'] = round((($link['changefreq'] * $link['changecount']) + (REQUEST_TIME - $link['lastmod'])) / ($link['changecount'] + 1));
$link['changecount']++;
$link['lastmod'] = REQUEST_TIME;
}
/**
* Calculates the average interval between UNIX timestamps.
*
* @param $timestamps
* An array of UNIX timestamp integers.
* @return
* An integer of the average interval.
*/
function xmlsitemap_calculate_changefreq($timestamps) {
sort($timestamps);
$count = count($timestamps) - 1;
$diff = 0;
for ($i = 0; $i < $count; $i++) {
$diff += $timestamps[$i + 1] - $timestamps[$i];
}
return $count > 0 ? round($diff / $count) : 0;
}
/**
* Check if there is a visible sitemap link given a certain set of conditions.
*
* @param $conditions
* An array of values to match keyed by field.
* @param $flag
* An optional boolean that if TRUE, will set the regenerate needed flag if
* there is a match. Defaults to FALSE.
* @return
* TRUE if there is a visible link, or FALSE otherwise.
*/
function _xmlsitemap_check_changed_links(array $conditions = array(), array $updates = array(), $flag = FALSE) {
// If we are changing status or access, check for negative current values.
$conditions['status'] = (!empty($updates['status']) && empty($condition['status'])) ? 0 : 1;
$conditions['access'] = (!empty($updates['access']) && empty($condition['access'])) ? 0 : 1;
$query = db_select('xmlsitemap');
$query->addExpression('1');
foreach ($conditions as $field => $value) {
$query->condition($field, $value);
}
$query->range(0, 1);
$changed = $query->execute()->fetchField();
if ($changed && $flag) {
variable_set('xmlsitemap_regenerate_needed', TRUE);
}
return $changed;
}
/**
* Check if there is sitemap link is changed from the existing data.
*
* @param $link
* An array of the sitemap link.
* @param $original_link
* An optional array of the existing data. This should only contain the
* fields necessary for comparison. If not provided the existing data will be
* loaded from the database.
* @param $flag
* An optional boolean that if TRUE, will set the regenerate needed flag if
* there is a match. Defaults to FALSE.
* @return
* TRUE if the link is changed, or FALSE otherwise.
*/
function _xmlsitemap_check_changed_link(array $link, $original_link = NULL, $flag = FALSE) {
$changed = FALSE;
if ($original_link === NULL) {
// Load only the fields necessary for data to be changed in the sitemap.
$original_link = db_query_range("SELECT loc, access, status, lastmod, priority, changefreq, changecount, language FROM {xmlsitemap} WHERE type = :type AND id = :id", 0, 1, array(':type' => $link['type'], ':id' => $link['id']))->fetchAssoc();
}
if (!$original_link) {
if ($link['access'] && $link['status']) {
// Adding a new visible link.
$changed = TRUE;
}
}
else {
if (!($original_link['access'] && $original_link['status']) && $link['access'] && $link['status']) {
// Changing a non-visible link to a visible link.
$changed = TRUE;
}
elseif ($original_link['access'] && $original_link['status'] && array_diff_assoc($original_link, $link)) {
// Changing a visible link
$changed = TRUE;
}
}
if ($changed && $flag) {
variable_set('xmlsitemap_regenerate_needed', TRUE);
}
return $changed;
}
/**
* Load a specific sitemap link.
*
* @param $conditions
* An array of values to match keyed by field.
* @return
* An array representing the first sitemap link matching the conditions found.
*
* @todo Convert to use $type and $id as parameters.
*/
function xmlsitemap_load_link(array $conditions) {
$query = db_select('xmlsitemap', 'x');
$query->fields('x');
foreach ($conditions as $field => $value) {
$query->condition($field, $value);
}
$query->range(0, 1);
$link = $query->execute()->fetchAssoc();
// Allow other modules to respond after loading the link.
//module_invoke_all('xmlsitemap_load_link', $link, $conditions, $args);
return $link;
}
/**
* Saves or updates a sitemap link.
*
* @param $link
* An array with a sitemap link.
*/
function xmlsitemap_save_link(array $link) {
$link += array(
'access' => 1,
'status' => 1,
'status_override' => 0,
'lastmod' => 0,
'priority' => 0.5,
'priority_override' => 0,
'changefreq' => 0,
'changecount' => 0,
'language' => LANGUAGE_NONE,
);
// Allow other modules to alter the link before saving.
drupal_alter('xmlsitemap_link', $link);
// Temporary validation checks.
// @todo Remove in final?
if ($link['priority'] < 0 || $link['priority'] > 1) {
trigger_error(t('Invalid sitemap link priority %priority.<br />@link', array('%priority' => $link['priority'], '@link' => var_export($link, TRUE))), E_USER_ERROR);
}
if ($link['changecount'] < 0) {
trigger_error(t('Negative changecount value. Please report this to <a href="@516928">@516928</a>.<br />@link', array('@516928' => 'http://drupal.org/node/516928', '@link' => var_export($link, TRUE))), E_USER_ERROR);
$link['changecount'] = 0;
}
$existing = db_query_range("SELECT loc, access, status, lastmod, priority, changefreq, changecount, language FROM {xmlsitemap} WHERE type = :type AND id = :id", 0, 1, array(':type' => $link['type'], ':id' => $link['id']))->fetchAssoc();
// Check if this is a changed link and set the regenerate flag if necessary.
if (!variable_get('xmlsitemap_regenerate_needed', FALSE)) {
_xmlsitemap_check_changed_link($link, $existing, TRUE);
}
if ($existing) {
drupal_write_record('xmlsitemap', $link, array('type', 'id'));
}
else {
drupal_write_record('xmlsitemap', $link);
}
// Allow other modules to respond after saving the link.
//module_invoke_all('xmlsitemap_save_link', $link);
return $link;
}
/**
* Perform a mass update of sitemap data.
*
* If visible links are updated, this will automatically set the regenerate
* needed flag to TRUE.
*
* @param $updates
* An array of values to update fields to, keyed by field name.
* @param $conditions
* An array of values to match keyed by field.
* @return
* The number of links that were updated.
*/
function xmlsitemap_update_links($updates = array(), $conditions = array()) {
// If we are going to modify a visible sitemap link, we will need to set
// the regenerate needed flag.
if (!variable_get('xmlsitemap_regenerate_needed', FALSE)) {
_xmlsitemap_check_changed_links($conditions, $updates, TRUE);
}
// Process updates.
$query = db_update('xmlsitemap');
$query->fields($updates);
foreach ($conditions as $field => $value) {
$query->condition($field, $value);
}
return $query->execute();
}
/**
* Delete one or more sitemap links.
*
* If a visible sitemap link was deleted, this will automatically set the
* regenerate needed flag.
*
* @param $conditions
* An array of values to match keyed by field.
* @return
* The number of links that were deleted.
*/
function xmlsitemap_delete_link(array $conditions) {
if (!variable_get('xmlsitemap_regenerate_needed', TRUE)) {
_xmlsitemap_check_changed_links($conditions, array(), TRUE);
}
$query = db_delete('xmlsitemap');
foreach ($conditions as $field => $value) {
$query->condition($field, $value);
}
return $query->execute();
}
/**
* Get the filename of a specific sitemap page chunk.
*
* @param $chunk
* An integer representing the integer of the sitemap page chunk.
* @param $language
* A string with a language code.
* @return
* A file path to the expected chunk file.
*
* @todo Move to xmlsitemap.inc
*/
function xmlsitemap_get_chunk_file($chunk = 0, $language = LANGUAGE_NONE, $extension = 'xml') {
return xmlsitemap_get_directory() . "/xmlsitemap-{$language}-{$chunk}.{$extension}";
}
/**
* Implements hook_form_alter().
*
* Set the regeneration needed flag if settings are changed.
*/
function xmlsitemap_form_alter(&$form, $form_state, $form_id) {
$forms = array(
'locale_languages_overview_form', // Language settings
'xmlsitemap_settings_form', // XML sitemap settings
);
if (in_array($form_id, $forms)) {
array_unshift($form['#submit'], 'xmlsitemap_form_submit_flag_regenerate');
}
}
/**
* Submit handler; Set the regenerate needed flag if variables have changed.
*
* This function needs to be called before system_settings_form_submit() or any
* calls to variable_set().
*/
function xmlsitemap_form_submit_flag_regenerate($form, $form_state) {
foreach ($form_state['values'] as $variable => $value) {
$stored_value = variable_get($variable, 'not_a_variable');
if (is_array($value) && !empty($form_state['values']['array_filter'])) {
$value = array_keys(array_filter($value));
}
if ($stored_value != 'not_a_variable' && $stored_value != $value) {
variable_set('xmlsitemap_regenerate_needed', TRUE);
drupal_set_message(t('XML sitemap settings have been modified and the files should be regenerated. You can <a href="@run-cron">run cron manually</a> to regenerate the cached files.', array('@run-cron' => url('admin/reports/status/run-cron', array('query' => drupal_get_destination())))), 'warning', FALSE);
return;
}
}
}
/**
* Internal default variables for xmlsitemap_var().
*/
function xmlsitemap_variables() {
return array(
'xmlsitemap_rebuild_needed' => FALSE,
'xmlsitemap_regenerate_needed' => FALSE,
'xmlsitemap_generated_last' => 0,
'xmlsitemap_minimum_lifetime' => 0,
'xmlsitemap_xsl' => TRUE,
'xmlsitemap_languages' => array(language_default('language')),
'xmlsitemap_chunk_size' => 'auto',
'xmlsitemap_batch_limit' => 100,
'xmlsitemap_path' => 'xmlsitemap',
'xmlsitemap_base_url' => $GLOBALS['base_url'],
'xmlsitemap_developer_mode' => FALSE,
'xmlsitemap_frontpage_priority' => '1.0',
'xmlsitemap_frontpage_changefreq' => XMLSITEMAP_FREQUENCY_DAILY,
// Removed variables are set to NULL so they can still be deleted.
'xmlsitemap_gz' => FALSE,
'xmlsitemap_regenerate_last' => NULL,
'xmlsitemap_custom_links' => NULL,
'xmlsitemap_priority_default' => NULL,
);
}
/**
* Internal implementation of variable_get().
*/
function xmlsitemap_var($name, $default = NULL) {
$defaults = &drupal_static(__FUNCTION__);
if (!isset($defaults)) {
$defaults = xmlsitemap_variables();
}
$name = 'xmlsitemap_'. $name;
// @todo Remove when stable.
if (!isset($defaults[$name])) {
trigger_error(strtr('Default variable for %variable not found.', array('%variable' => drupal_placeholder($name))));
}
return variable_get($name, isset($default) || !isset($defaults[$name]) ? $default : $defaults[$name]);
}
/**
* Set the current user stored in $GLOBALS['user'].
*
* @todo Remove when http://drupal.org/node/287292 is fixed.
*/
function xmlsitemap_switch_user($new_user = NULL) {
global $user;
$user_original = &drupal_static(__FUNCTION__);
if (!isset($new_user)) {
if (isset($user_original)) {
// Restore the original user.
$user = $user_original;
$user_original = NULL;
drupal_save_session(TRUE);
}
else {
return FALSE;
}
}
elseif (is_numeric($new_user) && $user->uid != $new_user) {
// Get the full user object.
if (!$new_user) {
$new_user = drupal_anonymous_user();
}
elseif (!$new_user = user_load($new_user)) {
return FALSE;
}
// Backup the original user object.
if (!isset($user_original)) {
$user_original = $user;
drupal_save_session(FALSE);
}
$user = $new_user;
}
elseif (is_object($new_user) && $user->uid != $new_user->uid) {
// Backup the original user object.
if (!isset($user_original)) {
$user_original = $user;
drupal_save_session(FALSE);
}
$user = $new_user;
}
else {
return FALSE;
}
return $user;
}
/**
* Restore the user that was originally loaded.
*
* @return
* Current user.
*/
function xmlsitemap_restore_user() {
return xmlsitemap_switch_user();
}
function xmlsitemap_process_form_link_options($form, &$form_state) {
$link = &$form_state['values']['xmlsitemap'];
$fields = array('status' => 1, 'priority' => 0.5);
foreach ($fields as $field => $default) {
if ($link[$field] === 'default') {
$link[$field] = isset($link[$field . '_default']) ? $link[$field . '_default'] : $default;
$link[$field . '_override'] = 0;
}
else {
$link[$field . '_override'] = 1;
}
}
}
/**
* @todo Document this function.
* @todo Make these translatable
*/
function xmlsitemap_get_changefreq_options() {
return array(
XMLSITEMAP_FREQUENCY_ALWAYS => 'always',
XMLSITEMAP_FREQUENCY_HOURLY => 'hourly',
XMLSITEMAP_FREQUENCY_DAILY => 'daily',
XMLSITEMAP_FREQUENCY_WEEKLY => 'weekly',
XMLSITEMAP_FREQUENCY_MONTHLY => 'monthly',
XMLSITEMAP_FREQUENCY_YEARLY => 'yearly',
);
}
/**
* Returns information about supported sitemap link types.
*
* @param $type
* (optional) The link type to return information for. If omitted,
* information for all link types is returned.
* @param $reset
* (optional) Boolean whether to reset the static cache and do nothing. Only
* used for tests.
*
* @see hook_xmlsitemap_link_info()
* @see hook_xmlsitemap_link_info_alter()
*/
function xmlsitemap_get_link_info($type = NULL) {
global $language;
$link_info = &drupal_static(__FUNCTION__);
if (!isset($link_info)) {
if ($cached = cache_get('xmlsitemap:link_info:' . $language->language)) {
$link_info = $cached->data;
}
else {
$link_info = array();
foreach (module_implements('xmlsitemap_link_info') as $module) {
$module_link_info = module_invoke($module, 'xmlsitemap_link_info');
foreach ($module_link_info as $type => $info) {
$module_link_info[$type] += array(
'type' => $type,
'module' => $module,
'purge' => TRUE,
'table' => FALSE,
);
}
$link_info = array_merge($link_info, $module_link_info);
}
drupal_alter('xmlsitemap_link_info', $link_info);
cache_set('xmlsitemap:link_info:' . $language->language, $link_info);
}
}
if (isset($type)) {
return isset($link_info[$type]) ? $link_info[$type] : NULL;
}
return $link_info;
}
/**
* Implements hook_xmlsitemap_link_info().
*/
function xmlsitemap_xmlsitemap_link_info() {
return array(
'frontpage' => array(),
);
}
function xmlsitemap_get_directory() {
$directory = &drupal_static(__FUNCTION__);
if (!isset($directory)) {
$directory = file_directory_path() . '/' . variable_get('xmlsitemap_path', 'xmlsitemap');
}
return $directory;
}
/**
* Check that the sitemap files directory exists and is writable.
*/
function xmlsitemap_check_directory() {
$directory = xmlsitemap_get_directory();
$result = file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
if (!$result) {
watchdog('file system', 'The directory %directory does not exist or is not writable.', array('%directory' => $directory), WATCHDOG_ERROR);
}
return $result;
}
function xmlsitemap_clear_directory($delete = FALSE) {
$directory = xmlsitemap_get_directory();
return _xmlsitemap_delete_recursive($directory, $delete);
}
/**
* Recursively delete all files and folders in the specified filepath.
*
* This is a backport of Drupal 7's file_unmanaged_delete_recursive().
*
* Note that this only deletes visible files with write permission.
*
* @param $path
* A filepath relative to file_directory_path.
* @param $delete_root
* A boolean if TRUE will delete the $path directory afterwards.
*/
function _xmlsitemap_delete_recursive($path, $delete_root = FALSE) {
// Resolve streamwrapper URI to local path.
$path = drupal_realpath($path);
if (is_dir($path)) {
$dir = dir($path);
while (($entry = $dir->read()) !== FALSE) {
if ($entry == '.' || $entry == '..') {
continue;
}
$entry_path = $path . '/' . $entry;
file_unmanaged_delete_recursive($entry_path, TRUE);
}
$dir->close();
return $delete_root ? rmdir($path) : TRUE;
}
return file_unmanaged_delete($path);
}
/**
* Load a language object by its language code.
*
* @todo Remove when http://drupal.org/node/660736 is fixed in Drupal core.
*
* @param $language
* A language code. If not provided the default language will be returned.
* @return
* A language object.
*/
function xmlsitemap_language_load($language = LANGUAGE_NONE) {
$languages = &drupal_static(__FUNCTION__);
if (!isset($languages)) {
$languages = language_list();
$languages[LANGUAGE_NONE] = NULL;
}
return $languages[$language];
}

View File

@@ -0,0 +1,124 @@
<?php
// $Id: xmlsitemap.pages.inc,v 1.6 2010/01/24 05:54:40 davereid Exp $
/**
* @file
* Page callbacks for the xmlsitemap module.
*
* @ingroup xmlsitemap
*/
/**
* Output a sitemap page.
*
* @see xmlsitemap_file_transfer()
*/
function xmlsitemap_output_chunk($chunk = 0) {
global $language;
// @todo Remove this from the final version?
if (isset($_GET['refresh']) && user_access('administer xmlsitemap')) {
module_load_include('inc', 'xmlsitemap');
xmlsitemap_generate($chunk, $language);
}
$file = xmlsitemap_get_chunk_file($chunk, $language->language);
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();
$scheme = $scheme = variable_get('file_default_scheme', 'public');
// Transfer file in 1024 byte chunks to save memory usage.
if ($scheme && file_stream_wrapper_valid_scheme($scheme) && $fd = fopen($uri, 'rb')) {
while (!feof($fd)) {
print fread($fd, 1024);
}
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');
echo $xsl_content;
}

View File

@@ -0,0 +1,707 @@
<?php
// $Id: xmlsitemap.test,v 1.19 2010/01/24 07:12:25 davereid Exp $
/**
* @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() {
// Call parent::setUp() allowing test cases to pass further modules.
$modules = func_get_args();
$modules = array_merge(array('xmlsitemap'), $modules);
call_user_func_array(array('parent', 'setUp'), $modules);
// Ensure the files directory is created and writable during testing.
// @todo This can be removed when http://drupal.org/node/654752 is fixed.
drupal_static_reset('xmlsitemap_get_directory');
$this->checkFilesDirectory();
}
function tearDown() {
// Capture any (remaining) watchdog errors.
$this->assertNoWatchdogErrors();
// Reset the watchdog seen IDs for the next test run.
$this->getWatchdogMessages(array(), TRUE);
parent::tearDown();
}
/**
* 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.'));
}
}
protected function drupalGetSitemap($language = LANGUAGE_NONE, $regenerate = FALSE) {
if ($regenerate) {
$this->regenerateSitemap();
}
$this->drupalGet('sitemap.xml', array('language' => xmlsitemap_language_load($language)));
$this->assertResponse(200);
}
/**
* 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);
module_load_include('inc', 'xmlsitemap');
xmlsitemap_regenerate();
$this->assertTrue(variable_get('xmlsitemap_generated_last', 0) && !variable_get('xmlsitemap_regenerate_needed', FALSE), t('XML sitemaps regenerated and flag cleared.'));
}
protected function assertSitemapLink($conditions) {
$link = xmlsitemap_load_link($conditions);
$this->assertTrue(is_array($link), 'Link loaded.');
return $link;
}
protected function assertNoSitemapLink($conditions) {
$link = xmlsitemap_load_link($conditions);
$this->assertFalse($link, 'Link not loaded.');
return $link;
}
protected function assertSitemapLinkVisible() {
$links = func_get_args();
foreach ($links as $link) {
$this->assertTrue($link && $link['access'] && $link['status'], 'Sitemap link is visible.');
}
}
protected function assertSitemapLinkNotVisible() {
$links = func_get_args();
foreach ($links as $link) {
$this->assertTrue($link && !($link['access'] && $link['status']), 'Sitemap link is not visible.');
}
}
protected function assertSitemapLinkValues(array $link, array $conditions) {
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 link field @key.', array('@key' => $key)));
}
else {
// Otherwise check simple equality (==).
$this->assertEqual($link[$key], $value, t('Equal values for link field @key.', array('@key' => $key)));
}
}
}
protected function assertNotSitemapLinkValues(array $link, array $conditions) {
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 link field @key.', array('@key' => $key)));
}
else {
// Otherwise check simple equality (==).
$this->assertNotEqual($link[$key], $value, t('Not equal values for link field @key.', array('@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,
);
// Make the default path easier to read than a random string.
$link += array('loc' => $link['type'] . '-' . $link['id']);
$last_id = $link['id'] + 1;
xmlsitemap_save_link($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();
static $levels;
if (!module_exists('dblog') || $reset) {
$seen_ids = array();
return;
}
if (!isset($levels)) {
$levels = watchdog_severity_levels();
}
$query = db_select('watchdog');
$query->fields('watchdog', array('wid', 'type', 'severity', 'message', 'variables', 'timestamp'));
foreach ($conditions as $field => $value) {
if ($field == 'variables' && is_array($value)) {
$value = serialize($value);
}
$query->condition($field, $value);
}
if ($seen_ids) {
$query->condition('wid', $seen_ids, 'NOT IN');
}
$query->orderBy('timestamp', 'ASC');
$messages = $query->execute()->fetchAllAssoc('wid');
foreach ($messages as &$message) {
$message->variables = unserialize($message->variables);
if (!is_array($message->variables)) {
$message->variables = array();
}
$message->text = $message->timestamp . ' - ' . $levels[$message->severity] . ' - ' . t($message->message, $message->variables);
}
$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) {
if (in_array($message->severity, array(WATCHDOG_EMERG, 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));
}
}
}
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 setUp() {
parent::setUp('xmlsitemap');
}
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_save_link().
*/
function testSaveLink() {
$link = array('type' => 'testing', 'id' => 1, 'loc' => 'testing', 'status' => 1);
xmlsitemap_save_link($link);
$this->assertFlag('regenerate_needed', TRUE);
$link['status'] = 0;
xmlsitemap_save_link($link);
$this->assertFlag('regenerate_needed', TRUE);
$link['priority'] = 0.5;
$link['loc'] = 'new_location';
$link['status'] = 1;
xmlsitemap_save_link($link);
$this->assertFlag('regenerate_needed', TRUE);
$link['priority'] = 0.0;
xmlsitemap_save_link($link);
$this->assertFlag('regenerate_needed', TRUE);
$link['priority'] = 0.1;
xmlsitemap_save_link($link);
$this->assertFlag('regenerate_needed', TRUE);
$link['priority'] = 1.0;
xmlsitemap_save_link($link);
$this->assertFlag('regenerate_needed', TRUE);
$link['priority'] = 1;
xmlsitemap_save_link($link);
$this->assertFlag('regenerate_needed', FALSE);
$link['priority'] = 0;
xmlsitemap_save_link($link);
$this->assertFlag('regenerate_needed', TRUE);
$link['priority'] = 0.5;
xmlsitemap_save_link($link);
$this->assertFlag('regenerate_needed', TRUE);
$link['priority'] = 0.5;
$link['priority_override'] = 0;
$link['status'] = 1;
xmlsitemap_save_link($link);
$this->assertFlag('regenerate_needed', FALSE);
}
/**
* Tests for xmlsitemap_delete_link().
*/
function testDeleteLink() {
// 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_delete_link(array('loc' => 'testing1'));
$this->assertEqual($deleted, 2);
$this->assertFalse(xmlsitemap_load_link(array('type' => $link1['type'], 'id' => $link1['id'])));
$this->assertFalse(xmlsitemap_load_link(array('type' => $link2['type'], 'id' => $link2['id'])));
$this->assertTrue(xmlsitemap_load_link(array('type' => $link3['type'], 'id' => $link3['id'])));
$this->assertFlag('regenerate_needed', TRUE);
$deleted = xmlsitemap_delete_link(array('type' => $link3['type'], 'id' => $link3['id']));
$this->assertEqual($deleted, 1);
$this->assertFalse(xmlsitemap_load_link(array('type' => $link3['type'], 'id' => $link3['id'])));
$this->assertFlag('regenerate_needed', FALSE);
}
/**
* Tests for xmlsitemap_update_links().
*/
function testUpdateLinks() {
// Add our testing data.
$link1 = $this->addSitemapLink(array('subtype' => 'group1'));
$link2 = $this->addSitemapLink(array('subtype' => 'group1'));
$link3 = $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_update_links(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_update_links(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_update_links(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_update_links(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
}
}
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() {
parent::setUp('path');
$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->drupalGetSitemap(LANGUAGE_NONE, TRUE);
$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->drupalGet('sitemap.xml', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
$this->assertResponse(304);
}
/**
* Test that the sitemap will not be genereated before the lifetime expires.
*/
function testMinimumLifetime() {
$this->regenerateSitemap();
$edit = array('xmlsitemap_minimum_lifetime' => 300);
$this->drupalPost('admin/config/search/xmlsitemap', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'));
$link = $this->addSitemapLink(array('loc' => 'lifetime-test'));
drupal_cron_run();
$this->drupalGetSitemap();
$this->assertNoRaw('lifetime-test');
variable_set('xmlsitemap_generated_last', REQUEST_TIME - 300);
drupal_cron_run();
$this->drupalGetSitemap();
$this->assertRaw('lifetime-test');
xmlsitemap_delete_link(array('type' => $link['type'], 'id' => $link['id']));
drupal_cron_run();
$this->drupalGetSitemap();
$this->assertRaw('lifetime-test');
$this->drupalGetSitemap(LANGUAGE_NONE, TRUE);
$this->assertNoRaw('lifetime-test');
}
/**
* Test base URL functionality.
*/
function testBaseURL() {
$edit = array('xmlsitemap_base_url' => '');
$this->drupalPost('admin/config/search/xmlsitemap', $edit, t('Save configuration'));
$this->assertText(t('Base URL field is required.'));
$edit = array('xmlsitemap_base_url' => 'invalid');
$this->drupalPost('admin/config/search/xmlsitemap', $edit, t('Save configuration'));
$this->assertText(t('Invalid base URL.'));
$edit = array('xmlsitemap_base_url' => 'http://example.com/ ');
$this->drupalPost('admin/config/search/xmlsitemap', $edit, t('Save configuration'));
$this->assertText(t('Invalid base URL.'));
$edit = array('xmlsitemap_base_url' => 'http://example.com/');
$this->drupalPost('admin/config/search/xmlsitemap', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'));
$this->drupalGetSitemap(LANGUAGE_NONE, TRUE);
$this->assertRaw('<loc>http://example.com/</loc>');
}
/**
* Test that configuration problems are reported properly in the status report.
*/
function testStatusReport() {
// Test the rebuild flag.
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 anonymous users access to sitemap.xml.
user_role_revoke_permissions(DRUPAL_ANONYMOUS_RID, array('access content'));
$this->assertXMLSitemapProblems(t('In order to allow search engines to view the XML sitemap and content on your site, the anonymous user role must have the access content permission.'));
user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access content'));
$this->assertNoXMLSitemapProblems();
// Test chunk count > 1000.
// Test directory not writable.
}
/**
* Test that duplicate paths are skipped during generation.
*/
function testDuplicatePaths() {
$link1 = $this->addSitemapLink(array('loc' => 'duplicate'));
$link2 = $this->addSitemapLink(array('loc' => 'duplicate'));
$this->drupalGetSitemap(LANGUAGE_NONE, TRUE);
$this->assertUniqueText('duplicate');
}
}
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() {
parent::setUp('xmlsitemap', 'robotstxt');
}
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)));
}
}

View File

@@ -0,0 +1,198 @@
<?php
// $Id: xmlsitemap_custom.admin.inc,v 1.10 2010/01/24 03:57:14 davereid Exp $
/**
* @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'] = array(
'title' => t('Edit'),
'href' => 'admin/config/search/xmlsitemap/custom/edit/' . $link->id,
'query' => $destination,
);
$operations['delete'] = array(
'title' => t('Delete'),
'href' => 'admin/config/search/xmlsitemap/custom/delete/' . $link->id,
'query' => $destination,
);
$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. <a href="@link">Add custom link</a>.', array('@link' => url('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');
$link += array(
'id' => db_query("SELECT MAX(id) FROM {xmlsitemap} WHERE type = 'custom'")->fetchField() + 1,
'loc' => '',
'priority' => 0.5,
'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' => 'container',
'#attributes' => array('class' => array('form-actions')),
'#weight' => 100,
);
$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_save_link($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) {
$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_delete_link(array('type' => 'custom', 'id' => $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';
}

View File

@@ -0,0 +1,18 @@
; $Id: xmlsitemap_custom.info,v 1.4 2009/12/23 00:13:04 davereid Exp $
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 2010-01-24
version = "7.x-2.x-dev"
core = "7.x"
project = "xmlsitemap"
datestamp = "1264335489"

View File

@@ -0,0 +1,15 @@
<?php
// $Id: xmlsitemap_custom.install,v 1.2 2009/12/22 23:38:54 davereid Exp $
/**
* @file
* Install and uninstall schema and functions for the xmlsitemap_custom module.
*/
/**
* Implements hook_uninstall().
*/
function xmlsitemap_custom_uninstall() {
drupal_load('module', 'xmlsitemap');
xmlsitemap_delete_link(array('type' => 'custom'));
}

View File

@@ -0,0 +1,63 @@
<?php
// $Id: xmlsitemap_custom.module,v 1.4 2010/01/23 19:24:42 davereid Exp $
/**
* 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',
);
$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'),
'type' => MENU_CALLBACK,
'file' => 'xmlsitemap_custom.admin.inc',
);
$items['admin/config/search/xmlsitemap/custom/delete/%xmlsitemap_custom'] = array(
'title' => 'Edit custom link',
'page callback' => 'drupal_get_form',
'page arguments' => array('xmlsitemap_custom_delete_link_form', 6),
'access arguments' => array('administer xmlsitemap'),
'type' => MENU_CALLBACK,
'file' => 'xmlsitemap_custom.admin.inc',
);
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_load_link()
*/
function xmlsitemap_custom_load($id) {
return xmlsitemap_load_link(array('type' => 'custom', 'id' => $id));
}
/**
* Implements hook_xmlsitemap_link_info().
*/
function xmlsitemap_custom_xmlsitemap_link_info() {
return array(
'custom' => array(
'purge' => FALSE,
),
);
}

View File

@@ -0,0 +1,104 @@
<?php
// $Id: xmlsitemap_custom.test,v 1.6 2010/01/24 03:57:14 davereid Exp $
/**
* @file
* Unit tests for the xmlsitemap_custom module.
*/
class XMLSitemapCustomFunctionalTest extends XMLSitemapTestHelper {
protected $admin_user;
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() {
parent::setUp('xmlsitemap_custom', 'path');
$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/config/people/accounts';
$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');
$link = $this->assertSitemapLink(array('type' => 'custom', 'loc' => 'system/files'));
$this->assertSitemapLinkValues($link, 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');
$link = $this->assertSitemapLink(array('type' => 'custom', 'id' => $link['id']));
$this->assertSitemapLinkValues($link, 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');
$link = $this->assertSitemapLink(array('type' => 'custom', 'loc' => $edit['loc']));
// 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');
$link = $this->assertSitemapLink(array('type' => 'custom', 'loc' => $edit['loc']));
}
}

View File

@@ -0,0 +1,53 @@
<?php
// $Id: xmlsitemap_engines.test,v 1.5 2010/01/24 05:33:27 davereid Exp $
/**
* @file
* Unit tests for the xmlsitemap_engines module.
*/
class XMLSitemapEnginesFunctionalTest extends XMLSitemapTestHelper {
public static function getInfo() {
return array(
'name' => 'XML sitemap engines interface tests',
'description' => 'Functional tests for the XML sitemap engines module.',
'group' => 'XML sitemap',
);
}
function setUp() {
parent::setUp('xmlsitemap_engines', 'xmlsitemap_engines_test');
$this->admin_user = $this->drupalCreateUser(array('access content', 'administer xmlsitemap'));
$this->drupalLogin($this->admin_user);
variable_set('xmlsitemap_generated_last', REQUEST_TIME);
}
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.'));
xmlsitemap_engines_cron();
$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(t('Invalid custom URL an-invalid-url.'));
$this->assertNoText(t('The configuration options have been saved.'));
$edit = array('xmlsitemap_engines_custom_urls' => url('ping', array('absolute' => TRUE)));
$this->drupalPost('admin/config/search/xmlsitemap/engines', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'));
xmlsitemap_engines_cron();
$this->assertWatchdogMessage(array('type' => 'xmlsitemap', 'message' => 'Submitted the sitemap to %url and received response @code.'));
$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' => url('ping', array('absolute' => TRUE, 'query' => array('sitemap' => '[sitemap]'))));
$this->drupalPost('admin/config/search/xmlsitemap/engines', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'));
}
}

View File

@@ -0,0 +1,15 @@
; $Id: xmlsitemap_engines_test.info,v 1.3 2009/12/22 23:56:59 davereid Exp $
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 2010-01-24
version = "7.x-2.x-dev"
core = "7.x"
project = "xmlsitemap"
datestamp = "1264335489"

View File

@@ -0,0 +1,43 @@
<?php
// $Id: xmlsitemap_engines_test.module,v 1.5 2010/01/24 05:33:27 davereid Exp $
/**
* 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']));
}
}

View File

@@ -0,0 +1,66 @@
<?php
// $Id: xmlsitemap_engines.admin.inc,v 1.3 2010/01/24 03:57:19 davereid Exp $
/**
* @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(300, 900, 1800, 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 custom submission 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'),
);
$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 custom URL %url.', array('%url' => $custom_url)));
}
}
$form_state['values']['xmlsitemap_engines_custom_urls'] = implode("\n", $custom_urls);
}

View File

@@ -0,0 +1,35 @@
<?php
// $Id: xmlsitemap_engines.api.php,v 1.2 2009/12/22 23:38:54 davereid Exp $
/**
* @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".
*/

View File

@@ -0,0 +1,19 @@
; $Id: xmlsitemap_engines.info,v 1.4 2009/12/22 23:56:59 davereid Exp $
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 2010-01-24
version = "7.x-2.x-dev"
core = "7.x"
project = "xmlsitemap"
datestamp = "1264335489"

View File

@@ -0,0 +1,69 @@
<?php
// $Id: xmlsitemap_engines.install,v 1.5 2010/01/18 07:46:29 davereid Exp $
/**
* @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');
}
/**
* Filter the xmlsitemap_engines_submit variable.
*/
function xmlsitemap_engines_update_1() {
variable_set('xmlsitemap_engines_submit', array_filter(variable_get('xmlsitemap_engines_submit', array())));
}
/**
* Rename the xmlsitemap_engines_engines variable to xmlsitemap_engines_submit.
*/
function xmlsitemap_engines_update_2() {
variable_set('xmlsitemap_engines_engines', variable_get('xmlsitemap_engines_submit', array()));
variable_del('xmlsitemap_engines_submit');
}
/**
* Increase the module weight so it always runs after sitemap generation.
*/
function xmlsitemap_engines_update_3() {
db_update('system')
->fields(array('weight' => 2))
->condition('type', 'module')
->condition('name', 'xmlsitemap_engines')
->execute();
}
/**
* Update Windows Live search to Bing.
*/
function xmlsitemap_engines_update_4() {
$engines = variable_get('xmlsitemap_engines_engines', array());
$index = array_search('windows_live', $engines);
if ($index !== FALSE) {
$engines[$index] = 'bing';
}
variable_set('xmlsitemap_engines_engines', $engines);
}

View File

@@ -0,0 +1,210 @@
<?php
// $Id: xmlsitemap_engines.module,v 1.7 2009/12/22 23:43:57 davereid Exp $
/**
* Implements hook_help().
*/
function xmlsitemap_engines_help($path, $arg) {
$output = '';
switch ($path) {
case 'admin/config/search/xmlsitemap/engines':
if (!module_exists('site_verify')) {
$output .= 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'));
}
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_submit_access() {
// Skip if the site is offline since search engines will not be able to
// access the site's content.
if (variable_get('site_offline', 0)) {
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.
*/
function xmlsitemap_engines_submit_engines() {
$sitemaps = xmlsitemap_get_sitemaps();
$engines = variable_get('xmlsitemap_engines_engines', array());
$engine_info = xmlsitemap_engines_get_engine_info();
foreach ($engines as $engine) {
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, $sitemaps = array()) {
foreach ($sitemaps as $sitemap) {
$url = xmlsitemap_engines_prepare_url($url, $sitemap);
$request = drupal_http_request($url);
watchdog('xmlsitemap', 'Submitted the sitemap to %url and received response @code.', array('%url' => $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, $reset = FALSE) {
global $language;
static $engines;
if ($reset) {
$engines = NULL;
}
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]',
);
$engines['yahoo'] = array(
'name' => t('Yahoo!'),
'url' => 'http://search.yahooapis.com/SiteExplorerService/V1/ping?sitemap=[sitemap]',
);
$engines['ask'] = array(
'name' => t('Ask.com'),
'url' => 'http://submissions.ask.com/ping?sitemap=[sitemap]',
);
$engines['bing'] = array(
'name' => t('Bing (formerly Live Search)'),
'url' => 'http://www.bing.com/webmaster/ping.aspx?siteMap=[sitemap]',
);
$engines['moreover'] = array(
'name' => t('Moreover'),
'url' => 'http://api.moreover.com/ping?u=[sitemap]',
);
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;
}

View File

@@ -0,0 +1,16 @@
; $Id: xmlsitemap_i18n.info,v 1.3 2009/12/22 23:56:59 davereid Exp $
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 2010-01-24
version = "7.x-2.x-dev"
core = "7.x"
project = "xmlsitemap"
datestamp = "1264335489"

View File

@@ -0,0 +1,69 @@
<?php
// $Id: xmlsitemap_i18n.module,v 1.4 2009/12/23 22:29:16 davereid Exp $
/**
* 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_form_FORM_ID_alter().
*
* Allow the user to have multilingual sitemaps.
*/
function xmlsitemap_i18n_form_xmlsitemap_settings_form_alter(&$form, $form_state) {
$form['xmlsitemap']['xmlsitemap_languages']['#options'] = locale_language_list();
$form['xmlsitemap']['xmlsitemap_languages']['#description'] = t("Each language's sitemap will respect the <a href=\"@i18n-settings\">multilingual content selection mode</a>.", array('@i18n-settings' => url('admin/settings/language/i18n')));
}
/**
* Implements hook_query_TAG_alter().
*
* @see i18n_db_rewrite_where()
*/
function xmlsitemap_i18n_query_xmlsitemap_alter(QueryAlterableInterface $query) {
// Get languages to simplify query building.
$mode = variable_get('i18n_selection_mode', 'simple');
$current = $query->getMetaData('language')->language;
$default = i18n_default_language();
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;
}
}

View File

@@ -0,0 +1,129 @@
<?php
// $Id: xmlsitemap_i18n.test,v 1.5 2010/01/24 03:56:51 davereid Exp $
/**
* @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() {
// Call parent::setUp() allowing test cases to pass further modules.
$modules = func_get_args();
$modules = array_merge(array('locale', 'translation', 'i18n', 'xmlsitemap_i18n'), $modules);
call_user_func_array(array('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');
variable_set('xmlsitemap_languages', array('en', 'fr'));
variable_set('language_negotiation', LANGUAGE_NEGOTIATION_PATH);
}
}
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'),
);
}
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('en');
$this->assertRawSitemapLinks($node, $node_en, $node_fr, $link, $link_en, $link_fr);
$this->drupalGetSitemap('fr');
$this->assertRawSitemapLinks($node, $node_en, $node_fr, $link, $link_en, $link_fr);
variable_set('i18n_selection_mode', 'simple');
$this->regenerateSitemap();
$this->drupalGetSitemap('en');
$this->assertRawSitemapLinks($node, $node_en, $link, $link_en);
$this->assertNoRawSitemapLinks($node_fr, $link_fr);
$this->drupalGetSitemap('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('en');
$this->assertRawSitemapLinks($node, $node_en, $link, $link_en);
$this->assertNoRawSitemapLinks($node_fr, $link_fr);
$this->drupalGetSitemap('fr');
$this->assertRawSitemapLinks($node, $node_en, $node_fr, $link, $link_en, $link_fr);
variable_set('i18n_selection_mode', 'default');
$this->regenerateSitemap();
$this->drupalGetSitemap('en');
$this->assertRawSitemapLinks($node, $node_en, $link, $link_en);
$this->assertNoRawSitemapLinks($node_fr, $link_fr);
$this->drupalGetSitemap('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('en');
$this->assertRawSitemapLinks($node_en, $link, $link_en);
$this->assertNoRawSitemapLinks($node, $node_fr, $link_fr);
$this->drupalGetSitemap('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'),
);
}
function setUp() {
parent::setUp('xmlsitemap_node');
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'));
$node = node_load($node->nid, NULL, TRUE);
$this->assertIdentical($node->xmlsitemap['language'], 'en');
$this->drupalPost('node/' . $node->nid . '/edit', array('language' => 'fr'), t('Save'));
$node = node_load($node->nid, NULL, TRUE);
$this->assertIdentical($node->xmlsitemap['language'], 'fr');
}
}

View File

@@ -0,0 +1,16 @@
; $Id: xmlsitemap_menu.info,v 1.3 2009/12/22 23:56:59 davereid Exp $
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
; Information added by drupal.org packaging script on 2010-01-24
version = "7.x-2.x-dev"
core = "7.x"
project = "xmlsitemap"
datestamp = "1264335489"

View File

@@ -0,0 +1,69 @@
<?php
// $Id: xmlsitemap_menu.install,v 1.3 2010/01/18 07:46:28 davereid Exp $
/**
* @file
* Install and uninstall schema and functions for the xmlsitemap_menu module.
*/
/**
* Implements hook_uninstall().
*/
function xmlsitemap_menu_uninstall() {
// Remove variables.
drupal_load('module', 'xmlsitemap_menu');
$variables = array_keys(xmlsitemap_menu_variables());
foreach ($variables as $variable) {
variable_del($variable);
}
}
// @todo Remove these update functions before alpha.
function xmlsitemap_menu_update_1() {
$value = xmlsitemap_menu_var('menus');
variable_set('xmlsitemap_menu_menus', array_filter($value));
}
function xmlsitemap_menu_update_2() {
$field = array(
'description' => 'The {menu_links}.menu_name of this menu link.',
'type' => 'varchar',
'length' => 32,
'default' => NULL,
);
db_add_field('xmlsitemap', 'menu_name', $field);
db_add_index('xmlsitemap', 'menu_name', array('menu_name'));
db_query("UPDATE {xmlsitemap} SET menu_name = (SELECT menu_name FROM {menu_links} WHERE mlid = {xmlsitemap}.id) WHERE type = 'menu'");
}
function xmlsitemap_menu_update_3() {
$menus = variable_get('xmlsitemap_menu_menus', array());
foreach ($menus as $menu) {
variable_set('xmlsitemap_menu_status_' . $menu, TRUE);
}
variable_del('xmlsitemap_menu_menus');
}
function xmlsitemap_menu_update_4() {
}
function xmlsitemap_menu_update_5() {
}
// Skip to 6 since I was stupid and had xmlsitemap_menu_update_5() in xmlsitemap_node.install
function xmlsitemap_menu_update_6() {
db_update('system')
->fields(array('weight' => 0))
->condition('type', 'module')
->condition('name', 'xmlsitemap_menu')
->execute();
}
function xmlsitemap_menu_update_7() {
$menus = array_keys(menu_get_menus());
foreach ($menus as $menu) {
if (variable_get('xmlsitemap_menu_priority_' . $menu, 'default') === 'default') {
variable_set('xmlsitemap_menu_priority_' . $menu, 0.5);
}
}
}

View File

@@ -0,0 +1,339 @@
<?php
// $Id: xmlsitemap_menu.module,v 1.7 2010/01/20 03:34:33 davereid Exp $
/**
* 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_menu_get_menus()) {
// Set the global user variable to the anonymous user.
xmlsitemap_switch_user(0);
$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));
foreach ($mlids as $mlid) {
$menu_item = xmlsitemap_menu_menu_link_load($mlid);
$link = xmlsitemap_menu_create_link($menu_item);
xmlsitemap_save_link($link);
}
// Set the global user variable back to the original user.
xmlsitemap_restore_user();
}
}
/**
* Implements hook_xmlsitemap_links().
*/
function xmlsitemap_menu_xmlsitemap_links($offset = 0, $limit = 0) {
$links = array();
if ($menus = xmlsitemap_menu_get_menus()) {
// Set the global user variable to the anonymous user.
xmlsitemap_switch_user(0);
$sql = "SELECT ml.mlid FROM {menu_links} ml WHERE ml.mlid > :mlid AND ml.menu_name IN (:menus) ORDER BY ml.mlid";
$args = array(':mlid' => $offset, ':menus' => $menus);
$mlids = ($limit ? db_query_range($sql, 0, $limit, $args) : db_query($sql, $args));
foreach ($mlids as $mlid) {
$menu_item = xmlsitemap_menu_menu_link_load($mlid);
$links[] = xmlsitemap_menu_create_link($menu_item);
}
// Set the global user variable back to the original user.
xmlsitemap_restore_user();
}
return $links;
}
/**
* Implements hook_xmlsitemap_links_batch_info().
*/
function xmlsitemap_menu_xmlsitemap_links_batch_info() {
$menus = xmlsitemap_menu_get_menus();
return array(
'max' => $menus ? db_query("SELECT COUNT(ml.mlid) FROM {menu_links} ml WHERE ml.menu_name IN (:menus)", array(':menus' => $menus))->fetchField() : 0,
);
}
/**
* Implements hook_xmlsitemap_link_info().
*/
function xmlsitemap_menu_xmlsitemap_link_info() {
return array(
'menu' => array(
'purge' => TRUE,
'table' => 'menu_links',
'id' => 'mlid',
'subtype' => 'menu_name',
'subtypes' => xmlsitemap_menu_get_menus(),
),
);
}
/**
* Load a menu link and its associated sitemap link data.
*/
function xmlsitemap_menu_menu_link_load($mlid) {
$menu_item = menu_link_load($mlid);
if ($data = xmlsitemap_load_link(array('type' => 'menu', 'id' => $mlid))) {
$menu_item['xmlsitemap'] = $data;
}
return $menu_item;
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Show a summary of menus on the XML sitemap settings page.
*/
function xmlsitemap_menu_form_xmlsitemap_settings_form_alter(&$form, $form_state) {
$type = array(
'type' => 'menu',
'title' => t('Menus'),
'item_title' => t('Menu'),
'access' => user_access('administer menu'),
);
$menus = menu_get_menus();
foreach ($menus as $menu => $name) {
$menus[$menu] = array(
'name' => $name,
'link' => 'admin/build/menu-customize/' . $menu . '/edit',
'status' => variable_get('xmlsitemap_menu_status_' . $menu, 0),
'priority' => variable_get('xmlsitemap_menu_priority_' . $menu, 0.5),
);
}
xmlsitemap_add_form_type_summary($form, $type, $menus);
$form['menu']['#weight'] = 40;
}
/**
* Implements hook_form_FORM_ID_alter().
*
* @see menu_edit_menu()
* @see xmlsitemap_menu_menu_edit_menu_submit()
*/
function xmlsitemap_menu_form_menu_edit_menu_alter(&$form, $form_state) {
$menu = isset($form['menu_name']['#value']) ? $form['menu_name']['#value'] : '';
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
$options = array(
'status' => variable_get('xmlsitemap_menu_status_' . $menu, 0),
'priority' => variable_get('xmlsitemap_menu_priority_' . $menu, 0.5),
);
xmlsitemap_add_form_type_options($form, 'menu', $options);
// @todo Enable this feature:
//$form['xmlsitemap']['xmlsitemap_menu_calculate_priority'] = array(
// '#type' => 'checkbox',
// '#title' => t('Calculate priority based on menu item depth and weight.'),
// '#default_value' => variable_get('xmlsitemap_menu_calculate_priority_' . $menu, FALSE),
//);
$form['submit'] += array('#weight' => 50);
if (isset($form['delete'])) {
$form['delete'] += array('#weight' => 51);
}
$form['#submit'][] = 'xmlsitemap_menu_menu_edit_menu_submit';
}
/**
* Form submit handler; update settings when a menu is saved.
*/
function xmlsitemap_menu_menu_edit_menu_submit($form, $form_state) {
$menu = $form_state['values']['menu_name'];
$new_priority = $form_state['values']['xmlsitemap_menu_priority'];
$new_status = $form_state['values']['xmlsitemap_menu_status'];
if ($new_status != variable_get('xmlsitemap_menu_status_' . $menu, 0)) {
xmlsitemap_update_links(array('status' => $new_status), array('type' => 'menu', 'subtype' => $menu, 'status_override' => 0));
}
if ($new_priority != variable_get('xmlsitemap_menu_priority_' . $menu, 0.5)) {
xmlsitemap_update_links(array('priority' => $new_priority), array('type' => 'menu', 'subtype' => $menu, 'priority_override' => 0));
}
variable_set('xmlsitemap_menu_priority_' . $menu, $new_priority);
variable_set('xmlsitemap_menu_status_' . $menu, $new_status);
}
/**
* Implements hook_form_FORM_ID_alter().
*
* @see menu_delete_menu_confirm()
* @see xmlsitemap_menu_form_menu_delete_menu_confirm_submit()
*/
function xmlsitemap_menu_form_menu_delete_menu_confirm_alter(&$form, $form_state) {
$form['#submit'][] = 'xmlsitemap_menu_form_menu_delete_menu_confirm_submit';
}
/**
* Form submit handler; delete sitemap links when a menu is deleted.
*/
function xmlsitemap_menu_form_menu_delete_menu_confirm_submit($form, $form_state) {
$menu = $form['#menu']['menu_name'];
xmlsitemap_delete_link(array('type' => 'menu', 'subtype' => $menu));
variable_del('xmlsitemap_menu_status_' . $menu);
variable_del('xmlsitemap_menu_priority_ ' . $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) {
// foreach (element_children($form) as $mlid) {
// if (isset($form[$mlid]['#item'])) {
// $menu_item = menu_link_load($form[$mlid]['#item']['mlid'], TRUE);
// xmlsitemap_menu_item_update($menu_item);
// }
// }
//}
/**
* Implements hook_form_FORM_ID_alter().
*
* @see menu_item_delete_form()
*/
function xmlsitemap_menu_form_menu_item_delete_form_alter(&$form, $form_state) {
$form['#submit'][] = 'xmlsitemap_menu_menu_item_delete_form_submit';
}
/**
* Form submit callback; delete the sitemap link when a menu item is deleted.
*/
function xmlsitemap_menu_menu_item_delete_form_submit($form, $form_state) {
xmlsitemap_menu_item_delete($form['#item']);
}
/**
* Implements hook_form_FORM_ID_alter().
*
* @see menu_edit_item()
*/
function xmlsitemap_menu_form_menu_edit_item_alter(&$form, $form_state) {
$form['#submit'][] = 'xmlsitemap_menu_menu_edit_item_submit';
}
/**
* Form submit callback; update the sitemap link when a menu item is updated.
*/
function xmlsitemap_menu_menu_edit_item_submit($form, $form_state) {
xmlsitemap_switch_user(0);
$menu_item = xmlsitemap_menu_menu_link_load($form_state['values']['menu']['mlid']);
xmlsitemap_restore_user();
xmlsitemap_menu_item_update($menu_item);
}
function xmlsitemap_menu_item_update(array $menu_item) {
$link = xmlsitemap_menu_create_link($menu_item);
xmlsitemap_save_link($link);
}
function xmlsitemap_menu_item_delete(array $menu_item) {
xmlsitemap_delete_link(array('type' => 'menu', 'id' => $menu_item['mlid']));
}
/**
* Fetch an array of menus to be included in the sitemap.
*/
function xmlsitemap_menu_get_menus() {
$menus = array_keys(menu_get_menus());
foreach ($menus as $index => $menu) {
if (!variable_get('xmlsitemap_menu_status_' . $menu, 0)) {
unset($menus[$index]);
}
}
return $menus;
}
/**
* 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();
}
$menu_item['xmlsitemap'] += array(
'type' => 'menu',
'id' => $menu_item['mlid'],
'loc' => $menu_item['link_path'],
'status' => variable_get('xmlsitemap_menu_status_' . $menu_item['menu_name'], 0),
'status_default' => variable_get('xmlsitemap_menu_status_' . $menu_item['menu_name'], 0),
'status_override' => 0,
'priority' => variable_get('xmlsitemap_menu_priority_' . $menu_item['menu_name'], 0.5),
'priority_default' => variable_get('xmlsitemap_menu_priority_' . $menu_item['menu_name'], 0.5),
'priority_override' => 0,
);
// The following values must always be checked because they are volatile.
$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(
// Deprecated variables set to NULL so they are still removed on uninstall.
'xmlsitemap_menu_menus' => NULL,
'xmlsitemap_menu_calculate_priority' => NULL,
);
$menus = array_keys(menu_get_menus());
foreach ($menus as $menu) {
$defaults['xmlsitemap_menu_status_' . $menu] = 0;
$defaults['xmlsitemap_menu_priority_' . $menu] = 0.5;
$defaults['xmlsitemap_menu_calculate_priority_' . $menu] = FALSE;
}
return $defaults;
}
/**
* Internal implementation of variable_get().
*/
function xmlsitemap_menu_var($name, $default = NULL) {
static $defaults = NULL;
if (!isset($defaults)) {
$defaults = xmlsitemap_menu_variables();
}
$name = 'xmlsitemap_menu_' . $name;
// @todo Remove when stable.
if (!isset($defaults[$name])) {
trigger_error(t('Default variable for %variable not found.', array('%variable' => $name)));
}
return variable_get($name, isset($default) || !isset($defaults[$name]) ? $default : $defaults[$name]);
}

View File

@@ -0,0 +1,16 @@
; $Id: xmlsitemap_node.info,v 1.6 2010/01/18 23:15:38 davereid Exp $
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 2010-01-24
version = "7.x-2.x-dev"
core = "7.x"
project = "xmlsitemap"
datestamp = "1264335489"

View File

@@ -0,0 +1,62 @@
<?php
// $Id: xmlsitemap_node.install,v 1.12 2010/01/18 07:46:27 davereid Exp $
/**
* @file
* Install and uninstall schema and functions for the xmlsitemap_node module.
*/
/**
* Implements hook_uninstall().
*/
function xmlsitemap_node_uninstall() {
// Remove variables.
drupal_load('module', 'xmlsitemap_node');
$variables = array_keys(xmlsitemap_node_variables());
foreach ($variables as $variable) {
variable_del($variable);
}
}
// @todo Remove these update functions before alpha.
function xmlsitemap_node_update_1() {
$field = array(
'description' => "The {node_type}.type of this link's node.",
'type' => 'varchar',
'length' => 32,
);
db_add_field('xmlsitemap', 'node_type', $field);
db_add_index('xmlsitemap', 'node_type', array('node_type'));
db_query("UPDATE {xmlsitemap} SET node_type = (SELECT type FROM {node} WHERE nid = {xmlsitemap}.id) WHERE type = 'node'");
}
function xmlsitemap_node_update_2() {
$node_types = array_keys(node_get_types('names'));
foreach ($node_types as $node_type) {
if (variable_get('xmlsitemap_node_priority_' . $node_type, 'default') === 'default') {
variable_set('xmlsitemap_node_priority_' . $node_type, 0.5);
}
}
}
function xmlsitemap_node_update_3() {
db_update('system')
->fields(array('weight' => 0))
->condition('type', 'module')
->condition('name', 'xmlsitemap_node')
->execute();
}
function xmlsitemap_node_update_4() {
return array();
}
/**
* Update node languages.
*
* @after xmlsitemap_update_16()
* Must run after the {xmlsitemap}.language field has been added.
*/
function xmlsitemap_node_update_5() {
db_query("UPDATE {xmlsitemap} SET language = (SELECT {node}.language FROM {node} WHERE {node}.nid = {xmlsitemap}.id) WHERE {xmlsitemap}.type = 'node'");
}

View File

@@ -0,0 +1,17 @@
// $Id: xmlsitemap_node.js,v 1.2 2009/12/22 23:38:54 davereid Exp $
Drupal.verticalTabs = Drupal.verticalTabs || {};
Drupal.verticalTabs.xmlsitemap = function() {
var vals = [];
// Inclusion select field.
var status = $('#edit-xmlsitemap-node-status option:selected').text();
vals.push(Drupal.t('Inclusion: @value', { '@value': status }));
// Priority select field.
var priority = $('#edit-xmlsitemap-node-priority option:selected').text();
vals.push(Drupal.t('Priority: @value', { '@value': priority }));
return vals.join('<br />');
}

View File

@@ -0,0 +1,354 @@
<?php
// $Id: xmlsitemap_node.module,v 1.33 2010/01/24 04:31:32 davereid Exp $
/**
* 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_node_get_types()) {
$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));
foreach ($nids as $nid) {
$node = node_load($nid, NULL, TRUE);
$link = xmlsitemap_node_create_link($node);
xmlsitemap_save_link($link);
}
}
}
/**
* Implements hook_nodeapi().
*/
function xmlsitemap_node_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
switch ($op) {
case 'load':
if ($data = xmlsitemap_load_link(array('type' => 'node', 'id' => $node->nid))) {
return array('xmlsitemap' => $data);
}
break;
case 'insert':
$link = xmlsitemap_node_create_link($node);
xmlsitemap_save_link($link);
break;
case 'update':
$link = xmlsitemap_node_create_link($node);
if (!empty($node->revision)) {
// Update the change frequency.
xmlsitemap_recalculate_changefreq($link);
}
xmlsitemap_save_link($link);
break;
case 'delete':
xmlsitemap_delete_link(array('type' => 'node', 'id' => $node->nid));
break;
}
}
/**
* Implements hook_comment().
*/
function xmlsitemap_node_comment($comment, $op) {
switch ($op) {
case 'delete':
case 'publish':
case 'unpublish':
$comment = (object) $comment;
if ($node = node_load($comment->nid, NULL, TRUE)) {
$link = xmlsitemap_node_create_link($node);
if ($op == 'publish') {
xmlsitemap_recalculate_changefreq($link);
}
xmlsitemap_save_link($link);
}
break;
}
}
/**
* Implements hook_node_type().
*/
function xmlsitemap_node_node_type($op, $info) {
if ($op == 'delete') {
variable_del('xmlsitemap_node_status_' . $info->type);
variable_del('xmlsitemap_node_priority_' . $info->type);
//xmlsitemap_delete_link(array('type' => 'node', 'subtype' => $info->type));
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Show a summary of content types on the XML sitemap settings page.
*/
function xmlsitemap_node_form_xmlsitemap_settings_form_alter(&$form, $form_state) {
$type = array(
'type' => 'node',
'title' => t('Content types'),
'item_title' => t('Content type'),
'access' => user_access('administer content types'),
);
$node_types = node_type_get_names();
foreach ($node_types as $node_type => $node_type_name) {
$node_types[$node_type] = array(
'name' => drupal_ucfirst($node_type_name),
'link' => 'admin/content/node-type/' . str_replace('_', '-', $node_type),
'status' => variable_get('xmlsitemap_node_status_' . $node_type, 0),
'priority' => variable_get('xmlsitemap_node_priority_' . $node_type, 0.5),
);
}
xmlsitemap_add_form_type_summary($form, $type, $node_types);
$form['node']['#weight'] = 30;
}
/**
* Implements hook_form_FORM_ID_alter().
*
* @see node_type_form()
* @see xmlsitemap_add_form_type_options()
*/
function xmlsitemap_node_form_node_type_form_alter(&$form, $form_state) {
$node_type = isset($form['#node_type']->type) ? $form['#node_type']->type : '';
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
$options = array(
'status' => variable_get('xmlsitemap_node_status_' . $node_type, 0),
'priority' => variable_get('xmlsitemap_node_priority_' . $node_type, 0.5),
);
xmlsitemap_add_form_type_options($form, 'node', $options);
$form['xmlsitemap']['#attached']['js']['vertical-tabs'] = drupal_get_path('module', 'xmlsitemap_node') . '/xmlsitemap_node.js';
// Add our submit handler before node_type_form_submit() so we can compare
// the old and new values.
array_unshift($form['#submit'], 'xmlsitemap_node_type_form_submit');
}
function xmlsitemap_node_type_form_submit($form, $form_state) {
$node_type = $form_state['values']['old_type'];
$new_status = $form_state['values']['xmlsitemap_node_status'];
$new_priority = $form_state['values']['xmlsitemap_node_priority'];
$new_type = $form_state['values']['type'];
if ($new_status != variable_get('xmlsitemap_node_status_' . $node_type, 0)) {
xmlsitemap_update_links(array('status' => $new_status), array('type' => 'node', 'subtype' => $node_type, 'status_override' => 0));
}
if ($new_priority != variable_get('xmlsitemap_node_priority_' . $node_type, 0.5)) {
xmlsitemap_update_links(array('priority' => $new_priority), array('type' => 'node', 'subtype' => $node_type, 'priority_override' => 0));
}
if ($node_type != $new_type) {
xmlsitemap_update_links(array('subtype' => $new_type), array('type' => 'node', 'subtype' => $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_alter(&$form, $form_state, $form_id) {
if (!empty($form['#node_edit_form'])) {
$node = clone $form['#node'];
if (!isset($node->nid)) {
// Handle new nodes that do not have a value for nid yet.
$node->nid = NULL;
}
$link = xmlsitemap_node_create_link($node);
// Add the link options.
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
xmlsitemap_add_form_link_options($form, $link);
$form['xmlsitemap']['#access'] |= user_access('administer nodes');
$form['xmlsitemap']['#weight'] = 30;
if (user_access('administer content types')) {
$form['xmlsitemap']['#description'] = t('The default XML sitemap settings for this content type can be changed <a href="@link-type">here</a>.', array('@link-type' => url('admin/content/node-type/' . $node->type, array('query' => drupal_get_destination()))));
}
//// The following values should not exist for new nodes.
//if ($node->nid) {
// $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'],
// );
//}
}
}
/**
* Implements hook_xmlsitemap_links().
*/
function xmlsitemap_node_xmlsitemap_links($offset = 0, $limit = 0) {
$links = array();
if ($types = xmlsitemap_node_get_types()) {
$sql = "SELECT n.nid FROM {node} n WHERE n.nid > :offset AND n.type IN (:types) ORDER BY n.nid";
$args = array(':offset' => $offset, ':types' => $types);
$nids = ($limit ? db_query_range($sql, 0, $limit, $args) : db_query($sql, $args));
foreach ($nids as $nid) {
$node = node_load($nid, NULL, TRUE);
$links[] = xmlsitemap_node_create_link($node);
}
}
return $links;
}
/**
* Implements hook_xmlsitemap_links_batch_info().
*/
function xmlsitemap_node_xmlsitemap_links_batch_info() {
$types = xmlsitemap_node_get_types();
return array(
'max' => $types ? db_query("SELECT COUNT(n.nid) FROM {node} n WHERE n.type IN (:types)", array(':types' => $types))->fetchField() : 0,
);
}
/**
* Implements hook_xmlsitemap_link_info().
*/
function xmlsitemap_node_xmlsitemap_link_info() {
return array(
'node' => array(
'purge' => TRUE,
'table' => 'node',
'id' => 'nid',
'subtype' => 'type',
'subtypes' => xmlsitemap_node_get_types(),
),
);
}
/**
* Fetch an array of node types to be included in the sitemap.
*/
function xmlsitemap_node_get_types() {
$node_types = array_keys(node_type_get_names());
foreach ($node_types as $index => $node_type) {
if (!variable_get('xmlsitemap_node_status_' . $node_type, 0)) {
unset($node_types[$index]);
}
}
return $node_types;
}
/**
* Implements hook_content_extra_fields().
*
* This is so that the XML sitemap fieldset is sortable on the node edit form.
*/
function xmlsitemap_node_content_extra_fields() {
$extras['xmlsitemap'] = array(
'label' => t('XML sitemap'),
'description' => t('XML sitemap module form.'),
'weight' => 30,
);
return $extras;
}
/**
* 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($node) {
static $timestamps = array();
if (!isset($timestamps[$node->nid])) {
$timestamps[$node->nid] = db_query("SELECT c.timestamp FROM {comments} c WHERE c.nid = %d AND c.status = %d UNION ALL SELECT nr.timestamp FROM {node_revisions} nr WHERE nr.nid = %d", $node->nid, COMMENT_PUBLISHED, $node->nid)->fetchCol();
}
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(&$node) {
if (!isset($node->xmlsitemap)) {
$node->xmlsitemap = array();
}
$node->xmlsitemap += array(
'type' => 'node',
'id' => $node->nid,
'subtype' => $node->type,
'loc' => 'node/'. $node->nid,
'status' => variable_get('xmlsitemap_node_status_' . $node->type, 0),
'status_default' => variable_get('xmlsitemap_node_status_' . $node->type, 0),
'status_override' => 0,
'priority' => variable_get('xmlsitemap_node_priority_' . $node->type, 0.5),
'priority_default' => variable_get('xmlsitemap_node_priority_' . $node->type, 0.5),
'priority_override' => 0,
'changefreq' => $node->nid ? xmlsitemap_calculate_changefreq(xmlsitemap_node_get_timestamps($node)) : 0,
'changecount' => $node->nid ? count(xmlsitemap_node_get_timestamps($node)) - 1 : 0,
);
// The following values must always be checked because they are volatile.
$node->xmlsitemap['lastmod'] = isset($node->changed) ? $node->changed : REQUEST_TIME;
$node->xmlsitemap['access'] = $node->nid ? (bool) node_access('view', $node, drupal_anonymous_user()) : 1;
$node->xmlsitemap['language'] = isset($node->language) ? $node->language : LANGUAGE_NONE;
return $node->xmlsitemap;
}
/**
* Internal default variables for xmlsitemap_node_var().
*/
function xmlsitemap_node_variables() {
$defaults = array();
$node_types = array_keys(node_type_get_names());
foreach ($node_types as $node_type) {
$defaults['xmlsitemap_node_priority_' . $node_type] = 0.5;
$defaults['xmlsitemap_node_status_' . $node_type] = 0;
// @todo Remove the variable.
$defaults['xmlsitemap_node_update_' . $node_type] = NULL;
}
return $defaults;
}
///**
// * Internal implementation of variable_get().
// */
//function xmlsitemap_node_var($name, $default = NULL) {
// static $defaults;
// if (!isset($defaults)) {
// $defaults = xmlsitemap_node_variables();
// }
//
// $name = 'xmlsitemap_node_'. $name;
//
// // @todo Remove when stable.
// if (!isset($defaults[$name])) {
// trigger_error(t('Default variable for %variable not found.', array('%variable' => $name)));
// }
//
// return variable_get($name, isset($default) || !isset($defaults[$name]) ? $default : $defaults[$name]);
//}

View File

@@ -0,0 +1,186 @@
<?php
// $Id: xmlsitemap_node.test,v 1.7 2010/01/24 07:21:22 davereid Exp $
/**
* @file
* Unit tests for the xmlsitemap_node module.
*/
class XMLSitemapNodeTestHelper extends XMLSitemapTestHelper {
protected $admin_user;
protected $normal_user;
protected $nodes = array();
function setUp() {
parent::setUp('xmlsitemap', 'xmlsitemap_node', 'comment');
$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'));
variable_set('xmlsitemap_node_status_page', 1);
}
//function addNodes($count) {
// for ($i = count($this->nodes); $i < ($count + 1); $i++) {
// $this->nodes[$i] = $this->drupalCreateNode();
// }
//}
protected function assertNodeSitemapLinkVisible(stdClass $node) {
$link = xmlsitemap_load_link(array('type' => 'node', 'id' => $node->nid));
return $this->assertSitemapLinkVisible($link);
}
protected function assertNodeSitemapLinkNotVisible(stdClass $node) {
$link = xmlsitemap_load_link(array('type' => 'node', 'id' => $node->nid));
return $this->assertSitemapLinkNotVisible($link);
}
protected function assertNodeSitemapLinkValues(stdClass $node, array $values) {
$link = xmlsitemap_load_link(array('type' => 'node', 'id' => $node->nid));
if (!$link) {
$this->fail(t('Could not load sitemap link for node @nid.', array('@nid' => $node->nid)));
}
else {
$this->assertSitemapLinkValues($link, $values);
}
}
}
//class XMLSitemapNodeUnitTest extends DrupalWebTestCase {
// public static function getInfo() {
// return array(
// 'name' => 'XML Sitemap node unit tests',
// 'description' => 'Unit tests for the XML Sitemap node module.',
// 'group' => 'XML Sitemap',
// );
// }
//
// function setUp() {
// parent::setUp('xmlsitemap', 'xmlsitemap_node');
// }
//}
class XMLSitemapNodeFunctionalTest extends XMLSitemapNodeTestHelper {
public static function getInfo() {
return array(
'name' => 'XML sitemap node functional tests',
'description' => 'Interface tests for the XML sitemap node module.',
'group' => 'XML sitemap',
);
}
function testNodeSettings() {
$node = $this->drupalCreateNode(array('status' => FALSE, 'uid' => $this->normal_user->uid));
$this->assertNodeSitemapLinkValues($node, 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' => 'Test node body',
);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertText('Page Test node title has been updated.');
$this->assertNodeSitemapLinkValues($node, 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('Page Test node title has been updated.');
$this->assertNodeSitemapLinkValues($node, 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('Page Test node title has been updated.');
$this->assertNodeSitemapLinkValues($node, 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->assertNodeSitemapLinkValues($node_old, array('status' => 1, 'priority' => 0.5));
$edit = array(
'xmlsitemap_node_status' => 0,
'xmlsitemap_node_priority' => '0.0',
);
$this->drupalGet('admin/content/node-type/page');
$this->drupalPost('admin/content/node-type/page', $edit, t('Save content type'));
$this->assertText(t('The content type Page has been updated.'));
$node = $this->drupalCreateNode();
$this->assertNodeSitemapLinkValues($node, array('status' => 0, 'priority' => 0.0));
$this->assertNodeSitemapLinkValues($node_old, array('status' => 0, 'priority' => 0.0));
$edit = array(
'xmlsitemap_node_status' => 1,
'xmlsitemap_node_priority' => '0.5',
);
$this->drupalPost('admin/content/node-type/page', $edit, t('Save content type'));
$this->assertText(t('The content type Page has been updated.'));
$this->assertNodeSitemapLinkValues($node, array('status' => 1, 'priority' => 0.5));
$this->assertNodeSitemapLinkValues($node_old, array('status' => 1, 'priority' => 0.5));
//$this->drupalPost('node/' . $node->nid . '/edit', array(), t('Save'));
//$this->assertText(t('Page @title has been updated.', array('@title' => $node->title)));
}
/**
* Test the import of old nodes via cron.
*/
function testCron() {
$this->drupalLogin($this->admin_user);
$limit = 5;
$this->drupalPost('admin/config/search/xmlsitemap', array('xmlsitemap_batch_limit' => $limit), t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'));
$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->assertNodeSitemapLinkValues($node, 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));
}
}
}
}

View File

@@ -0,0 +1,16 @@
; $Id: xmlsitemap_taxonomy.info,v 1.3 2009/12/22 23:56:59 davereid Exp $
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
; Information added by drupal.org packaging script on 2010-01-24
version = "7.x-2.x-dev"
core = "7.x"
project = "xmlsitemap"
datestamp = "1264335489"

View File

@@ -0,0 +1,48 @@
<?php
// $Id: xmlsitemap_taxonomy.install,v 1.4 2010/01/18 06:30:31 davereid Exp $
/**
* @file
* Install and uninstall schema and functions for the xmlsitemap_taxonomy module.
*/
/**
* Implements hook_uninstall().
*/
function xmlsitemap_taxonomy_uninstall() {
// Remove variables.
drupal_load('module', 'xmlsitemap_taxonomy');
$variables = array_keys(xmlsitemap_taxonomy_variables());
foreach ($variables as $variable) {
variable_del($variable);
}
}
// @todo Remove these update functions before alpha.
function xmlsitemap_taxonomy_update_1() {
$vids = array_keys(taxonomy_get_vocabularies());
foreach ($vids as $vid) {
if (variable_get('xmlsitemap_taxonomy_priority_' . $vid, 'default') === 'default') {
variable_set('xmlsitemap_taxonomy_priority_' . $vid, 0.5);
}
}
}
/**
* Change 'taxonomy' type to 'taxonomy_term'.
*/
function xmlsitemap_taxonomy_update_2() {
$field = array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
);
db_change_field('xmlsitemap', 'type', 'type', $field);
db_update('xmlsitemap')
->fields(array('type' => 'taxonomy_term'))
->condition('type', 'taxonomy')
->execute();
}
// @todo Convert {xmlsitemap}.subtype from vid to vocabulary_machine_name.

View File

@@ -0,0 +1,366 @@
<?php
// $Id: xmlsitemap_taxonomy.module,v 1.9 2010/01/20 03:34:29 davereid Exp $
/**
* 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 ($vids = xmlsitemap_taxonomy_get_vids()) {
$tids = db_query_range("SELECT t.tid FROM {term_data} t LEFT JOIN {xmlsitemap} x ON x.type = 'taxonomy_term' AND t.tid = x.id WHERE x.id IS NULL AND t.vid IN (:vids) ORDER BY t.tid DESC", 0, $limit, array(':vids' => $vids));
foreach ($tids as $tid) {
$term = xmlsitemap_taxonomy_taxonomy_term_load($tid);
$link = xmlsitemap_taxonomy_create_link($term);
xmlsitemap_save_link($link);
}
}
}
/**
* Implements hook_xmlsitemap_links().
*/
function xmlsitemap_taxonomy_xmlsitemap_links($offset = 0, $limit = 0) {
$links = array();
if ($vids = xmlsitemap_taxonomy_get_vids()) {
$sql = "SELECT t.tid FROM {term_data} t WHERE t.tid > :tid AND t.vid IN (:vids) ORDER BY t.tid";
$args = array(':tid' => $offset, ':vids' => $vids);
$tids = ($limit ? db_query_range($sql, 0, $limit, $args) : db_query($sql, $args));
foreach ($tids as $tid) {
$term = xmlsitemap_taxonomy_taxonomy_term_load($tid);
$links[] = xmlsitemap_taxonomy_create_link($term);
}
}
return $links;
}
/**
* Implements hook_xmlsitemap_links_batch_info().
*/
function xmlsitemap_taxonomy_xmlsitemap_links_batch_info() {
$vids = xmlsitemap_taxonomy_get_vids();
return array(
'max' => $vids ? db_query("SELECT COUNT(t.tid) FROM {term_data} t WHERE t.vid IN (:vids)", array(':vids' => $vids))->fetchField() : 0,
);
}
/**
* Implements hook_xmlsitemap_link_info().
*/
function xmlsitemap_taxonomy_xmlsitemap_link_info() {
return array(
'taxononomy' => array(
'purge' => TRUE,
'table' => 'term_data',
'id' => 'tid',
'subtype' => 'vid',
'subtypes' => xmlsitemap_taxonomy_get_vids(),
),
);
}
/**
* Load a taxonomy term and its associated sitemap link data.
*
* Use this instead of taxonomy_get_term().
*
* @todo Convert this to hook_taxonomy_term_load() in Drupal 7.
*/
function xmlsitemap_taxonomy_taxonomy_term_load($tid) {
$term = taxonomy_get_term($tid);
if ($data = xmlsitemap_load_link(array('type' => 'taxonomy_term', 'id' => $tid))) {
$term->xmlsitemap = $data;
}
return $term;
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Show a summary of vocabularies on the XML sitemap settings page.
*/
function xmlsitemap_taxonomy_form_xmlsitemap_settings_form_alter(&$form, $form_state) {
$type = array(
'type' => 'taxonomy_term',
'title' => t('Taxonomy'),
'item_title' => t('Vocabulary'),
'access' => user_access('administer taxonomy'),
);
$vocabularies = taxonomy_get_vocabularies();
foreach ($vocabularies as $vid => $vocabulary) {
$vocabularies[$vid] = array(
'name' => $vocabulary->name,
'link' => 'admin/content/taxonomy/edit/vocabulary/' . $vid,
'status' => xmlsitemap_taxonomy_var('status_' . $vid),
'priority' => xmlsitemap_taxonomy_var('priority_' . $vid),
);
}
xmlsitemap_add_form_type_summary($form, $type, $vocabularies);
$form['taxonomy_term']['#weight'] = 40;
}
/**
* Implements hook_form_FORM_ID_alter().
*
* @see taxonomy_form_vocabulary()
* @see xmlsitemap_add_form_type_options()
*/
function xmlsitemap_taxonomy_form_taxonomy_form_vocabulary_alter(&$form, $form_state) {
$vid = isset($form['vid']['#value']) ? $form['vid']['#value'] : 0;
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
$options = array(
'status' => variable_get('xmlsitemap_taxonomy_status_' . $vid, 0),
'priority' => variable_get('xmlsitemap_taxonomy_priority_' . $vid, 0.5),
);
xmlsitemap_add_form_type_options($form, 'taxonomy', $options);
// @todo Enable these features:
//$form['xmlsitemap']['xmlsitemap_taxonomy_calculate_priority'] = array(
// '#type' => 'checkbox',
// '#title' => t('Calculate priority based on term depth and weight.'),
// '#default_value' => xmlsitemap_taxonomy_var('calculate_priority_' . $vid),
//);
//$form['xmlsitemap']['xmlsitemap_taxonomy_include_empty_terms'] = array(
// '#type' => 'checkbox',
// '#title' => t('Include terms that do not have any associated content.'),
// '#default_value' => xmlsitemap_taxonomy_var('include_empty_terms_' . $vid),
//);
// The submit and delete buttons need to be weighted down.
$form['submit'] += array('#weight' => 50);
if (isset($form['delete'])) {
$form['delete'] += array('#weight' => 51);
}
$form['#submit'][] = 'xmlsitemap_taxonomy_taxonomy_form_vocabulary_submit';
}
/**
* Form submit handler; update settings when a taxonomy vocabulary is saved.
*/
function xmlsitemap_taxonomy_taxonomy_form_vocabulary_submit($form, $form_state) {
$vid = $form_state['values']['vid'];
$new_status = $form_state['values']['xmlsitemap_taxonomy_status'];
$new_priority = $form_state['values']['xmlsitemap_taxonomy_priority'];
if ($new_status != variable_get('xmlsitemap_taxonomy_status_' . $vid, 0)) {
xmlsitemap_update_links(array('status' => $new_status), array('type' => 'taxonomy_term', 'subtype' => $vid, 'status_override' => 0));
}
if ($new_priority != variable_get('xmlsitemap_taxonomy_priority_' . $vid, 0.5)) {
xmlsitemap_update_links(array('priority' => $new_priority), array('type' => 'taxonomy_term', 'subtype' => $vid, 'priority_override' => 0));
}
variable_set('xmlsitemap_taxonomy_status_' . $vid, $new_status);
variable_set('xmlsitemap_taxonomy_priority_' . $vid, $new_priority);
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function xmlsitemap_taxonomy_form_taxonomy_form_term_alter(&$form, $form_state) {
// Because the same form is used for deletion in confirm_form, we must check
// if the normal editing form elements are present. Hopefully this is fixed
// in Drupal 7.
if (isset($form['identification'])) {
if ($form['#term']['tid']) {
$term = xmlsitemap_taxonomy_taxonomy_term_load($form['#term']['tid']);
}
else {
$term = (object) $form['#term'];
}
$term->vid = $form['vid']['#value'];
$link = xmlsitemap_taxonomy_create_link($term);
// Add the link options.
module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
xmlsitemap_add_form_link_options($form, $link);
$form['xmlsitemap']['#access'] |= user_access('administer taxonomy');
if (user_access('administer taxonomy')) {
$form['xmlsitemap']['#description'] = t('The default priority for this vocabulary can be changed <a href="@link-type">here</a>.', array('@link-type' => url('admin/content/taxonomy/edit/vocabulary/' . $term->vid, array('query' => drupal_get_destination()))));
}
// The submit and delete buttons need to be weighted down.
$form['submit'] += array('#weight' => 50);
if (isset($form['delete'])) {
$form['delete'] += array('#weight' => 51);
}
}
}
/**
* Implements hook_taxonomy().
*/
function xmlsitemap_taxonomy_taxonomy($op, $type, $array = NULL) {
if ($type == 'vocabulary') {
$vid = $array['vid'];
// Insert and update actions handled by xmlsitemap_taxonomy_taxonomy_form_vocabulary_submit().
if ($op == 'delete') {
xmlsitemap_delete_link(array('type' => 'taxonomy_term', 'subtype' => $vid));
variable_del('xmlsitemap_taxonomy_status_' . $vid);
variable_del('xmlsitemap_taxonomy_priority_' . $vid);
}
}
if ($type == 'term') {
$tid = $array['tid'];
if ($op == 'insert' || $op == 'update') {
$link = xmlsitemap_taxonomy_create_link((object) $array);
xmlsitemap_save_link($link);
}
elseif ($op == 'delete') {
xmlsitemap_delete_link(array('type' => 'taxonomy_term', 'id' => $tid));
}
}
}
/**
* 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();
}
$term->xmlsitemap += array(
'id' => $term->tid,
'type' => 'taxonomy_term',
'subtype' => $term->vid,
'loc' => taxonomy_term_path($term),
'status' => variable_get('xmlsitemap_taxonomy_status_' . $term->vid, 0),
'status_default' => variable_get('xmlsitemap_taxonomy_status_' . $term->vid, 0),
'status_override' => 0,
'priority' => variable_get('xmlsitemap_taxonomy_priority_' . $term->vid, 0.5),
'priority_default' => variable_get('xmlsitemap_taxonomy_priority_' . $term->vid, 0.5),
'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['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($term) {
// Calculate priority.
// Min weight = -128
// Max weight = 127
// Max depth = ?
return NULL;
}
/**
* Find the tree depth of a taxonomy term.
*
* @param $tid
* A term ID.
* @return
* The tree depth of the term.
*/
function xmlsitemap_taxonomy_get_term_depth($tid) {
static $depths = array();
if (!isset($depths[$tid])) {
if ($parent = db_query("SELECT parent FROM {term_hierarchy} WHERE tid = %d", $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[$tid] = $depths[$parent] + 1;
}
else {
// Term has no parents, so depth is 0.
$depths[$tid] = 0;
}
}
return $depths[$tid];
}
function xmlsitemap_taxonomy_get_node_count($term) {
// @todo Use db_rewrite_sql() w/ switch user.
return db_query_range("SELECT COUNT(tn.nid) FROM {term_node} tn LEFT JOIN {node n} USING (nid) WHERE tn.tid = :tid AND n.status = 1", 0, 1, array(':tid' => $term->tid))->fetchField();
}
/**
* Fetch an array of vocabulary IDs to be included in the sitemap.
*/
function xmlsitemap_taxonomy_get_vids() {
$vids = array_keys(taxonomy_get_vocabularies());
foreach ($vids as $index => $vid) {
if (!variable_get('xmlsitemap_taxonomy_status_' . $vid, 0)) {
unset($vids[$index]);
}
}
return $vids;
}
/**
* Internal default variables for template_var().
*/
function xmlsitemap_taxonomy_variables() {
$defaults = array(
// Removed variables set to NULL.
'xmlsitemap_taxonomy_include_empty_terms' => NULL,
'xmlsitemap_taxonomy_calculate_priority' => NULL,
);
$vids = array_keys(taxonomy_get_vocabularies());
foreach ($vids as $vid) {
$defaults['xmlsitemap_taxonomy_status_' . $vid] = 0;
$defaults['xmlsitemap_taxonomy_priority_' . $vid] = '0.5';
$defaults['xmlsitemap_taxonomy_calculate_priority_' . $vid] = FALSE;
$defaults['xmlsitemap_taxonomy_include_empty_terms_' . $vid] = FALSE;
//$defaults += _xmlsitemap_taxonomy_variables_vid($vid);
}
return $defaults;
}
//function _xmlsitemap_taxonomy_variables_vid($vid) {
// $defaults = array();
// $defaults['xmlsitemap_taxonomy_status_' . $vid] = 0;
// $defaults['xmlsitemap_taxonomy_priority_' . $vid] = 0.5;
// $defaults['xmlsitemap_taxonomy_calculate_priority_' . $vid] = FALSE;
// $defaults['xmlsitemap_taxonomy_include_empty_terms_' . $vid] = TRUE;
// return $defaults;
//}
/**
* Internal implementation of variable_get().
*/
function xmlsitemap_taxonomy_var($name, $default = NULL) {
static $defaults = NULL;
if (!isset($defaults)) {
$defaults = xmlsitemap_taxonomy_variables();
}
$name = 'xmlsitemap_taxonomy_' . $name;
// @todo Remove when stable.
if (!isset($defaults[$name])) {
trigger_error(t('Default variable for %variable not found.', array('%variable' => $name)));
}
return variable_get($name, isset($default) || !isset($defaults[$name]) ? $default : $defaults[$name]);
}

View 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);

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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>
<td>Sitemap URL</td>
<td>Last modification date</td>
</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>

View File

@@ -0,0 +1,46 @@
/* $Id: xmlsitemap.xsl.css,v 1.4 2009/12/29 16:56:53 davereid Exp $ */
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;
}

View File

@@ -0,0 +1,45 @@
// $Id: xmlsitemap.xsl.js,v 1.4 2009/12/23 07:34:46 davereid Exp $
(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);