xmlsitemap.module 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. <?php
  2. // $Id: xmlsitemap.module,v 1.43 2010/01/24 03:57:17 davereid Exp $
  3. /**
  4. * @defgroup xmlsitemap XML sitemap: create sitemaps.org sitemaps.
  5. */
  6. /**
  7. * @file
  8. * Main file for the xmlsitemap module.
  9. */
  10. /**
  11. * The maximum number of links in one sitemap chunk file.
  12. */
  13. define('XMLSITEMAP_MAX_SITEMAP_LINKS', 50000);
  14. /**
  15. * The maximum filesize of a sitemap chunk file.
  16. */
  17. define('XMLSITEMAP_MAX_SITEMAP_FILESIZE', 10485760);
  18. /**
  19. * The maximum number of links in one sitemap index file.
  20. */
  21. define('XMLSITEMAP_MAX_SITEMAP_CHUNKS', 1000);
  22. define('XMLSITEMAP_FREQUENCY_YEARLY', 31449600); // 60 * 60 * 24 * 7 * 52
  23. define('XMLSITEMAP_FREQUENCY_MONTHLY', 2419200); // 60 * 60 * 24 * 7 * 4
  24. define('XMLSITEMAP_FREQUENCY_WEEKLY', 604800); // 60 * 60 * 24 * 7
  25. define('XMLSITEMAP_FREQUENCY_DAILY', 86400); // 60 * 60 * 24
  26. define('XMLSITEMAP_FREQUENCY_HOURLY', 3600); // 60 * 60
  27. define('XMLSITEMAP_FREQUENCY_ALWAYS', 60);
  28. /**
  29. * Implements hook_help().
  30. */
  31. function xmlsitemap_help($path, $arg) {
  32. $output = '';
  33. switch ($path) {
  34. case 'admin/help/xmlsitemap':
  35. return;
  36. case 'admin/help#xmlsitemap':
  37. $sitemaps = xmlsitemap_get_sitemaps(TRUE);
  38. $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>';
  39. break;
  40. case 'admin/config/search/xmlsitemap':
  41. break;
  42. case 'admin/config/search/xmlsitemap/rebuild':
  43. $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>';
  44. }
  45. if (arg(0) == 'admin' && strpos($path, 'xmlsitemap') !== FALSE) {
  46. if ($arg[1] == 'config' && user_access('administer xmlsitemap')) {
  47. module_load_install('xmlsitemap');
  48. xmlsitemap_check_status();
  49. }
  50. module_load_include('inc', 'xmlsitemap');
  51. if ($blurb = _xmlsitemap_get_blurb()) {
  52. $output .= $blurb;
  53. }
  54. }
  55. return $output;
  56. }
  57. /**
  58. * Implements hook_perm().
  59. */
  60. function xmlsitemap_permission() {
  61. return array(
  62. 'administer xmlsitemap' => array(
  63. 'title' => t('Administer XML sitemap settings.'),
  64. ),
  65. );
  66. }
  67. /**
  68. * Implements hook_menu().
  69. */
  70. function xmlsitemap_menu() {
  71. $items['admin/config/search/xmlsitemap'] = array(
  72. 'title' => 'XML sitemap',
  73. 'description' => 'Configure the XML sitemap.',
  74. 'page callback' => 'drupal_get_form',
  75. 'page arguments' => array('xmlsitemap_settings_form'),
  76. 'access arguments' => array('administer xmlsitemap'),
  77. 'file' => 'xmlsitemap.admin.inc',
  78. );
  79. $items['admin/config/search/xmlsitemap/settings'] = array(
  80. 'title' => 'Settings',
  81. 'access arguments' => array('administer xmlsitemap'),
  82. 'type' => MENU_DEFAULT_LOCAL_TASK,
  83. 'file' => 'xmlsitemap.admin.inc',
  84. 'weight' => -10,
  85. );
  86. $items['admin/config/search/xmlsitemap/rebuild'] = array(
  87. 'title' => 'Rebuild',
  88. 'description' => 'Rebuild the site map.',
  89. 'page callback' => 'drupal_get_form',
  90. 'page arguments' => array('xmlsitemap_rebuild_form'),
  91. 'access arguments' => array('administer xmlsitemap'),
  92. 'type' => MENU_LOCAL_TASK,
  93. 'file' => 'xmlsitemap.admin.inc',
  94. 'weight' => 10,
  95. );
  96. $items['sitemap.xml'] = array(
  97. 'page callback' => 'xmlsitemap_output_chunk',
  98. 'access arguments' => array('access content'),
  99. 'type' => MENU_CALLBACK,
  100. 'file' => 'xmlsitemap.pages.inc',
  101. );
  102. $chunks = xmlsitemap_get_chunk_count();
  103. if ($chunks > 1) {
  104. for ($i = 1; $i <= $chunks; $i++) {
  105. $items['sitemap-' . $i . '.xml'] = array(
  106. 'page callback' => 'xmlsitemap_output_chunk',
  107. 'page arguments' => array((string) $i),
  108. 'access arguments' => array('access content'),
  109. 'type' => MENU_CALLBACK,
  110. 'file' => 'xmlsitemap.pages.inc',
  111. );
  112. }
  113. }
  114. $items['sitemap.xsl'] = array(
  115. 'page callback' => 'xmlsitemap_output_xsl',
  116. 'access callback' => TRUE,
  117. 'type' => MENU_CALLBACK,
  118. 'file' => 'xmlsitemap.pages.inc',
  119. );
  120. return $items;
  121. }
  122. /**
  123. * Implements hook_cron().
  124. */
  125. function xmlsitemap_cron() {
  126. // If there were no new or changed links, skip.
  127. if (!variable_get('xmlsitemap_regenerate_needed', FALSE)) {
  128. return;
  129. }
  130. // If the minimum sitemap lifetime hasn't been passed, skip.
  131. $lifetime = REQUEST_TIME - variable_get('xmlsitemap_generated_last', 0);
  132. if ($lifetime < variable_get('xmlsitemap_minimum_lifetime', 0)) {
  133. return;
  134. }
  135. // Regenerate the sitemap XML files.
  136. module_load_include('inc', 'xmlsitemap');
  137. xmlsitemap_regenerate();
  138. }
  139. /**
  140. * Implements hook_xmlsitemap_links().
  141. */
  142. function xmlsitemap_xmlsitemap_links() {
  143. // Frontpage link.
  144. $links[] = array(
  145. 'type' => 'frontpage',
  146. 'id' => 0,
  147. 'loc' => '',
  148. );
  149. return $links;
  150. }
  151. /**
  152. * Implements hook_xmlsitemap_link_alter().
  153. */
  154. function xmlsitemap_xmlsitemap_link_alter(&$link) {
  155. // Alter the frontpage priority.
  156. if ($link['type'] == 'frontpage' || $link['loc'] == '' || $link['loc'] == variable_get('site_frontpage', 'node')) {
  157. $link['priority'] = xmlsitemap_var('frontpage_priority');
  158. $link['changefreq'] = xmlsitemap_var('frontpage_changefreq');
  159. }
  160. }
  161. /**
  162. * Implements hook_robotstxt().
  163. */
  164. function xmlsitemap_robotstxt() {
  165. module_load_include('inc', 'xmlsitemap');
  166. $sitemaps = xmlsitemap_get_sitemaps();
  167. foreach ($sitemaps as $index => $sitemap) {
  168. $sitemaps[$index] = 'Sitemap: ' . $sitemap;
  169. }
  170. return $sitemaps;
  171. }
  172. /**
  173. * Get an array of the current site's sitemaps.
  174. *
  175. * @param $links
  176. * A boolean if TRUE, the array elements will be HTML links.
  177. * @return
  178. * An array of sitemaps.
  179. */
  180. function xmlsitemap_get_sitemaps($links = FALSE) {
  181. static $sitemaps = array();
  182. if (!$sitemaps) {
  183. $url_options = xmlsitemap_get_url_options();
  184. $sitemap_languages = xmlsitemap_var('languages');
  185. natsort($sitemap_languages);
  186. foreach ($sitemap_languages as $language) {
  187. $url_options['language'] = xmlsitemap_language_load($language);
  188. $sitemap = url('sitemap.xml', $url_options);
  189. $sitemaps[$language] = $links ? l($sitemap, $sitemap) : $sitemap;
  190. }
  191. }
  192. return $sitemaps;
  193. }
  194. /**
  195. * Return a list of commonly used parameters for url() used by XML sitemap.
  196. *
  197. * @see url()
  198. */
  199. function xmlsitemap_get_url_options($options = array()) {
  200. return $options + array(
  201. 'absolute' => TRUE,
  202. 'base_url' => xmlsitemap_var('base_url'),
  203. );
  204. }
  205. /**
  206. * Determine the frequency of updates to a link.
  207. *
  208. * @param $interval
  209. * An interval value in seconds.
  210. * @return
  211. * A string representing the update frequency according to the sitemaps.org
  212. * protocol.
  213. */
  214. function xmlsitemap_get_changefreq($interval) {
  215. if ($interval <= 0 || !is_numeric($interval)) {
  216. return FALSE;
  217. }
  218. foreach (xmlsitemap_get_changefreq_options() as $value => $frequency) {
  219. if ($interval <= $value) {
  220. return $frequency;
  221. }
  222. }
  223. return 'never';
  224. }
  225. /**
  226. * Get the current number of sitemap chunks.
  227. */
  228. function xmlsitemap_get_chunk_count($reset = FALSE) {
  229. static $chunks;
  230. if (!isset($chunks) || $reset) {
  231. $count = max(xmlsitemap_get_link_count($reset), 1);
  232. $chunks = ceil($count / xmlsitemap_get_chunk_size($reset));
  233. }
  234. return $chunks;
  235. }
  236. /**
  237. * Get the current number of sitemap links.
  238. */
  239. function xmlsitemap_get_link_count($reset = FALSE) {
  240. static $count;
  241. if (!isset($count) || $reset) {
  242. $count = db_query("SELECT COUNT(id) FROM {xmlsitemap} WHERE access = 1 AND status = 1")->fetchField();
  243. }
  244. return $count;
  245. }
  246. /**
  247. * Get the sitemap chunk size.
  248. *
  249. * This function is useful with the chunk size is set to automatic as it will
  250. * calculate the appropriate value. Use this function instead of @code
  251. * xmlsitemap_var('chunk_size') @endcode when the actual value is needed.
  252. *
  253. * @param $reset
  254. * A boolean to reset the saved, static result. Defaults to FALSE.
  255. * @return
  256. * An integer with the number of links in each sitemap page.
  257. */
  258. function xmlsitemap_get_chunk_size($reset = FALSE) {
  259. static $size;
  260. if (!isset($size) || $reset) {
  261. $size = xmlsitemap_var('chunk_size');
  262. if ($size === 'auto') {
  263. $count = max(xmlsitemap_get_link_count($reset), 1); // Prevent divide by zero.
  264. $size = min(ceil($count / 10000) * 5000, XMLSITEMAP_MAX_SITEMAP_LINKS);
  265. }
  266. }
  267. return $size;
  268. }
  269. /**
  270. * Recalculate the changefreq of a sitemap link.
  271. *
  272. * @param $link
  273. * A sitemap link array.
  274. */
  275. function xmlsitemap_recalculate_changefreq(&$link) {
  276. $link['changefreq'] = round((($link['changefreq'] * $link['changecount']) + (REQUEST_TIME - $link['lastmod'])) / ($link['changecount'] + 1));
  277. $link['changecount']++;
  278. $link['lastmod'] = REQUEST_TIME;
  279. }
  280. /**
  281. * Calculates the average interval between UNIX timestamps.
  282. *
  283. * @param $timestamps
  284. * An array of UNIX timestamp integers.
  285. * @return
  286. * An integer of the average interval.
  287. */
  288. function xmlsitemap_calculate_changefreq($timestamps) {
  289. sort($timestamps);
  290. $count = count($timestamps) - 1;
  291. $diff = 0;
  292. for ($i = 0; $i < $count; $i++) {
  293. $diff += $timestamps[$i + 1] - $timestamps[$i];
  294. }
  295. return $count > 0 ? round($diff / $count) : 0;
  296. }
  297. /**
  298. * Check if there is a visible sitemap link given a certain set of conditions.
  299. *
  300. * @param $conditions
  301. * An array of values to match keyed by field.
  302. * @param $flag
  303. * An optional boolean that if TRUE, will set the regenerate needed flag if
  304. * there is a match. Defaults to FALSE.
  305. * @return
  306. * TRUE if there is a visible link, or FALSE otherwise.
  307. */
  308. function _xmlsitemap_check_changed_links(array $conditions = array(), array $updates = array(), $flag = FALSE) {
  309. // If we are changing status or access, check for negative current values.
  310. $conditions['status'] = (!empty($updates['status']) && empty($condition['status'])) ? 0 : 1;
  311. $conditions['access'] = (!empty($updates['access']) && empty($condition['access'])) ? 0 : 1;
  312. $query = db_select('xmlsitemap');
  313. $query->addExpression('1');
  314. foreach ($conditions as $field => $value) {
  315. $query->condition($field, $value);
  316. }
  317. $query->range(0, 1);
  318. $changed = $query->execute()->fetchField();
  319. if ($changed && $flag) {
  320. variable_set('xmlsitemap_regenerate_needed', TRUE);
  321. }
  322. return $changed;
  323. }
  324. /**
  325. * Check if there is sitemap link is changed from the existing data.
  326. *
  327. * @param $link
  328. * An array of the sitemap link.
  329. * @param $original_link
  330. * An optional array of the existing data. This should only contain the
  331. * fields necessary for comparison. If not provided the existing data will be
  332. * loaded from the database.
  333. * @param $flag
  334. * An optional boolean that if TRUE, will set the regenerate needed flag if
  335. * there is a match. Defaults to FALSE.
  336. * @return
  337. * TRUE if the link is changed, or FALSE otherwise.
  338. */
  339. function _xmlsitemap_check_changed_link(array $link, $original_link = NULL, $flag = FALSE) {
  340. $changed = FALSE;
  341. if ($original_link === NULL) {
  342. // Load only the fields necessary for data to be changed in the sitemap.
  343. $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();
  344. }
  345. if (!$original_link) {
  346. if ($link['access'] && $link['status']) {
  347. // Adding a new visible link.
  348. $changed = TRUE;
  349. }
  350. }
  351. else {
  352. if (!($original_link['access'] && $original_link['status']) && $link['access'] && $link['status']) {
  353. // Changing a non-visible link to a visible link.
  354. $changed = TRUE;
  355. }
  356. elseif ($original_link['access'] && $original_link['status'] && array_diff_assoc($original_link, $link)) {
  357. // Changing a visible link
  358. $changed = TRUE;
  359. }
  360. }
  361. if ($changed && $flag) {
  362. variable_set('xmlsitemap_regenerate_needed', TRUE);
  363. }
  364. return $changed;
  365. }
  366. /**
  367. * Load a specific sitemap link.
  368. *
  369. * @param $conditions
  370. * An array of values to match keyed by field.
  371. * @return
  372. * An array representing the first sitemap link matching the conditions found.
  373. *
  374. * @todo Convert to use $type and $id as parameters.
  375. */
  376. function xmlsitemap_load_link(array $conditions) {
  377. $query = db_select('xmlsitemap', 'x');
  378. $query->fields('x');
  379. foreach ($conditions as $field => $value) {
  380. $query->condition($field, $value);
  381. }
  382. $query->range(0, 1);
  383. $link = $query->execute()->fetchAssoc();
  384. // Allow other modules to respond after loading the link.
  385. //module_invoke_all('xmlsitemap_load_link', $link, $conditions, $args);
  386. return $link;
  387. }
  388. /**
  389. * Saves or updates a sitemap link.
  390. *
  391. * @param $link
  392. * An array with a sitemap link.
  393. */
  394. function xmlsitemap_save_link(array $link) {
  395. $link += array(
  396. 'access' => 1,
  397. 'status' => 1,
  398. 'status_override' => 0,
  399. 'lastmod' => 0,
  400. 'priority' => 0.5,
  401. 'priority_override' => 0,
  402. 'changefreq' => 0,
  403. 'changecount' => 0,
  404. 'language' => LANGUAGE_NONE,
  405. );
  406. // Allow other modules to alter the link before saving.
  407. drupal_alter('xmlsitemap_link', $link);
  408. // Temporary validation checks.
  409. // @todo Remove in final?
  410. if ($link['priority'] < 0 || $link['priority'] > 1) {
  411. trigger_error(t('Invalid sitemap link priority %priority.<br />@link', array('%priority' => $link['priority'], '@link' => var_export($link, TRUE))), E_USER_ERROR);
  412. }
  413. if ($link['changecount'] < 0) {
  414. 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);
  415. $link['changecount'] = 0;
  416. }
  417. $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();
  418. // Check if this is a changed link and set the regenerate flag if necessary.
  419. if (!variable_get('xmlsitemap_regenerate_needed', FALSE)) {
  420. _xmlsitemap_check_changed_link($link, $existing, TRUE);
  421. }
  422. if ($existing) {
  423. drupal_write_record('xmlsitemap', $link, array('type', 'id'));
  424. }
  425. else {
  426. drupal_write_record('xmlsitemap', $link);
  427. }
  428. // Allow other modules to respond after saving the link.
  429. //module_invoke_all('xmlsitemap_save_link', $link);
  430. return $link;
  431. }
  432. /**
  433. * Perform a mass update of sitemap data.
  434. *
  435. * If visible links are updated, this will automatically set the regenerate
  436. * needed flag to TRUE.
  437. *
  438. * @param $updates
  439. * An array of values to update fields to, keyed by field name.
  440. * @param $conditions
  441. * An array of values to match keyed by field.
  442. * @return
  443. * The number of links that were updated.
  444. */
  445. function xmlsitemap_update_links($updates = array(), $conditions = array()) {
  446. // If we are going to modify a visible sitemap link, we will need to set
  447. // the regenerate needed flag.
  448. if (!variable_get('xmlsitemap_regenerate_needed', FALSE)) {
  449. _xmlsitemap_check_changed_links($conditions, $updates, TRUE);
  450. }
  451. // Process updates.
  452. $query = db_update('xmlsitemap');
  453. $query->fields($updates);
  454. foreach ($conditions as $field => $value) {
  455. $query->condition($field, $value);
  456. }
  457. return $query->execute();
  458. }
  459. /**
  460. * Delete one or more sitemap links.
  461. *
  462. * If a visible sitemap link was deleted, this will automatically set the
  463. * regenerate needed flag.
  464. *
  465. * @param $conditions
  466. * An array of values to match keyed by field.
  467. * @return
  468. * The number of links that were deleted.
  469. */
  470. function xmlsitemap_delete_link(array $conditions) {
  471. if (!variable_get('xmlsitemap_regenerate_needed', TRUE)) {
  472. _xmlsitemap_check_changed_links($conditions, array(), TRUE);
  473. }
  474. $query = db_delete('xmlsitemap');
  475. foreach ($conditions as $field => $value) {
  476. $query->condition($field, $value);
  477. }
  478. return $query->execute();
  479. }
  480. /**
  481. * Get the filename of a specific sitemap page chunk.
  482. *
  483. * @param $chunk
  484. * An integer representing the integer of the sitemap page chunk.
  485. * @param $language
  486. * A string with a language code.
  487. * @return
  488. * A file path to the expected chunk file.
  489. *
  490. * @todo Move to xmlsitemap.inc
  491. */
  492. function xmlsitemap_get_chunk_file($chunk = 0, $language = LANGUAGE_NONE, $extension = 'xml') {
  493. return xmlsitemap_get_directory() . "/xmlsitemap-{$language}-{$chunk}.{$extension}";
  494. }
  495. /**
  496. * Implements hook_form_alter().
  497. *
  498. * Set the regeneration needed flag if settings are changed.
  499. */
  500. function xmlsitemap_form_alter(&$form, $form_state, $form_id) {
  501. $forms = array(
  502. 'locale_languages_overview_form', // Language settings
  503. 'xmlsitemap_settings_form', // XML sitemap settings
  504. );
  505. if (in_array($form_id, $forms)) {
  506. array_unshift($form['#submit'], 'xmlsitemap_form_submit_flag_regenerate');
  507. }
  508. }
  509. /**
  510. * Submit handler; Set the regenerate needed flag if variables have changed.
  511. *
  512. * This function needs to be called before system_settings_form_submit() or any
  513. * calls to variable_set().
  514. */
  515. function xmlsitemap_form_submit_flag_regenerate($form, $form_state) {
  516. foreach ($form_state['values'] as $variable => $value) {
  517. $stored_value = variable_get($variable, 'not_a_variable');
  518. if (is_array($value) && !empty($form_state['values']['array_filter'])) {
  519. $value = array_keys(array_filter($value));
  520. }
  521. if ($stored_value != 'not_a_variable' && $stored_value != $value) {
  522. variable_set('xmlsitemap_regenerate_needed', TRUE);
  523. 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);
  524. return;
  525. }
  526. }
  527. }
  528. /**
  529. * Internal default variables for xmlsitemap_var().
  530. */
  531. function xmlsitemap_variables() {
  532. return array(
  533. 'xmlsitemap_rebuild_needed' => FALSE,
  534. 'xmlsitemap_regenerate_needed' => FALSE,
  535. 'xmlsitemap_generated_last' => 0,
  536. 'xmlsitemap_minimum_lifetime' => 0,
  537. 'xmlsitemap_xsl' => TRUE,
  538. 'xmlsitemap_languages' => array(language_default('language')),
  539. 'xmlsitemap_chunk_size' => 'auto',
  540. 'xmlsitemap_batch_limit' => 100,
  541. 'xmlsitemap_path' => 'xmlsitemap',
  542. 'xmlsitemap_base_url' => $GLOBALS['base_url'],
  543. 'xmlsitemap_developer_mode' => FALSE,
  544. 'xmlsitemap_frontpage_priority' => '1.0',
  545. 'xmlsitemap_frontpage_changefreq' => XMLSITEMAP_FREQUENCY_DAILY,
  546. // Removed variables are set to NULL so they can still be deleted.
  547. 'xmlsitemap_gz' => FALSE,
  548. 'xmlsitemap_regenerate_last' => NULL,
  549. 'xmlsitemap_custom_links' => NULL,
  550. 'xmlsitemap_priority_default' => NULL,
  551. );
  552. }
  553. /**
  554. * Internal implementation of variable_get().
  555. */
  556. function xmlsitemap_var($name, $default = NULL) {
  557. $defaults = &drupal_static(__FUNCTION__);
  558. if (!isset($defaults)) {
  559. $defaults = xmlsitemap_variables();
  560. }
  561. $name = 'xmlsitemap_'. $name;
  562. // @todo Remove when stable.
  563. if (!isset($defaults[$name])) {
  564. trigger_error(strtr('Default variable for %variable not found.', array('%variable' => drupal_placeholder($name))));
  565. }
  566. return variable_get($name, isset($default) || !isset($defaults[$name]) ? $default : $defaults[$name]);
  567. }
  568. /**
  569. * Set the current user stored in $GLOBALS['user'].
  570. *
  571. * @todo Remove when http://drupal.org/node/287292 is fixed.
  572. */
  573. function xmlsitemap_switch_user($new_user = NULL) {
  574. global $user;
  575. $user_original = &drupal_static(__FUNCTION__);
  576. if (!isset($new_user)) {
  577. if (isset($user_original)) {
  578. // Restore the original user.
  579. $user = $user_original;
  580. $user_original = NULL;
  581. drupal_save_session(TRUE);
  582. }
  583. else {
  584. return FALSE;
  585. }
  586. }
  587. elseif (is_numeric($new_user) && $user->uid != $new_user) {
  588. // Get the full user object.
  589. if (!$new_user) {
  590. $new_user = drupal_anonymous_user();
  591. }
  592. elseif (!$new_user = user_load($new_user)) {
  593. return FALSE;
  594. }
  595. // Backup the original user object.
  596. if (!isset($user_original)) {
  597. $user_original = $user;
  598. drupal_save_session(FALSE);
  599. }
  600. $user = $new_user;
  601. }
  602. elseif (is_object($new_user) && $user->uid != $new_user->uid) {
  603. // Backup the original user object.
  604. if (!isset($user_original)) {
  605. $user_original = $user;
  606. drupal_save_session(FALSE);
  607. }
  608. $user = $new_user;
  609. }
  610. else {
  611. return FALSE;
  612. }
  613. return $user;
  614. }
  615. /**
  616. * Restore the user that was originally loaded.
  617. *
  618. * @return
  619. * Current user.
  620. */
  621. function xmlsitemap_restore_user() {
  622. return xmlsitemap_switch_user();
  623. }
  624. function xmlsitemap_process_form_link_options($form, &$form_state) {
  625. $link = &$form_state['values']['xmlsitemap'];
  626. $fields = array('status' => 1, 'priority' => 0.5);
  627. foreach ($fields as $field => $default) {
  628. if ($link[$field] === 'default') {
  629. $link[$field] = isset($link[$field . '_default']) ? $link[$field . '_default'] : $default;
  630. $link[$field . '_override'] = 0;
  631. }
  632. else {
  633. $link[$field . '_override'] = 1;
  634. }
  635. }
  636. }
  637. /**
  638. * @todo Document this function.
  639. * @todo Make these translatable
  640. */
  641. function xmlsitemap_get_changefreq_options() {
  642. return array(
  643. XMLSITEMAP_FREQUENCY_ALWAYS => 'always',
  644. XMLSITEMAP_FREQUENCY_HOURLY => 'hourly',
  645. XMLSITEMAP_FREQUENCY_DAILY => 'daily',
  646. XMLSITEMAP_FREQUENCY_WEEKLY => 'weekly',
  647. XMLSITEMAP_FREQUENCY_MONTHLY => 'monthly',
  648. XMLSITEMAP_FREQUENCY_YEARLY => 'yearly',
  649. );
  650. }
  651. /**
  652. * Returns information about supported sitemap link types.
  653. *
  654. * @param $type
  655. * (optional) The link type to return information for. If omitted,
  656. * information for all link types is returned.
  657. * @param $reset
  658. * (optional) Boolean whether to reset the static cache and do nothing. Only
  659. * used for tests.
  660. *
  661. * @see hook_xmlsitemap_link_info()
  662. * @see hook_xmlsitemap_link_info_alter()
  663. */
  664. function xmlsitemap_get_link_info($type = NULL) {
  665. global $language;
  666. $link_info = &drupal_static(__FUNCTION__);
  667. if (!isset($link_info)) {
  668. if ($cached = cache_get('xmlsitemap:link_info:' . $language->language)) {
  669. $link_info = $cached->data;
  670. }
  671. else {
  672. $link_info = array();
  673. foreach (module_implements('xmlsitemap_link_info') as $module) {
  674. $module_link_info = module_invoke($module, 'xmlsitemap_link_info');
  675. foreach ($module_link_info as $type => $info) {
  676. $module_link_info[$type] += array(
  677. 'type' => $type,
  678. 'module' => $module,
  679. 'purge' => TRUE,
  680. 'table' => FALSE,
  681. );
  682. }
  683. $link_info = array_merge($link_info, $module_link_info);
  684. }
  685. drupal_alter('xmlsitemap_link_info', $link_info);
  686. cache_set('xmlsitemap:link_info:' . $language->language, $link_info);
  687. }
  688. }
  689. if (isset($type)) {
  690. return isset($link_info[$type]) ? $link_info[$type] : NULL;
  691. }
  692. return $link_info;
  693. }
  694. /**
  695. * Implements hook_xmlsitemap_link_info().
  696. */
  697. function xmlsitemap_xmlsitemap_link_info() {
  698. return array(
  699. 'frontpage' => array(),
  700. );
  701. }
  702. function xmlsitemap_get_directory() {
  703. $directory = &drupal_static(__FUNCTION__);
  704. if (!isset($directory)) {
  705. $directory = file_directory_path() . '/' . variable_get('xmlsitemap_path', 'xmlsitemap');
  706. }
  707. return $directory;
  708. }
  709. /**
  710. * Check that the sitemap files directory exists and is writable.
  711. */
  712. function xmlsitemap_check_directory() {
  713. $directory = xmlsitemap_get_directory();
  714. $result = file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
  715. if (!$result) {
  716. watchdog('file system', 'The directory %directory does not exist or is not writable.', array('%directory' => $directory), WATCHDOG_ERROR);
  717. }
  718. return $result;
  719. }
  720. function xmlsitemap_clear_directory($delete = FALSE) {
  721. $directory = xmlsitemap_get_directory();
  722. return _xmlsitemap_delete_recursive($directory, $delete);
  723. }
  724. /**
  725. * Recursively delete all files and folders in the specified filepath.
  726. *
  727. * This is a backport of Drupal 7's file_unmanaged_delete_recursive().
  728. *
  729. * Note that this only deletes visible files with write permission.
  730. *
  731. * @param $path
  732. * A filepath relative to file_directory_path.
  733. * @param $delete_root
  734. * A boolean if TRUE will delete the $path directory afterwards.
  735. */
  736. function _xmlsitemap_delete_recursive($path, $delete_root = FALSE) {
  737. // Resolve streamwrapper URI to local path.
  738. $path = drupal_realpath($path);
  739. if (is_dir($path)) {
  740. $dir = dir($path);
  741. while (($entry = $dir->read()) !== FALSE) {
  742. if ($entry == '.' || $entry == '..') {
  743. continue;
  744. }
  745. $entry_path = $path . '/' . $entry;
  746. file_unmanaged_delete_recursive($entry_path, TRUE);
  747. }
  748. $dir->close();
  749. return $delete_root ? rmdir($path) : TRUE;
  750. }
  751. return file_unmanaged_delete($path);
  752. }
  753. /**
  754. * Load a language object by its language code.
  755. *
  756. * @todo Remove when http://drupal.org/node/660736 is fixed in Drupal core.
  757. *
  758. * @param $language
  759. * A language code. If not provided the default language will be returned.
  760. * @return
  761. * A language object.
  762. */
  763. function xmlsitemap_language_load($language = LANGUAGE_NONE) {
  764. $languages = &drupal_static(__FUNCTION__);
  765. if (!isset($languages)) {
  766. $languages = language_list();
  767. $languages[LANGUAGE_NONE] = NULL;
  768. }
  769. return $languages[$language];
  770. }