/[drupal]/contributions/modules/xmlsitemap/xmlsitemap.module
ViewVC logotype

Contents of /contributions/modules/xmlsitemap/xmlsitemap.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.22 - (show annotations) (download) (as text)
Tue Nov 18 20:35:17 2008 UTC (12 months, 1 week ago) by darrenoh
Branch: MAIN
CVS Tags: HEAD
Changes since 1.21: +4 -3 lines
File MIME type: text/x-php
Fixed handling of invalid interval strings in xmlsitemap_frequency().
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

  ViewVC Help
Powered by ViewVC 1.1.2