| 1 |
<?php
|
| 2 |
// $Id: xmlsitemap.module,v 1.21 2008/11/17 01:27:10 kiam Exp $
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file Creates a site map compatible with the sitemaps.org schema.
|
| 6 |
*/
|
| 7 |
|
| 8 |
/**
|
| 9 |
* Implementation of hook_help().
|
| 10 |
*/
|
| 11 |
function xmlsitemap_help($path, $arg) {
|
| 12 |
switch ($path) {
|
| 13 |
case 'admin/settings/xmlsitemap':
|
| 14 |
case 'admin/settings/xmlsitemap/settings':
|
| 15 |
return t('Configure the site map. Your site map is at !url.', array('!url' => '<a href="'. xmlsitemap_url('sitemap.xml', drupal_lookup_path('alias', 'sitemap.xml') ? drupal_lookup_path('alias', 'sitemap.xml') : NULL, NULL, NULL, TRUE) .'">'. xmlsitemap_url('sitemap.xml', drupal_lookup_path('alias', 'sitemap.xml') ? drupal_lookup_path('alias', 'sitemap.xml') : NULL, NULL, NULL, TRUE) .'</a>'));
|
| 16 |
case 'admin/settings/xmlsitemap/engines':
|
| 17 |
return t('Configure behavior for search engines.');
|
| 18 |
case 'admin/settings/xmlsitemap/additional':
|
| 19 |
return t('Set up additional links for your site map.');
|
| 20 |
}
|
| 21 |
}
|
| 22 |
|
| 23 |
/**
|
| 24 |
* Implementation of hook_menu().
|
| 25 |
*/
|
| 26 |
function xmlsitemap_menu() {
|
| 27 |
$items = array();
|
| 28 |
$access_config = array('administer site configuration');
|
| 29 |
$access_content = array('access content');
|
| 30 |
$items['admin/settings/xmlsitemap'] = array(
|
| 31 |
'title' => 'XML Sitemap',
|
| 32 |
'description' => 'Configure site map.',
|
| 33 |
'page callback' => 'drupal_get_form',
|
| 34 |
'page arguments' => array('xmlsitemap_settings_sitemap'),
|
| 35 |
'access arguments' => $access_config,
|
| 36 |
);
|
| 37 |
$items['admin/settings/xmlsitemap/settings'] = array(
|
| 38 |
'title' => 'Site map',
|
| 39 |
'description' => 'Configure site map.',
|
| 40 |
'type' => MENU_DEFAULT_LOCAL_TASK,
|
| 41 |
'weight' => -1,
|
| 42 |
);
|
| 43 |
$items['admin/settings/xmlsitemap/engines'] = array(
|
| 44 |
'title' => 'Search engines',
|
| 45 |
'description' => 'Configure search engines.',
|
| 46 |
'page callback' => 'drupal_get_form',
|
| 47 |
'page arguments' => array('xmlsitemap_settings_engines'),
|
| 48 |
'type' => MENU_LOCAL_TASK,
|
| 49 |
'access arguments' => $access_config,
|
| 50 |
);
|
| 51 |
$items['admin/settings/xmlsitemap/additional'] = array(
|
| 52 |
'title' => 'Additional',
|
| 53 |
'description' => 'Configure additional links.',
|
| 54 |
'page callback' => 'drupal_get_form',
|
| 55 |
'page arguments' => array('xmlsitemap_settings_additional'),
|
| 56 |
'type' => MENU_LOCAL_TASK,
|
| 57 |
'weight' => 1,
|
| 58 |
'access arguments' => $access_config,
|
| 59 |
|
| 60 |
);
|
| 61 |
$items['sitemap.xml'] = array(
|
| 62 |
'title' => 'Site map index',
|
| 63 |
'page callback' => '_xmlsitemap_output',
|
| 64 |
'type' => MENU_CALLBACK,
|
| 65 |
'access arguments' => $access_content
|
| 66 |
);
|
| 67 |
$chunk_count = variable_get('xmlsitemap_chunk_count', 0);
|
| 68 |
for ($chunk = 0; $chunk < $chunk_count; ++$chunk) {
|
| 69 |
$items["sitemap$chunk.xml"] = array(
|
| 70 |
'title' => 'Site map !number',
|
| 71 |
'title arguments' => array('!number' => $chunk),
|
| 72 |
'page callback' => '_xmlsitemap_output',
|
| 73 |
'page arguments' => array((string)$chunk),
|
| 74 |
'type' => MENU_CALLBACK,
|
| 75 |
'access arguments' => $access_content
|
| 76 |
);
|
| 77 |
}
|
| 78 |
|
| 79 |
return $items;
|
| 80 |
}
|
| 81 |
|
| 82 |
/**
|
| 83 |
* Menu callback; return site map settings form.
|
| 84 |
*/
|
| 85 |
function xmlsitemap_settings_sitemap() {
|
| 86 |
$form['xmlsitemap_chunk_size'] = array(
|
| 87 |
'#type' => 'textfield',
|
| 88 |
'#title' => t('Chunk size'),
|
| 89 |
'#default_value' => variable_get('xmlsitemap_chunk_size', 50000),
|
| 90 |
'#size' => 10,
|
| 91 |
'#maxlength' => 5,
|
| 92 |
'#description' => t('This is the number of links to include in one site map. Values can range between 1 and 50,000. If the total number of links exceeds the chunk size, multiple site maps will be generated.'),
|
| 93 |
'#weight' => -1,
|
| 94 |
);
|
| 95 |
$form['xmlsitemap_front_page_priority'] = array(
|
| 96 |
'#type' => 'select',
|
| 97 |
'#title' => t('Front page priority'),
|
| 98 |
'#default_value' => variable_get('xmlsitemap_front_page_priority', 1),
|
| 99 |
'#options' => xmlsitemap_priority_options(),
|
| 100 |
'#description' => t('This is the absolute priority for the front page.'),
|
| 101 |
'#weight' => -1,
|
| 102 |
);
|
| 103 |
return system_settings_form($form);
|
| 104 |
}
|
| 105 |
|
| 106 |
/**
|
| 107 |
* Validate site map settings form.
|
| 108 |
*/
|
| 109 |
function xmlsitemap_settings_sitemap_validate($form, &$form_state) {
|
| 110 |
if ($form_state['values']['xmlsitemap_chunk_size'] > 50000) {
|
| 111 |
form_set_error('xmlsitemap_chunk_size', t('Cannot send more than 50,000 links at one time.'));
|
| 112 |
}
|
| 113 |
}
|
| 114 |
|
| 115 |
/**
|
| 116 |
* Submit site map settings form.
|
| 117 |
*/
|
| 118 |
function xmlsitemap_settings_sitemap_submit($form, &$form_state) {
|
| 119 |
system_settings_form_submit($form, $form_state);
|
| 120 |
xmlsitemap_update_sitemap();
|
| 121 |
}
|
| 122 |
|
| 123 |
/**
|
| 124 |
* Menu callback; return search engine settings form.
|
| 125 |
*/
|
| 126 |
function xmlsitemap_settings_engines() {
|
| 127 |
$form['submission'] = array(
|
| 128 |
'#type' => 'fieldset',
|
| 129 |
'#title' => t('Submission settings'),
|
| 130 |
);
|
| 131 |
$form['submission']['xmlsitemap_submit'] = array(
|
| 132 |
'#type' => 'checkbox',
|
| 133 |
'#title' => t('Submit site map when updated.'),
|
| 134 |
'#default_value' => variable_get('xmlsitemap_submit', FALSE),
|
| 135 |
'#description' => t('If enabled, search engines will be notified of changes to the site map each time it is updated.'),
|
| 136 |
);
|
| 137 |
$form['submission']['xmlsitemap_cron_submit'] = array(
|
| 138 |
'#type' => 'checkbox',
|
| 139 |
'#title' => t('Submit site map on cron run.'),
|
| 140 |
'#default_value' => variable_get('xmlsitemap_cron_submit', FALSE),
|
| 141 |
'#description' => t('If enabled, search engines will be notified of changes to the site map each time cron is run.'),
|
| 142 |
);
|
| 143 |
$form['submission']['xmlsitemap_log_access'] = array(
|
| 144 |
'#type' => 'checkbox',
|
| 145 |
'#title' => t('Log access.'),
|
| 146 |
'#default_value' => variable_get('xmlsitemap_log_access', FALSE),
|
| 147 |
'#description' => t('If enabled, a watchdog entry will be made each time the site map is accessed, containing information about the requestor.'),
|
| 148 |
);
|
| 149 |
$form = array_merge($form, module_invoke_all('xmlsitemap_engines', 'form'));
|
| 150 |
menu_rebuild();
|
| 151 |
return system_settings_form($form);
|
| 152 |
}
|
| 153 |
|
| 154 |
/**
|
| 155 |
* Submit search engine settings form.
|
| 156 |
*/
|
| 157 |
function xmlsitemap_settings_engines_submit($form, &$form_state) {
|
| 158 |
if ($form_state['values']['xmlsitemap_root']) {
|
| 159 |
$form_state['values']['xmlsitemap_submit'] = FALSE;
|
| 160 |
$form_state['values']['xmlsitemap_log_access'] = FALSE;
|
| 161 |
}
|
| 162 |
system_settings_form_submit($form, $form_state);
|
| 163 |
}
|
| 164 |
|
| 165 |
/**
|
| 166 |
* Menu callback; return additional links form.
|
| 167 |
*/
|
| 168 |
function xmlsitemap_settings_additional() {
|
| 169 |
$form['xmlsitemap_additional_links_priority'] = array(
|
| 170 |
'#type' => 'select',
|
| 171 |
'#title' => t('Link priority'),
|
| 172 |
'#default_value' => variable_get('xmlsitemap_additional_links_priority', 0.5),
|
| 173 |
'#options' => xmlsitemap_priority_options(),
|
| 174 |
'#description' => t('This is the default priority for additional links.'),
|
| 175 |
);
|
| 176 |
$form['delete']['#tree'] = TRUE;
|
| 177 |
$form['path']['#tree'] = TRUE;
|
| 178 |
$form['link']['#tree'] = TRUE;
|
| 179 |
$form['link']['new'] = array(
|
| 180 |
'#type' => 'textfield',
|
| 181 |
'#size' => 40,
|
| 182 |
'#description' => t('Enter a Drupal path to add to the site map.'),
|
| 183 |
'#attributes' => array('tabindex' => 1),
|
| 184 |
);
|
| 185 |
$form['old_priority']['#tree'] = TRUE;
|
| 186 |
$form['priority']['#tree'] = TRUE;
|
| 187 |
$form['priority']['new'] = array(
|
| 188 |
'#type' => 'select',
|
| 189 |
'#default_value' => 'NULL',
|
| 190 |
'#options' => xmlsitemap_priority_options('default'),
|
| 191 |
'#attributes' => array('tabindex' => 2),
|
| 192 |
);
|
| 193 |
$result = pager_query("SELECT path, priority FROM {xmlsitemap_additional} ORDER BY last_changed DESC", 50);
|
| 194 |
while ($link = db_fetch_object($result)) {
|
| 195 |
$id = $link->path == 'new' ? '_new' : str_replace(array('_', '%'), array('__', '_'), urlencode($link->path));
|
| 196 |
$form['delete'][$id] = array('#type' => 'checkbox', '#default_value' => FALSE);
|
| 197 |
$form['path'][$id] = array('#type' => 'value', '#value' => $link->path);
|
| 198 |
$form['link'][$id] = array('#value' => '<a href="'. xmlsitemap_url($link->path, drupal_lookup_path('alias', $link->path) ? drupal_lookup_path('alias', $link->path) : NULL) .'">'. $link->path .'</a>');
|
| 199 |
$priority = isset($link->priority) ? $link->priority : 'NULL';
|
| 200 |
$form['old_priority'][$id] = array('#type' => 'value', '#value' => $priority);
|
| 201 |
$form['priority'][$id] = array('#type' => 'select', '#default_value' => $priority, '#options' => xmlsitemap_priority_options('default'));
|
| 202 |
}
|
| 203 |
$form['pager'] = array('#value' => theme('pager', NULL, 50));
|
| 204 |
return system_settings_form($form);
|
| 205 |
}
|
| 206 |
|
| 207 |
/**
|
| 208 |
* Implementation of hook_theme().
|
| 209 |
*/
|
| 210 |
function xmlsitemap_theme() {
|
| 211 |
return array(
|
| 212 |
'xmlsitemap_settings_additional' => array('form'),
|
| 213 |
);
|
| 214 |
}
|
| 215 |
|
| 216 |
/**
|
| 217 |
* Theme additional links form.
|
| 218 |
* @ingroup themeable
|
| 219 |
*/
|
| 220 |
function theme_xmlsitemap_settings_additional($form) {
|
| 221 |
$output = '';
|
| 222 |
$output .= drupal_render($form['xmlsitemap_additional_links_priority']);
|
| 223 |
$header = array(t('Delete'), t('Path'), t('Priority'));
|
| 224 |
foreach (element_children($form['link']) as $id) {
|
| 225 |
$row = array();
|
| 226 |
$row[] = isset($form['delete'][$id]) ? drupal_render($form['delete'][$id]) : array_merge(array('header' => TRUE, 'valign' => 'bottom'), theme('table_select_header_cell'));
|
| 227 |
$row[] = drupal_render($form['link'][$id]);
|
| 228 |
$row[] = drupal_render($form['priority'][$id]);
|
| 229 |
$rows[$id] = array('data' => $row, 'valign' => 'top');
|
| 230 |
}
|
| 231 |
if (!empty($rows)) {
|
| 232 |
$output .= theme('table', $header, $rows);
|
| 233 |
}
|
| 234 |
$output .= drupal_render($form);
|
| 235 |
drupal_add_js('document.getElementById("edit-link-new").focus()', 'inline', 'footer');
|
| 236 |
return $output;
|
| 237 |
}
|
| 238 |
|
| 239 |
|
| 240 |
/**
|
| 241 |
* Submit additional links form.
|
| 242 |
*/
|
| 243 |
function xmlsitemap_settings_additional_submit($form, &$form_state) {
|
| 244 |
$update = FALSE;
|
| 245 |
if ($form_state['values']['op'] == t('Save configuration')) {
|
| 246 |
if ($form_state['values']['xmlsitemap_additional_links_priority'] != variable_get('xmlsitemap_additional_links_priority', 0.1)) {
|
| 247 |
$update = TRUE;
|
| 248 |
}
|
| 249 |
if (!empty($form_state['values']['delete'])) {
|
| 250 |
foreach ($form_state['values']['delete'] as $id => $delete) {
|
| 251 |
if ($delete || $form_state['values']['path'][$id] == trim($form_state['values']['link']['new'])) {
|
| 252 |
db_query("DELETE FROM {xmlsitemap_additional} WHERE path = '%s'", $form_state['values']['path'][$id]);
|
| 253 |
unset($form_state['values']['priority'][$id]);
|
| 254 |
$update = TRUE;
|
| 255 |
}
|
| 256 |
}
|
| 257 |
unset($form_state['values']['delete']);
|
| 258 |
}
|
| 259 |
$path = trim($form_state['values']['link']['new']);
|
| 260 |
$pid = db_result(db_query("SELECT pid FROM {url_alias} WHERE src = '%s'", $path));
|
| 261 |
if (!empty($path)) {
|
| 262 |
db_query("
|
| 263 |
INSERT INTO {xmlsitemap_additional} (path, pid, last_changed, priority) VALUES ('%s', %s, %d, %s)
|
| 264 |
", $path, empty($pid) ? 'NULL' : $pid, time(), $form_state['values']['priority']['new']);
|
| 265 |
unset($form_state['values']['link'], $form_state['values']['priority']['new']);
|
| 266 |
$update = TRUE;
|
| 267 |
}
|
| 268 |
if (!empty($form_state['values']['priority'])) {
|
| 269 |
foreach ($form_state['values']['priority'] as $id => $priority) {
|
| 270 |
if ($priority != $form_state['values']['old_priority'][$id]) {
|
| 271 |
$pid = db_result(db_query("SELECT pid FROM {url_alias} WHERE src = '%s'", $form_state['values']['path'][$id]));
|
| 272 |
db_query("
|
| 273 |
UPDATE {xmlsitemap_additional}
|
| 274 |
SET pid = %s, previously_changed = last_changed, last_changed = %d, priority = %s
|
| 275 |
WHERE path = '%s'
|
| 276 |
", empty($pid) ? 'NULL' : $pid, time(), $priority, $form_state['values']['path'][$id]);
|
| 277 |
$update = TRUE;
|
| 278 |
}
|
| 279 |
}
|
| 280 |
unset($form_state['values']['path'], $form_state['values']['priority'], $form_state['values']['old_priority']);
|
| 281 |
}
|
| 282 |
}
|
| 283 |
elseif (variable_get('xmlsitemap_additional_links_priority', 0.1) != 0.1) {
|
| 284 |
if (in_array('NULL', $form_state['values']['old_priority'])) {
|
| 285 |
$update = TRUE;
|
| 286 |
}
|
| 287 |
unset($form_state['values']['delete'], $form_state['values']['path'], $form_state['values']['link'], $form_state['values']['old_priority'], $form_state['values']['priority']);
|
| 288 |
}
|
| 289 |
system_settings_form_submit($form, $form_state);
|
| 290 |
if ($update) {
|
| 291 |
xmlsitemap_update_sitemap();
|
| 292 |
}
|
| 293 |
}
|
| 294 |
|
| 295 |
/**
|
| 296 |
* Get an array of site map priority options.
|
| 297 |
* @param $option:
|
| 298 |
* If not given, the array will include priority values from 0.0 to 1.0.
|
| 299 |
* - exclude: Add option to exclude item from site map.
|
| 300 |
* - default: Add option to use default priority. Only for cases where a
|
| 301 |
* default priority exists.
|
| 302 |
* - both: Add both the default and exclude options.
|
| 303 |
* @return An array of priority options.
|
| 304 |
*/
|
| 305 |
function xmlsitemap_priority_options($option = NULL) {
|
| 306 |
if ($option == 'default' || $option == 'both') {
|
| 307 |
$options['NULL'] = t('Default');
|
| 308 |
}
|
| 309 |
$options['1'] = '1.0';
|
| 310 |
$values = array('0.9', '0.8', '0.7', '0.6', '0.5', '0.4', '0.3', '0.2', '0.1');
|
| 311 |
foreach ($values as $value) {
|
| 312 |
$options[$value] = $value;
|
| 313 |
}
|
| 314 |
$options['0'] = '0.0';
|
| 315 |
if ($option == 'exclude' || $option == 'both') {
|
| 316 |
$options['-1'] = t('Not in site map');
|
| 317 |
}
|
| 318 |
return $options;
|
| 319 |
}
|
| 320 |
|
| 321 |
/**
|
| 322 |
* Implementation of hook_robotstxt().
|
| 323 |
*/
|
| 324 |
function xmlsitemap_robotstxt() {
|
| 325 |
return array("Sitemap: ". xmlsitemap_url('sitemap.xml', drupal_lookup_path('alias', 'sitemap.xml') ? drupal_lookup_path('alias', 'sitemap.xml') : NULL, NULL, NULL, TRUE));
|
| 326 |
}
|
| 327 |
|
| 328 |
/**
|
| 329 |
* Menu callback; display the site map.
|
| 330 |
* @param $chunk:
|
| 331 |
* An integer specifying which chunk of the site map is being requested. If
|
| 332 |
* not set and there is more than one chunk, display the site map index.
|
| 333 |
* @return None
|
| 334 |
*/
|
| 335 |
function _xmlsitemap_output($chunk = NULL) {
|
| 336 |
drupal_set_header('Content-type: text/xml; charset=utf-8');
|
| 337 |
global $user;
|
| 338 |
$dest = file_directory_path() .'/xmlsitemap';
|
| 339 |
file_check_directory($dest, FILE_CREATE_DIRECTORY);
|
| 340 |
if (isset($chunk)) {
|
| 341 |
$dest .= "/sitemap$chunk.xml.gz";
|
| 342 |
$type = t('Site map @chunk', array('@chunk' => $chunk));
|
| 343 |
}
|
| 344 |
else {
|
| 345 |
$dest .= '/sitemap.xml.gz';
|
| 346 |
$link_count = _xmlsitemap_link_count();
|
| 347 |
$chunk_size = variable_get('xmlsitemap_chunk_size', 50000);
|
| 348 |
$type = $link_count > $chunk_size ? t('Site map index') : t('Site map');
|
| 349 |
}
|
| 350 |
$status = TRUE;
|
| 351 |
if (!file_exists($dest) || variable_get('xmlsitemap_update', FALSE)) {
|
| 352 |
$page = isset($chunk) ? $chunk : 'index';
|
| 353 |
$status = _xmlsitemap_update_cache($page);
|
| 354 |
}
|
| 355 |
if ($status) {
|
| 356 |
if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === FALSE || zlib_get_coding_type() !== FALSE || extension_loaded('eAccelerator') || (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED && empty($user->uid))) {
|
| 357 |
readgzfile($dest);
|
| 358 |
}
|
| 359 |
else {
|
| 360 |
drupal_set_header('Content-Encoding: gzip');
|
| 361 |
print file_get_contents($dest);
|
| 362 |
}
|
| 363 |
if (variable_get('xmlsitemap_log_access', FALSE)) {
|
| 364 |
$message = array_shift(module_invoke_all('xmlsitemap_engines', 'access', $type));
|
| 365 |
if (isset($message)) {
|
| 366 |
watchdog('xmlsitemap', $message);
|
| 367 |
}
|
| 368 |
else {
|
| 369 |
watchdog('xmlsitemap', '!sitemap downloaded by @user-agent at @address.', array(
|
| 370 |
'!sitemap' => $type,
|
| 371 |
'@user-agent' => $_SERVER['HTTP_USER_AGENT'],
|
| 372 |
'@address' => ip_address(),
|
| 373 |
));
|
| 374 |
}
|
| 375 |
}
|
| 376 |
}
|
| 377 |
else {
|
| 378 |
drupal_not_found();
|
| 379 |
}
|
| 380 |
}
|
| 381 |
|
| 382 |
/**
|
| 383 |
* Count the total number of links in the site.
|
| 384 |
* @return An integer containing the total number of links
|
| 385 |
*/
|
| 386 |
function _xmlsitemap_link_count() {
|
| 387 |
static $count;
|
| 388 |
$count = isset($count) ? $count : count(_xmlsitemap_links());
|
| 389 |
return $count;
|
| 390 |
}
|
| 391 |
|
| 392 |
/**
|
| 393 |
* Update the cached site map files.
|
| 394 |
* @return TRUE if the update was successful, FALSE otherwise.
|
| 395 |
*/
|
| 396 |
function _xmlsitemap_update_cache($page = NULL) {
|
| 397 |
global $user;
|
| 398 |
$current_user = $user;
|
| 399 |
$user = user_load(array('uid' => 0));
|
| 400 |
$path = file_directory_path() .'/xmlsitemap';
|
| 401 |
file_check_directory($path, FILE_CREATE_DIRECTORY);
|
| 402 |
$node_count = db_result(db_query("SELECT COUNT(*) FROM {node}"));
|
| 403 |
$dest = $path .'/sitemap.xml.gz';
|
| 404 |
$link_count = _xmlsitemap_link_count();
|
| 405 |
$chunk_size = variable_get('xmlsitemap_chunk_size', 50000);
|
| 406 |
$status = TRUE;
|
| 407 |
if ($link_count > $chunk_size) {
|
| 408 |
$data = gzencode(_xmlsitemap_output_index($link_count));
|
| 409 |
if (file_save_data($data, $dest, FILE_EXISTS_REPLACE) === 0 && ($page == 'index' || !isset($page))) {
|
| 410 |
$status = FALSE;
|
| 411 |
}
|
| 412 |
for ($chunk = 0; $chunk < $link_count / $chunk_size; ++$chunk) {
|
| 413 |
$dest = $path ."/sitemap$chunk.xml.gz";
|
| 414 |
$data = gzencode(_xmlsitemap_output_chunk($chunk));
|
| 415 |
if (file_save_data($data, $dest, FILE_EXISTS_REPLACE) === 0 && ($page == $chunk || !isset($page))) {
|
| 416 |
$status = FALSE;
|
| 417 |
}
|
| 418 |
}
|
| 419 |
}
|
| 420 |
else {
|
| 421 |
$chunk = 0;
|
| 422 |
$data = gzencode(_xmlsitemap_output_chunk($chunk));
|
| 423 |
if (file_save_data($data, $dest, FILE_EXISTS_REPLACE) === 0 && ($page == 'index' || !isset($page))) {
|
| 424 |
$status = FALSE;
|
| 425 |
}
|
| 426 |
}
|
| 427 |
variable_set('xmlsitemap_chunk_count', $chunk);
|
| 428 |
variable_set('xmlsitemap_update', FALSE);
|
| 429 |
if (!$status) {
|
| 430 |
drupal_set_message(t('Unable to load site map. Make sure that there is an xmlsitemap directory in your files directory and that it is writable by Drupal.'), 'error');
|
| 431 |
}
|
| 432 |
$user = $current_user;
|
| 433 |
return $status;
|
| 434 |
}
|
| 435 |
|
| 436 |
/**
|
| 437 |
* Generate the site map index.
|
| 438 |
* @param $link_count:
|
| 439 |
* An integer containing the total number of links in the site
|
| 440 |
* @return A string containing the site map index
|
| 441 |
*/
|
| 442 |
function _xmlsitemap_output_index($link_count) {
|
| 443 |
$output = '';
|
| 444 |
global $base_path;
|
| 445 |
$xsl_path = file_directory_path() .'/xmlsitemap/gss.xsl';
|
| 446 |
$xsl_path = file_exists($xsl_path) ? _xmlsitemap_file_create_url($xsl_path) : $base_path . drupal_get_path('module', 'xmlsitemap') .'/gss/gss.xsl';
|
| 447 |
$output .= '<?xml version="1.0" encoding="UTF-8"?>'."\n";
|
| 448 |
$output .= '<?xml-stylesheet type="text/xsl" href="'. $xsl_path .'" ?>'."\n";
|
| 449 |
$output .= '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"'."\n";
|
| 450 |
$output .= ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'."\n";
|
| 451 |
$output .= ' xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9'."\n";
|
| 452 |
$output .= ' http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd">'."\n";
|
| 453 |
$chunk_size = variable_get('xmlsitemap_chunk_size', 50000);
|
| 454 |
for ($chunk = 0; $chunk < $link_count / $chunk_size; ++$chunk) {
|
| 455 |
$output .= '<sitemap><loc>'. xmlsitemap_url("sitemap$chunk.xml", NULL, NULL, NULL, TRUE) .'</loc>';
|
| 456 |
$previous = $chunk * $chunk_size;
|
| 457 |
$links = array_slice(_xmlsitemap_links(), $previous, $chunk_size);
|
| 458 |
$output .= '<lastmod>'. gmdate('Y-m-d\TH:i:s+00:00', array_reduce($links, '_xmlsitemap_chunk_last_change')) .'</lastmod>';
|
| 459 |
$output .= "</sitemap>\n";
|
| 460 |
}
|
| 461 |
$output .= '</sitemapindex>';
|
| 462 |
return $output;
|
| 463 |
}
|
| 464 |
|
| 465 |
/**
|
| 466 |
* Compare link modification time to modification time of previous link.
|
| 467 |
* @param $time:
|
| 468 |
* Modification time of previous link
|
| 469 |
* @param $value:
|
| 470 |
* Link array
|
| 471 |
* @return Most recent time
|
| 472 |
*/
|
| 473 |
function _xmlsitemap_chunk_last_change($time, $value) {
|
| 474 |
if ($time < $value['#lastmod']) {
|
| 475 |
$time = $value['#lastmod'];
|
| 476 |
}
|
| 477 |
return $time;
|
| 478 |
}
|
| 479 |
|
| 480 |
/**
|
| 481 |
* Generate a chunk of the site map.
|
| 482 |
* @param $chunk:
|
| 483 |
* An integer specifying which chunk of the site map to display
|
| 484 |
* @return A string containing a chunk of the site map
|
| 485 |
*/
|
| 486 |
function _xmlsitemap_output_chunk($chunk) {
|
| 487 |
$output = '';
|
| 488 |
global $base_path;
|
| 489 |
if (!ini_get('safe_mode')) {
|
| 490 |
set_time_limit(240);
|
| 491 |
}
|
| 492 |
$xsl_path = file_directory_path() .'/xmlsitemap/gss.xsl';
|
| 493 |
$xsl_path = file_exists($xsl_path) ? _xmlsitemap_file_create_url($xsl_path) : $base_path . drupal_get_path('module', 'xmlsitemap') .'/gss/gss.xsl';
|
| 494 |
$output .= '<?xml version="1.0" encoding="UTF-8"?>'."\n";
|
| 495 |
$output .= '<?xml-stylesheet type="text/xsl" href="'. $xsl_path .'" ?>'."\n";
|
| 496 |
$output .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"'."\n";
|
| 497 |
$output .= ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'."\n";
|
| 498 |
$output .= ' xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9'."\n";
|
| 499 |
$output .= ' http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">'."\n";
|
| 500 |
$chunk_size = variable_get('xmlsitemap_chunk_size', 50000);
|
| 501 |
$previous = $chunk * $chunk_size;
|
| 502 |
$output .= implode("\n", _xmlsitemap_format(array_slice(_xmlsitemap_links(), $previous, $chunk_size)));
|
| 503 |
$output .= "\n</urlset>";
|
| 504 |
return $output;
|
| 505 |
}
|
| 506 |
|
| 507 |
/**
|
| 508 |
* Modified version of file_create_url(). Allows us to remove language
|
| 509 |
* prefixes.
|
| 510 |
* @param $path: the path to the file
|
| 511 |
* @return A URL to the file
|
| 512 |
*/
|
| 513 |
function _xmlsitemap_file_create_url($path) {
|
| 514 |
$path = trim(substr($path, strlen(file_directory_path())), '\\/');
|
| 515 |
switch (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC)) {
|
| 516 |
case FILE_DOWNLOADS_PUBLIC:
|
| 517 |
return $GLOBALS['base_url'] .'/'. file_directory_path() .'/'. str_replace('\\', '/', $path);
|
| 518 |
case FILE_DOWNLOADS_PRIVATE:
|
| 519 |
return xmlsitemap_url('system/files/'. $path, NULL, NULL, NULL, TRUE);
|
| 520 |
}
|
| 521 |
}
|
| 522 |
|
| 523 |
/**
|
| 524 |
* Implementation of hook_file_download().
|
| 525 |
*/
|
| 526 |
function xmlsitemap_file_download($file) {
|
| 527 |
if ($file == 'xmlsitemap/gss.xsl') {
|
| 528 |
return array('Content-Type:');
|
| 529 |
}
|
| 530 |
}
|
| 531 |
|
| 532 |
/**
|
| 533 |
* Get all site map links.
|
| 534 |
* @return An array of links from hook_xmlsitemap_links().
|
| 535 |
*/
|
| 536 |
function _xmlsitemap_links() {
|
| 537 |
static $links;
|
| 538 |
if (!isset($links)) {
|
| 539 |
$entries = module_invoke_all('xmlsitemap_links');
|
| 540 |
if (!empty($entries)) {
|
| 541 |
foreach ($entries as $key => $link) {
|
| 542 |
$lastmod[$key] = $link['#lastmod'];
|
| 543 |
}
|
| 544 |
array_multisort($lastmod, $entries);
|
| 545 |
}
|
| 546 |
$links = $entries;
|
| 547 |
}
|
| 548 |
return $links;
|
| 549 |
}
|
| 550 |
|
| 551 |
/**
|
| 552 |
* Process an array of links.
|
| 553 |
* @param $entries: An array of links to process
|
| 554 |
* @return A string of formatted links
|
| 555 |
*/
|
| 556 |
function _xmlsitemap_format($entries) {
|
| 557 |
$links = array();
|
| 558 |
foreach ($entries as $entry) {
|
| 559 |
if (isset($entry['#loc'])) {
|
| 560 |
$link = ' <url>'."\n";
|
| 561 |
$link .= ' <loc>'. check_url($entry['#loc']) .'</loc>'."\n";
|
| 562 |
if (isset($entry['#lastmod'])) {
|
| 563 |
$link .= ' <lastmod>'. gmdate('Y-m-d\TH:i:s+00:00', $entry['#lastmod']) .'</lastmod>'."\n";
|
| 564 |
}
|
| 565 |
if (isset($entry['#changefreq'])) {
|
| 566 |
$link .= ' <changefreq>'. _xmlsitemap_frequency($entry['#changefreq']) .'</changefreq>'."\n";
|
| 567 |
}
|
| 568 |
if (isset($entry['#priority']) && $entry['#priority'] <= 1 && $entry['#priority'] >= 0) {
|
| 569 |
$link .= ' <priority>'. number_format($entry['#priority'], 1) .'</priority>'."\n";
|
| 570 |
}
|
| 571 |
$link .= ' </url>';
|
| 572 |
$links[] = $link;
|
| 573 |
}
|
| 574 |
}
|
| 575 |
return $links;
|
| 576 |
}
|
| 577 |
|
| 578 |
/**
|
| 579 |
* Determine the frequency of updates to a link.
|
| 580 |
* @param $interval: The number of seconds since last change
|
| 581 |
* @return A string representing the update frequency according to the
|
| 582 |
* sitemaps.org protocol
|
| 583 |
*/
|
| 584 |
function _xmlsitemap_frequency($interval) {
|
| 585 |
$frequencies = array(
|
| 586 |
'always' => 3600,
|
| 587 |
'hourly' => 86400,
|
| 588 |
'daily' => 604800,
|
| 589 |
'weekly' => 2419200,
|
| 590 |
'monthly' => 29030400,
|
| 591 |
'yearly' => 100000000,
|
| 592 |
'never' => 0,
|
| 593 |
);
|
| 594 |
$frequency = 'never';
|
| 595 |
if (array_key_exists($interval, $frequencies)) {
|
| 596 |
$frequency = $interval;
|
| 597 |
}
|
| 598 |
elseif (is_numeric($interval)) {
|
| 599 |
foreach ($frequencies as $frequency => $value) {
|
| 600 |
if ($interval < $value) {
|
| 601 |
break;
|
| 602 |
}
|
| 603 |
}
|
| 604 |
}
|
| 605 |
return $frequency;
|
| 606 |
}
|
| 607 |
|
| 608 |
/**
|
| 609 |
* Implementation of hook_xmlsitemap_links().
|
| 610 |
*/
|
| 611 |
function xmlsitemap_xmlsitemap_links($type = NULL, $excludes = array()) {
|
| 612 |
$links = array();
|
| 613 |
if (!isset($type)) {
|
| 614 |
global $base_url;
|
| 615 |
$links[] = array('#loc' => "$base_url/", '#changefreq' => 'always', '#priority' => variable_get('xmlsitemap_front_page_priority', 1));
|
| 616 |
$links = array_merge($links, _xmlsitemap_additional_links());
|
| 617 |
$links = array_merge($links, module_invoke_all('gsitemap'));
|
| 618 |
$links = array_merge($links, _xmlsitemap_xml_links());
|
| 619 |
if (!empty($links)) {
|
| 620 |
foreach ($links as $key => $link) {
|
| 621 |
$loc[$key] = $link['#loc'];
|
| 622 |
}
|
| 623 |
array_multisort($loc, $links);
|
| 624 |
}
|
| 625 |
}
|
| 626 |
return $links;
|
| 627 |
}
|
| 628 |
|
| 629 |
/**
|
| 630 |
* Get additional links.
|
| 631 |
* @return An array of links. Each link is an array containing the XML
|
| 632 |
* values for a site map URL.
|
| 633 |
*/
|
| 634 |
function _xmlsitemap_additional_links() {
|
| 635 |
$links = array();
|
| 636 |
$result = db_query("
|
| 637 |
SELECT xa.*, ua.dst AS alias FROM {xmlsitemap_additional} xa
|
| 638 |
LEFT JOIN {url_alias} ua ON xa.pid = ua.pid
|
| 639 |
");
|
| 640 |
while ($link = db_fetch_object($result)) {
|
| 641 |
$age = time() - $link->last_changed;
|
| 642 |
if (!empty($link->previously_changed)) {
|
| 643 |
$interval = $link->last_changed - $link->previously_changed;
|
| 644 |
}
|
| 645 |
else {
|
| 646 |
$interval = 0;
|
| 647 |
}
|
| 648 |
$links[] = array(
|
| 649 |
'#loc' => xmlsitemap_url($link->path, $link->alias, NULL, NULL, TRUE),
|
| 650 |
'#lastmod' => $link->last_changed,
|
| 651 |
'#changefreq' => max($age, $interval),
|
| 652 |
'#priority' => isset($link->priority) ? $link->priority : variable_get('xmlsitemap_additional_links_priority', 0.1),
|
| 653 |
);
|
| 654 |
}
|
| 655 |
return $links;
|
| 656 |
}
|
| 657 |
|
| 658 |
/**
|
| 659 |
* Extract links from site maps returned by hook_xmlsitemap_links().
|
| 660 |
* @return An array of links.
|
| 661 |
*/
|
| 662 |
function _xmlsitemap_xml_links() {
|
| 663 |
$links = array();
|
| 664 |
$xml = module_invoke_all('xmlsitemap_links', 'xml');
|
| 665 |
$xml = array_merge($xml, module_invoke_all('gsitemap', 'xml'));
|
| 666 |
foreach ($xml as $entries) {
|
| 667 |
$start = strpos($entries, '<url>');
|
| 668 |
if ($start !== FALSE) {
|
| 669 |
$length = strpos($entries, '</urlset>') - $start;
|
| 670 |
$entries = substr($entries, $start, $length);
|
| 671 |
$entries = explode('<url>', $entries);
|
| 672 |
foreach ($entries as $value) {
|
| 673 |
if (($start = strpos($value, '<loc>')) !== FALSE) {
|
| 674 |
$length = $start + strlen('<loc>');
|
| 675 |
$link['#loc'] = substr($value, $length, strpos($value, '</loc>') - $length);
|
| 676 |
if (($start = strpos($value, '<lastmod>')) !== FALSE) {
|
| 677 |
$length = $start + strlen('<lastmod>');
|
| 678 |
$t = array_shift(explode('+', substr($value, $length, strpos($value, '</lastmod>') - $length)));
|
| 679 |
$t = explode('T', $t);
|
| 680 |
$t = array_merge(explode('-', $t[0]), explode(':', $t[1]));
|
| 681 |
$link['#lastmod'] = gmmktime($t[3], $t[4], $t[5], $t[1], $t[2], $t[0]);
|
| 682 |
}
|
| 683 |
if (($start = strpos($value, '<changefreq>')) !== FALSE) {
|
| 684 |
$length = $start + strlen('<changefreq>');
|
| 685 |
$link['#changefreq'] = substr($value, $length, strpos($value, '</changefreq>') - $length);
|
| 686 |
}
|
| 687 |
if (($start = strpos($value, '<priority>')) !== FALSE) {
|
| 688 |
$length = $start + strlen('<priority>');
|
| 689 |
$link['#priority'] = substr($value, $length, strpos($value, '</priority>') - $length);
|
| 690 |
}
|
| 691 |
$links[] = $link;
|
| 692 |
}
|
| 693 |
}
|
| 694 |
}
|
| 695 |
}
|
| 696 |
return $links;
|
| 697 |
}
|
| 698 |
|
| 699 |
/**
|
| 700 |
* Modified version of url(). We don't want to do a separate database query
|
| 701 |
* for each url, so we pass the alias as an extra parameter.
|
| 702 |
* @param $alias: The URL alias. Default is NULL.
|
| 703 |
* @return The fully formatted URL
|
| 704 |
*/
|
| 705 |
function xmlsitemap_url($path = NULL, $alias = NULL, $query = NULL, $fragment = NULL, $absolute = FALSE) {
|
| 706 |
if (isset($fragment)) {
|
| 707 |
$fragment = "#$fragment";
|
| 708 |
}
|
| 709 |
$colonpos = strpos($path, ':');
|
| 710 |
if ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && filter_xss_bad_protocol($path, FALSE) == check_plain($path)) {
|
| 711 |
if (strpos($path, '#') !== FALSE) {
|
| 712 |
list($path, $old_fragment) = explode('#', $path, 2);
|
| 713 |
if (isset($old_fragment) && !isset($fragment)) {
|
| 714 |
$fragment = "#$old_fragment";
|
| 715 |
}
|
| 716 |
}
|
| 717 |
if (isset($query)) {
|
| 718 |
$path .= (strpos($path, '?') !== FALSE ? '&' : '?') . $query;
|
| 719 |
}
|
| 720 |
return $path . $fragment;
|
| 721 |
}
|
| 722 |
global $base_url;
|
| 723 |
static $script;
|
| 724 |
static $clean_url;
|
| 725 |
$script = isset($script) ? $script : strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === FALSE ? 'index.php' : '';
|
| 726 |
$clean_url = isset($clean_url) ? $clean_url : variable_get('clean_url', FALSE);
|
| 727 |
$base = ($absolute ? $base_url .'/' : base_path());
|
| 728 |
|
| 729 |
if (!empty($path) && $path != '<front>') {
|
| 730 |
$path = _xmlsitemap_get_path_alias($path, $alias);
|
| 731 |
$path = drupal_urlencode($path);
|
| 732 |
|
| 733 |
if (!$clean_url) {
|
| 734 |
if (isset($query)) {
|
| 735 |
return $base . $script .'?q='. $path .'&'. $query . $fragment;
|
| 736 |
}
|
| 737 |
else {
|
| 738 |
return $base . $script .'?q='. $path . $fragment;
|
| 739 |
}
|
| 740 |
}
|
| 741 |
else {
|
| 742 |
if (isset($query)) {
|
| 743 |
return $base . $path .'?'. $query . $fragment;
|
| 744 |
}
|
| 745 |
else {
|
| 746 |
return $base . $path . $fragment;
|
| 747 |
}
|
| 748 |
}
|
| 749 |
}
|
| 750 |
else {
|
| 751 |
if (isset($query)) {
|
| 752 |
return $base . $script .'?'. $query . $fragment;
|
| 753 |
}
|
| 754 |
else {
|
| 755 |
return $base . $fragment;
|
| 756 |
}
|
| 757 |
}
|
| 758 |
}
|
| 759 |
|
| 760 |
/**
|
| 761 |
* Modified version of drupal_get_path_alias() for xmlsitemap_url().
|
| 762 |
* @param $path: An internal Drupal path
|
| 763 |
* @param $alias: The URL alias. Default is NULL.
|
| 764 |
* @return A processed path
|
| 765 |
*/
|
| 766 |
function _xmlsitemap_get_path_alias($path, $alias = NULL) {
|
| 767 |
$result = $path;
|
| 768 |
if (!empty($alias)) {
|
| 769 |
$result = $alias;
|
| 770 |
}
|
| 771 |
if (function_exists('custom_url_rewrite_outbound')) {
|
| 772 |
custom_url_rewrite_outbound($result, $customarray = array('alias' => $alias), $path);
|
| 773 |
}
|
| 774 |
return $result;
|
| 775 |
}
|
| 776 |
|
| 777 |
/**
|
| 778 |
* Implementation of hook_cron().
|
| 779 |
*/
|
| 780 |
function xmlsitemap_cron() {
|
| 781 |
if (variable_get('xmlsitemap_cron_submit', FALSE) && variable_get('xmlsitemap_changed', FALSE)) {
|
| 782 |
_xmlsitemap_ping();
|
| 783 |
}
|
| 784 |
}
|
| 785 |
|
| 786 |
/**
|
| 787 |
* Mark the site map as changed and the cache as needing update.
|
| 788 |
* @return None
|
| 789 |
*/
|
| 790 |
function xmlsitemap_update_sitemap() {
|
| 791 |
variable_set('xmlsitemap_changed', TRUE);
|
| 792 |
variable_set('xmlsitemap_update', TRUE);
|
| 793 |
if (variable_get('xmlsitemap_submit', FALSE)) {
|
| 794 |
_xmlsitemap_submit_on_exit();
|
| 795 |
}
|
| 796 |
}
|
| 797 |
|
| 798 |
/**
|
| 799 |
* Schedule a call to _xmlsitemap_ping() to be run on exit. Use this
|
| 800 |
* function instead of _xmlsitemap_ping() to avoid a delay in outputting
|
| 801 |
* the page to the user.
|
| 802 |
* @return TRUE if the function has been called previously, FALSE otherwise.
|
| 803 |
*/
|
| 804 |
function _xmlsitemap_submit_on_exit() {
|
| 805 |
static $called = FALSE;
|
| 806 |
$return = $called;
|
| 807 |
$called = TRUE;
|
| 808 |
return $return;
|
| 809 |
}
|
| 810 |
|
| 811 |
/**
|
| 812 |
* Implementation of hook_exit().
|
| 813 |
*/
|
| 814 |
function xmlsitemap_exit() {
|
| 815 |
if (_xmlsitemap_submit_on_exit()) {
|
| 816 |
_xmlsitemap_ping();
|
| 817 |
}
|
| 818 |
}
|
| 819 |
|
| 820 |
/**
|
| 821 |
* Submit the site map to search engines.
|
| 822 |
* @return None
|
| 823 |
*/
|
| 824 |
function _xmlsitemap_ping() {
|
| 825 |
$status = _xmlsitemap_update_cache();
|
| 826 |
if ($status) {
|
| 827 |
module_invoke_all('xmlsitemap_engines', 'ping');
|
| 828 |
variable_set('xmlsitemap_changed', FALSE);
|
| 829 |
}
|
| 830 |
}
|
| 831 |
|