You are here

xmlsitemap_node.module in XML sitemap 5.2

Adds nodes to the sitemap.

File

xmlsitemap_node/xmlsitemap_node.module
View source
<?php

/**
 * @file
 * Adds nodes to the sitemap.
 */

/**
 * @addtogroup xmlsitemap
 * @{
 */

/*****************************************************************************
 * Drupal hooks.
 ****************************************************************************/

/**
 * Implementation of hook_comment().
 */
function xmlsitemap_node_comment($comment, $op) {
  $comment = (object) $comment;
  switch ($op) {
    case 'insert':
    case 'update':
    case 'moderate':
    case 'delete':
      db_query("UPDATE {xmlsitemap_node} SET previous_comment = last_comment, last_comment = %d WHERE nid = %d", $comment->timestamp, $comment->nid);
      if (variable_get('xmlsitemap_node_count_comments', TRUE)) {
        xmlsitemap_flag_sitemap();
      }
      break;
  }
}

/**
 * Implementation of hook_form_alter().
 */
function xmlsitemap_node_form_alter($form_id, &$form) {
  switch ($form_id) {
    case 'node_type_form':
      if (isset($form['identity']['type'])) {
        $form['xmlsitemap_node'] = array(
          '#type' => 'fieldset',
          '#title' => t('XML sitemap'),
          '#collapsible' => TRUE,
        );
        $form['xmlsitemap_node']['xmlsitemap_node_type_priority'] = array(
          '#type' => 'select',
          '#title' => t('Priority adjustment'),
          '#description' => t('This number will be added to the priority of this content type.'),
          '#default_value' => variable_get('xmlsitemap_node_type_priority_' . $form['#node_type']->type, 0.5),
          '#options' => xmlsitemap_priority_options('exclude'),
        );
        $form['xmlsitemap_node']['xmlsitemap_old_priority'] = array(
          '#type' => 'value',
          '#value' => variable_get('xmlsitemap_node_type_priority_' . $form['#node_type']->type, 0.5),
        );
        $form['#submit']['_xmlsitemap_node_submit'] = array();
      }
      break;
    case 'xmlsitemap_settings_sitemap':
      $options = xmlsitemap_priority_options();
      $form['general']['priority']['xmlsitemap_node_promote_priority'] = array(
        '#type' => 'select',
        '#title' => t('Promotion adjustment'),
        '#description' => t('This number will be added to the priority of each post that is promoted to the front page.'),
        '#default_value' => variable_get('xmlsitemap_node_promote_priority', 0.3),
        '#options' => $options,
      );
      $form['general']['priority']['xmlsitemap_node_comment_priority'] = array(
        '#type' => 'select',
        '#title' => t('Comment ratio weight'),
        '#description' => t("This number will be added to the priority of the post with the highest number of comments. This doesn't apply if the maximum number of comments is one."),
        '#default_value' => variable_get('xmlsitemap_node_comment_priority', 0.2),
        '#options' => $options,
      );
      $form['xmlsitemap_node'] = array(
        '#type' => 'fieldset',
        '#title' => t('Content settings'),
        '#description' => t('The settings for the content to include in the sitemap.'),
        '#collapsible' => TRUE,
      );
      $form['xmlsitemap_node']['xmlsitemap_node_count_comments'] = array(
        '#type' => 'checkbox',
        '#title' => t('Count comments in change date and frequency'),
        '#description' => t('If enabled, the frequency of comments on a post will affect its change frequency and last modification date.'),
        '#default_value' => variable_get('xmlsitemap_node_count_comments', TRUE),
      );
      $form['buttons']['#weight'] = 1;
      break;
    default:
      if (isset($form['type']) && $form_id == $form['type']['#value'] . '_node_form') {
        $node = $form['#node'];
        if (user_access('override node settings')) {
          $form['xmlsitemap_node'] = array(
            '#type' => 'fieldset',
            '#title' => t('XML sitemap'),
            '#collapsible' => TRUE,
            '#weight' => 30,
          );
          $options = xmlsitemap_priority_options('both');
          $default = variable_get('xmlsitemap_node_type_priority_' . $node->type, '0.5');
          $form['xmlsitemap_node']['priority_override'] = array(
            '#type' => 'select',
            '#title' => t('Priority'),
            '#description' => t('The default priority is %priority.', array(
              '%priority' => $options[$default],
            )),
            '#default_value' => isset($node->priority_override) ? $node->priority_override : -2,
            '#options' => $options,
          );
        }
        else {
          $form['priority_override'] = array(
            '#type' => 'value',
            '#value' => $node->priority_override,
          );
        }
        $form['xmlsitemap_node_status'] = array(
          '#type' => 'value',
          '#value' => $node->status,
        );
      }
      break;
  }
}

/**
 * Implementation of hook_node_type().
 */
function xmlsitemap_node_node_type($op, $info) {
  if ($op == 'delete') {
    variable_del("xmlsitemap_node_type_priority_{$info->type}");
  }
  elseif ($op == 'update' && !empty($info->old_type) && $info->type != $info->old_type) {
    variable_set('xmlsitemap_node_type_priority_' . $info->type, variable_get('xmlsitemap_node_type_priority_' . $info->old_type, 0.5));
    variable_del('xmlsitemap_node_type_priority_' . $info->old_type);
  }
  xmlsitemap_flag_sitemap();
}

/**
 * Implementation of hook_nodeapi().
 */
function xmlsitemap_node_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
  switch ($op) {
    case 'prepare':
      $priority_override = db_result(db_query("SELECT priority_override\n        FROM {xmlsitemap_node}\n        WHERE nid = %d", $node->nid));
      $node->priority_override = $priority_override === FALSE ? 'NULL' : $priority_override;
      break;
    case 'insert':
      $node->priority_override = isset($node->priority_override) ? $node->priority_override : 'NULL';
      db_query("INSERT INTO {xmlsitemap_node} (nid, last_changed, priority_override) VALUES (%d, %d, %s)", $node->nid, $node->changed, $node->priority_override);
      if ($node->status) {
        xmlsitemap_flag_sitemap();
      }
      break;
    case 'update':
      if (!isset($node->priority_override)) {
        $priority_override = db_result(db_query("SELECT priority_override FROM {xmlsitemap_node} WHERE nid = %d", $node->nid));
        $node->priority_override = $priority_override !== FALSE ? $priority_override : 'NULL';
      }
      db_query("UPDATE {xmlsitemap_node}\n        SET previously_changed = last_changed, last_changed = %d, priority_override = %s\n        WHERE nid = %d", $node->changed, $node->priority_override, $node->nid);
      xmlsitemap_flag_sitemap();
      break;
    case 'delete':
      db_query("DELETE FROM {xmlsitemap_node} WHERE nid = %d", $node->nid);
      if ($node->status) {
        xmlsitemap_flag_sitemap();
      }
      break;
  }
}

/**
 * Implementation of hook_views_query_alter().
 */
function xmlsitemap_node_views_query_alter(&$query, &$view, $summary = NULL, $level = NULL) {
  switch ($view->page_type) {
    case 'xmlsitemap_news':
      $query
        ->add_field('created', 'node');
    case 'xmlsitemap_sitemap':
      $xn_join = array(
        'left' => array(
          'table' => 'node',
          'field' => 'nid',
        ),
        'right' => array(
          'field' => 'nid',
        ),
      );
      $ua_join = array(
        'left' => array(
          'table' => 'url_alias',
          'field' => 'pid',
        ),
        'right' => array(
          'field' => 'pid',
        ),
        'extra' => array(
          "src = CONCAT('node/', node.nid)" => NULL,
        ),
      );
      $query
        ->add_table('xmlsitemap_node', FALSE, 1, $xn_join);
      $query
        ->add_table('url_alias', FALSE, 1, $ua_join);
      if (module_exists('comment')) {
        $query
          ->add_table('node_comment_statistics');
        $query
          ->add_field('comment_count', 'node_comment_statistics');
        $query
          ->add_field('last_comment_timestamp', 'node_comment_statistics');
        $query
          ->add_field('previous_comment', 'xmlsitemap_node');
      }
      $query
        ->add_field('type', 'node');
      $query
        ->add_field('promote', 'node');
      $query
        ->add_field('changed', 'node');
      $query
        ->add_field('previously_changed', 'xmlsitemap_node');
      $query
        ->add_field('priority_override', 'xmlsitemap_node');
      $query
        ->add_field('dst', 'url_alias', 'alias');
      break;
  }
}

/**
 * Implementation of hook_views_style_plugins().
 */
function xmlsitemap_node_views_style_plugins() {
  return array(
    'xmlsitemap_sitemap' => array(
      'name' => t('XML sitemap: Sitemap'),
      'theme' => 'xmlsitemap_node_view_sitemap',
    ),
    'xmlsitemap_news' => array(
      'name' => t('XML sitemap: News'),
      'theme' => 'xmlsitemap_node_view_news',
    ),
  );
}

/**
 * Implementation of hook_xmlsitemap_description().
 */
function xmlsitemap_node_xmlsitemap_description() {
  return '<dt>' . t('XML sitemap node') . '</dt>' . '<dd>' . t('It adds nodes to the sitemap. The default priority of a node is determined by a combination of its <a href="@content">content type</a> priority, whether it appears on the front page of your site, and the number of comments it has received. You can override the default priority for individual nodes when you add or edit a node.', array(
    '@content' => url('admin/content/types'),
  )) . '</dd>';
}

/**
 * Implementation of hook_xmlsitemap_links().
 */
function xmlsitemap_node_xmlsitemap_links() {
  $excludes = xmlsitemap_node_excludes();
  switch ($GLOBALS['db_type']) {
    case 'mysql':
    case 'mysqli':
      $coalesce = 'COALESCE';
      break;
    case 'pgsql':
      $coalesce = 'FIRST';
      break;
  }
  if (module_exists('comment')) {
    $columns = 'n.nid, n.type, n.promote, n.changed, xn.previously_changed, xn.priority_override, s.comment_count, s.last_comment_timestamp, xn.previous_comment';
    $left_join = "LEFT JOIN {node_comment_statistics} s ON n.nid = s.nid";
  }
  else {
    $columns = 'n.nid, n.type, n.promote, n.changed, xn.previously_changed, xn.priority_override';
    $left_join = '';
  }
  $query = "SELECT {$columns}, {$coalesce}(ua.dst) AS alias FROM {node} n\n    LEFT JOIN {xmlsitemap_node} xn ON n.nid = xn.nid\n    {$left_join}\n    LEFT JOIN {url_alias} ua ON ua.src = CONCAT('node/', n.nid)\n    WHERE n.status > 0\n      AND n.type NOT IN (" . xmlsitemap_placeholders($excludes, 'varchar') . ")\n      AND (xn.priority_override IS NULL OR xn.priority_override = -2 OR xn.priority_override >= 0)\n      AND n.nid <> %d\n    GROUP BY {$columns}";
  $nid = 0;
  $frontpage = explode('/', drupal_get_normal_path(variable_get('site_frontpage', 'node')));
  if (count($frontpage) == 2 && $frontpage[0] == 'node' && is_numeric($frontpage[1])) {
    $nid = $frontpage[1];
  }
  $query_args = array_merge($excludes, array(
    $nid,
  ));
  $result = db_query(db_rewrite_sql($query), $query_args);
  $count_comments = variable_get('xmlsitemap_node_count_comments', TRUE) && module_exists('comment');
  while ($node = db_fetch_object($result)) {
    db_query("INSERT INTO {xmlsitemap} (loc, lastmod, changefreq, priority) VALUES ('%s', %d, %d, %f)", xmlsitemap_url('node/' . $node->nid, $node->alias, NULL, NULL, TRUE), $count_comments ? max($node->changed, $node->last_comment_timestamp) : $node->changed, xmlsitemap_node_frequency($node), xmlsitemap_node_priority($node));
  }
}

/*****************************************************************************
 * Menu callbacks / form builders, submit/validate functions.
 ****************************************************************************/

/**
 * Add submit actions to forms.
 * @return None
 */
function _xmlsitemap_node_submit($form_id, $form_values) {
  $op = isset($form_values['op']) ? $form_values['op'] : '';
  $priority = $form_values['xmlsitemap_node_type_priority'];
  $old_priority = $form_values['xmlsitemap_old_priority'];
  if ($op == t('Save content type') && $priority != $old_priority || $op == t('Reset to defaults') && $old_priority != 0.1) {
    xmlsitemap_flag_sitemap();
  }
}

/*****************************************************************************
 * Theme functions.
 ****************************************************************************/

/**
 * Display the nodes of a view as a Google News sitemap.
 */
function theme_xmlsitemap_node_view_news($view, $nodes, $type) {
  drupal_set_header('Content-Type: text/xml; charset=utf-8');
  print '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
  print '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"' . "\n";
  print '  xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"' . "\n";
  print '  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' . "\n";
  print '  xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9' . "\n";
  print '  http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">' . "\n";
  foreach ($nodes as $node) {
    $lastmod = variable_get('xmlsitemap_node_count_comments', TRUE) ? max($node->changed, $node->last_comment_timestamp) : $node->changed;
    print '  <url>' . "\n";
    print '    <loc>' . xmlsitemap_url('node/' . $node->nid, $node->alias, NULL, NULL, TRUE) . '</loc>' . "\n";
    print '    <lastmod>' . gmdate('Y-m-d\\TH:i:s+00:00', $lastmod) . '</lastmod>' . "\n";
    print '    <changefreq>' . xmlsitemap_sitemap_frequency(xmlsitemap_node_frequency($node)) . '</changefreq>' . "\n";
    print '    <news:news>' . "\n";
    print '      <news:publication_date>' . gmdate('Y-m-d\\TH:i:s+00:00', $node->created) . '</news:publication_date>' . "\n";
    print '    </news:news>' . "\n";
    print '  </url>' . "\n";
  }
  print '</urlset>';
  drupal_page_footer();
  exit;
}

/**
 * Display the nodes of a view as an XML sitemap.
 */
function theme_xmlsitemap_node_view_sitemap($view, $nodes, $type) {
  drupal_set_header('Content-Type: text/xml; charset=utf-8');
  print '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
  print '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"' . "\n";
  print '  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' . "\n";
  print '  xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9' . "\n";
  print '  http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">' . "\n";
  $count_comments = variable_get('xmlsitemap_node_count_comments', TRUE) && module_exists('comment');
  foreach ($nodes as $node) {
    if ($count_comments) {
      $lastmod = max($node->changed, $node->last_comment_timestamp);
    }
    else {
      $lastmod = $node->changed;
    }
    print '  <url>' . "\n";
    print '    <loc>' . xmlsitemap_url('node/' . $node->nid, $node->alias, NULL, NULL, TRUE) . '</loc>' . "\n";
    print '    <lastmod>' . gmdate('Y-m-d\\TH:i:s+00:00', $lastmod) . '</lastmod>' . "\n";
    print '    <changefreq>' . xmlsitemap_sitemap_frequency(xmlsitemap_node_frequency($node)) . '</changefreq>' . "\n";
    print '    <priority>' . number_format(xmlsitemap_node_priority($node), 1) . '</priority>' . "\n";
    print '  </url>' . "\n";
  }
  print '</urlset>';
  drupal_page_footer();
  exit;
}

/*****************************************************************************
 * Public functions.
 ****************************************************************************/

/**
 * Get array of excluded types.
 * @return An array of node types to exclude.
 */
function xmlsitemap_node_excludes() {
  static $excludes;
  if (!isset($excludes)) {
    $excludes = array();
    foreach (node_get_types() as $type => $name) {
      if (variable_get("xmlsitemap_node_type_priority_{$type}", 0.5) < 0) {
        $excludes[] = $type;
      }
    }
    if (empty($excludes)) {
      $excludes = array(
        0,
      );
    }
  }
  return $excludes;
}

/**
 * Calculate the change frequency of a node.
 * @param $node: Data for a node retrieved by _xmlsitemap_node_links().
 * @return Number of seconds between updates
 */
function xmlsitemap_node_frequency($node) {
  $age = REQUEST_TIME - $node->changed;
  if (variable_get('xmlsitemap_node_count_comments', TRUE) && module_exists('comment')) {
    $age = REQUEST_TIME - max($node->changed, $node->last_comment_timestamp);
    $interval = 0;
    if (!empty($node->previously_changed) && isset($node->previous_comment)) {
      $interval = min($node->changed, $node->last_comment_timestamp) - max($node->previously_changed, $node->previous_comment);
    }
    elseif (!empty($node->previously_changed)) {
      $interval = min($node->changed, $node->last_comment_timestamp) - $node->previously_changed;
    }
    elseif (isset($node->previous_comment)) {
      $interval = min($node->changed, $node->last_comment_timestamp) - $node->previous_comment;
    }
  }
  else {
    $interval = empty($node->previously_changed) ? 0 : $node->changed - $node->previously_changed;
  }
  return max($age, $interval);
}

/**
 * Get the nid of the front page node.
 * @return Integer of front page node ID, or 0 if front page is not a node.
 */
function _xmlsitemap_node_frontpage() {
  $nid = 0;
  $frontpage = explode('/', drupal_get_normal_path(variable_get('site_frontpage', 'node')));
  if (count($frontpage) == 2 && $frontpage[0] == 'node' && is_numeric($frontpage[1])) {
    $nid = $frontpage[1];
  }
  return $nid;
}

/**
 * Calculate the priority of a node.
 * @param $node: A node object
 * @return A number between 0 and 1, or -1
 */
function xmlsitemap_node_priority($node) {
  static $maxcomments;
  if (!isset($maxcomments)) {
    $maxcomments = 0;
    if (module_exists('comment')) {
      $maxcomments = db_result(db_query("SELECT MAX(comment_count) FROM {node_comment_statistics}"));
    }
  }
  if (!isset($node->priority_override) || $node->priority_override == -2.0) {
    $priority = 0;
    $priority += variable_get("xmlsitemap_node_type_priority_{$node->type}", 0.5);
    if ($node->promote) {
      $priority += variable_get('xmlsitemap_node_promote_priority', 0.3);
    }
    if (!empty($maxcomments)) {
      $priority += $node->comment_count / $maxcomments * variable_get('xmlsitemap_node_comment_priority', 0.5);
    }
    $priority = round($priority, 1);
    $priority = min($priority, 1);
  }
  else {
    $priority = $node->priority_override;
  }
  return $priority;
}

/**
 * @} End of "addtogroup xmlsitemap".
 */

Functions

Namesort descending Description
theme_xmlsitemap_node_view_news Display the nodes of a view as a Google News sitemap.
theme_xmlsitemap_node_view_sitemap Display the nodes of a view as an XML sitemap.
xmlsitemap_node_comment Implementation of hook_comment().
xmlsitemap_node_excludes Get array of excluded types.
xmlsitemap_node_form_alter Implementation of hook_form_alter().
xmlsitemap_node_frequency Calculate the change frequency of a node.
xmlsitemap_node_nodeapi Implementation of hook_nodeapi().
xmlsitemap_node_node_type Implementation of hook_node_type().
xmlsitemap_node_priority Calculate the priority of a node.
xmlsitemap_node_views_query_alter Implementation of hook_views_query_alter().
xmlsitemap_node_views_style_plugins Implementation of hook_views_style_plugins().
xmlsitemap_node_xmlsitemap_description Implementation of hook_xmlsitemap_description().
xmlsitemap_node_xmlsitemap_links Implementation of hook_xmlsitemap_links().
_xmlsitemap_node_frontpage Get the nid of the front page node.
_xmlsitemap_node_submit Add submit actions to forms.