xmlsitemap_node.module 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. <?php
  2. /**
  3. * Implements hook_entity_info_alter().
  4. */
  5. function xmlsitemap_node_entity_info_alter(array &$entity_info) {
  6. $entity_info['node']['label'] = t('Content');
  7. $entity_info['node']['bundle label'] = t('Content type');
  8. $entity_info['node']['xmlsitemap'] = array(
  9. 'process callback' => 'xmlsitemap_node_xmlsitemap_process_node_links',
  10. );
  11. }
  12. /**
  13. * Implements hook_cron().
  14. *
  15. * Process old nodes not found in the {xmlsitemap} table.
  16. */
  17. function xmlsitemap_node_cron() {
  18. xmlsitemap_node_xmlsitemap_index_links(xmlsitemap_var('batch_limit'));
  19. }
  20. /**
  21. * Implements hook_xmlsitemap_index_links().
  22. */
  23. function xmlsitemap_node_xmlsitemap_index_links($limit) {
  24. if ($types = xmlsitemap_get_link_type_enabled_bundles('node')) {
  25. $nids = db_query_range("SELECT n.nid FROM {node} n LEFT JOIN {xmlsitemap} x ON x.type = 'node' AND n.nid = x.id WHERE x.id IS NULL AND n.type IN (:types) ORDER BY n.nid DESC", 0, $limit, array(':types' => $types))->fetchCol();
  26. xmlsitemap_node_xmlsitemap_process_node_links($nids);
  27. }
  28. }
  29. /**
  30. * Process node sitemap links.
  31. *
  32. * @param $nids
  33. * An array of node IDs.
  34. */
  35. function xmlsitemap_node_xmlsitemap_process_node_links(array $nids) {
  36. $nodes = node_load_multiple($nids);
  37. foreach ($nodes as $node) {
  38. $link = xmlsitemap_node_create_link($node);
  39. xmlsitemap_link_save($link);
  40. }
  41. }
  42. /**
  43. * Implements hook_node_insert().
  44. */
  45. function xmlsitemap_node_node_insert(stdClass $node) {
  46. xmlsitemap_node_node_update($node);
  47. }
  48. /**
  49. * Implements hook_node_update().
  50. */
  51. function xmlsitemap_node_node_update(stdClass $node) {
  52. $link = xmlsitemap_node_create_link($node);
  53. xmlsitemap_link_save($link);
  54. }
  55. /**
  56. * Implements hook_node_delete().
  57. */
  58. function xmlsitemap_node_node_delete(stdClass $node) {
  59. xmlsitemap_link_delete('node', $node->nid);
  60. }
  61. /**
  62. * Implements hook_comment_update().
  63. */
  64. function xmlsitemap_node_comment_update(stdClass $comment) {
  65. if ($node = node_load($comment->nid, NULL, TRUE)) {
  66. xmlsitemap_node_node_update($node);
  67. }
  68. }
  69. /**
  70. * Implements hook_comment_publish().
  71. */
  72. function xmlsitemap_node_comment_publish(stdClass $comment) {
  73. xmlsitemap_node_comment_update($comment);
  74. }
  75. /**
  76. * Implements hook_comment_unpublish().
  77. */
  78. function xmlsitemap_node_comment_unpublish(stdClass $comment) {
  79. xmlsitemap_node_comment_update($comment);
  80. }
  81. /**
  82. * Implements hook_comment_delete().
  83. */
  84. function xmlsitemap_node_comment_delete(stdClass $comment) {
  85. xmlsitemap_node_comment_update($comment);
  86. }
  87. /**
  88. * Implements hook_field_extra_fields().
  89. */
  90. function xmlsitemap_node_field_extra_fields() {
  91. $extras = array();
  92. foreach (node_type_get_names() as $type => $name) {
  93. $extras['node'][$type]['form']['xmlsitemap'] = array(
  94. 'label' => t('XML sitemap'),
  95. 'description' => t('XML sitemap module element'),
  96. 'weight' => 30,
  97. );
  98. }
  99. return $extras;
  100. }
  101. /**
  102. * Implements hook_form_FORM_ID_alter().
  103. *
  104. * @see node_type_form()
  105. * @see xmlsitemap_add_link_bundle_settings()
  106. */
  107. function xmlsitemap_node_form_node_type_form_alter(array &$form, array $form_state) {
  108. $node_type = isset($form['#node_type']->type) ? $form['#node_type']->type : '';
  109. module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
  110. xmlsitemap_add_link_bundle_settings($form, $form_state, 'node', $node_type);
  111. }
  112. /**
  113. * Implements hook_form_alter().
  114. *
  115. * Add the XML sitemap individual link options for a node.
  116. *
  117. * @see xmlsitemap_add_form_link_options()
  118. */
  119. function xmlsitemap_node_form_node_form_alter(array &$form, array &$form_state) {
  120. // Add the link options.
  121. module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
  122. xmlsitemap_add_form_link_options($form, 'node', $form['type']['#value'], $form['nid']['#value']);
  123. $form['xmlsitemap']['#weight'] = 30;
  124. }
  125. /**
  126. * Fetch all the timestamps for when a node was changed.
  127. *
  128. * @param $node
  129. * A node object.
  130. * @return
  131. * An array of UNIX timestamp integers.
  132. */
  133. function xmlsitemap_node_get_timestamps(stdClass $node) {
  134. static $timestamps = array();
  135. if (!isset($timestamps[$node->nid])) {
  136. $timestamps[$node->nid] = db_query("SELECT nr.timestamp FROM {node_revision} nr WHERE nr.nid = :nid", array(':nid' => $node->nid))->fetchCol();
  137. if (module_exists('comment')) {
  138. $comment_timestamps = db_query("SELECT c.created FROM {comment} c WHERE c.nid = :nid AND c.status = :status", array(':nid' => $node->nid, ':status' => COMMENT_PUBLISHED))->fetchCol();
  139. $timestamps[$node->nid] = array_merge($timestamps[$node->nid], $comment_timestamps);
  140. }
  141. }
  142. return $timestamps[$node->nid];
  143. }
  144. /**
  145. * Create a sitemap link from a node.
  146. *
  147. * The link will be saved as $node->xmlsitemap.
  148. *
  149. * @param $node
  150. * A node object.
  151. */
  152. function xmlsitemap_node_create_link(stdClass $node) {
  153. if (!isset($node->xmlsitemap) || !is_array($node->xmlsitemap)) {
  154. $node->xmlsitemap = array();
  155. if ($node->nid && $link = xmlsitemap_link_load('node', $node->nid)) {
  156. $node->xmlsitemap = $link;
  157. }
  158. }
  159. $settings = xmlsitemap_link_bundle_load('node', $node->type);
  160. $uri = entity_uri('node', $node);
  161. $node->xmlsitemap += array(
  162. 'type' => 'node',
  163. 'id' => $node->nid,
  164. 'subtype' => $node->type,
  165. 'status' => $settings['status'],
  166. 'status_default' => $settings['status'],
  167. 'status_override' => 0,
  168. 'priority' => $settings['priority'],
  169. 'priority_default' => $settings['priority'],
  170. 'priority_override' => 0,
  171. );
  172. // Always recalculate changefreq and changecount.
  173. $timestamps = xmlsitemap_node_get_timestamps($node);
  174. $node->xmlsitemap['changefreq'] = $node->nid ? xmlsitemap_calculate_changefreq($timestamps) : 0;
  175. $node->xmlsitemap['changecount'] = $node->nid ? count($timestamps) - 1 : 0;
  176. // Node access must be reset since it a user may have changed published status, etc.
  177. //$access = &drupal_static('node_access');
  178. //unset($access[0][$node->nid]);
  179. //node_access_acquire_grants($node);
  180. // The following values must always be checked because they are volatile.
  181. $node->xmlsitemap['loc'] = $uri['path'];
  182. $node->xmlsitemap['lastmod'] = count($timestamps) ? max($timestamps) : 0;
  183. $node->xmlsitemap['access'] = $node->nid ? xmlsitemap_node_view_access($node, drupal_anonymous_user()) : 1;
  184. $node->xmlsitemap['language'] = isset($node->language) ? $node->language : LANGUAGE_NONE;
  185. return $node->xmlsitemap;
  186. }
  187. /**
  188. * Determine whether a user may view the specified node.
  189. *
  190. * @param $node
  191. * The node object on which the operation is to be performed, or node type
  192. * (e.g. 'forum') for "create" operation.
  193. * @param $account
  194. * Optional, a user object representing the user for whom the operation is to
  195. * be performed. Determines access for a user other than the current user.
  196. * @return
  197. * TRUE if the operation may be performed, FALSE otherwise.
  198. *
  199. * This is for all intesive purposes a copy of Drupal 7's node_access() function.
  200. */
  201. function xmlsitemap_node_view_access($node, $account = NULL) {
  202. global $user;
  203. $op = 'view';
  204. $rights = &drupal_static(__FUNCTION__, array());
  205. if (!$node || !in_array($op, array('view', 'update', 'delete', 'create'), TRUE)) {
  206. // If there was no node to check against, or the $op was not one of the
  207. // supported ones, we return access denied.
  208. return FALSE;
  209. }
  210. // If no user object is supplied, the access check is for the current user.
  211. if (empty($account)) {
  212. $account = $user;
  213. }
  214. // $node may be either an object or a node type. Since node types cannot be
  215. // an integer, use either nid or type as the static cache id.
  216. //$cid = is_object($node) ? $node->nid : $node;
  217. // If we've already checked access for this node, user and op, return from
  218. // cache.
  219. if (isset($rights[$account->uid][$node->nid])) {
  220. return $rights[$account->uid][$node->nid];
  221. }
  222. if (user_access('bypass node access', $account)) {
  223. $rights[$account->uid][$node->nid] = TRUE;
  224. return TRUE;
  225. }
  226. if (!user_access('access content', $account)) {
  227. $rights[$account->uid][$node->nid] = FALSE;
  228. return FALSE;
  229. }
  230. // We grant access to the node if both of the following conditions are met:
  231. // - No modules say to deny access.
  232. // - At least one module says to grant access.
  233. // If no module specified either allow or deny, we fall back to the
  234. // node_access table.
  235. $access = module_invoke_all('node_access', $node, $op, $account);
  236. if (in_array(NODE_ACCESS_DENY, $access, TRUE)) {
  237. $rights[$account->uid][$node->nid] = FALSE;
  238. return FALSE;
  239. }
  240. elseif (in_array(NODE_ACCESS_ALLOW, $access, TRUE)) {
  241. $rights[$account->uid][$node->nid] = TRUE;
  242. return TRUE;
  243. }
  244. // Check if authors can view their own unpublished nodes.
  245. if ($op == 'view' && !$node->status && user_access('view own unpublished content', $account) && $account->uid == $node->uid && $account->uid != 0) {
  246. $rights[$account->uid][$node->nid] = TRUE;
  247. return TRUE;
  248. }
  249. // If the module did not override the access rights, use those set in the
  250. // node_access table.
  251. if ($op != 'create' && $node->nid) {
  252. if (module_implements('node_grants')) {
  253. $query = db_select('node_access');
  254. $query->addExpression('1');
  255. $query->condition('grant_' . $op, 1, '>=');
  256. $nids = db_or()->condition('nid', $node->nid);
  257. if ($node->status) {
  258. $nids->condition('nid', 0);
  259. }
  260. $query->condition($nids);
  261. $query->range(0, 1);
  262. // Fetch the node grants and allow other modules to alter them (D7 backport).
  263. $grants = &drupal_static(__FUNCTION__ . ':grants', array());
  264. if (!isset($grants[$account->uid][$op])) {
  265. // Indicate that this is our special function in the grants.
  266. $account->xmlsitemap_node_access = TRUE;
  267. $grants[$account->uid][$op] = node_access_grants($op, $account);
  268. // Remove the special indicator.
  269. unset($account->xmlsitemap_node_access);
  270. }
  271. $grant_condition = db_or();
  272. foreach ($grants[$account->uid][$op] as $realm => $gids) {
  273. foreach ($gids as $gid) {
  274. $grant_condition->condition(db_and()
  275. ->condition('gid', $gid)
  276. ->condition('realm', $realm)
  277. );
  278. }
  279. }
  280. if (count($grant_condition) > 0) {
  281. $query->condition($grant_condition);
  282. }
  283. $result = (bool) $query->execute()->fetchField();
  284. $rights[$account->uid][$node->nid] = $result;
  285. return $result;
  286. }
  287. elseif (is_object($node) && $op == 'view' && $node->status) {
  288. // If no modules implement hook_node_grants(), the default behaviour is to
  289. // allow all users to view published nodes, so reflect that here.
  290. $rights[$account->uid][$node->nid] = TRUE;
  291. return TRUE;
  292. }
  293. }
  294. return FALSE;
  295. }