xmlsitemap.pages.inc 6.42 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
<?php
// $Id$

/**
 * @file
 * Page callbacks for the xmlsitemap module.
 *
 * @ingroup xmlsitemap
 */

/**
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 * Get the sitemap context of the current request.
 */
function xmlsitemap_get_current_context() {
  static $context;

  if (!isset($context)) {
    $context = module_invoke_all('xmlsitemap_context');
    drupal_alter('xmlsitemap_context', $context);
    asort($context);
  }

  return $context;
}

/**
 * Validate the context and use the default context if it fails validation.
28
 *
29
30
 * @todo Merge into xmlsitemap_get_current_context()?
 * @todo Use real default context variable instead of hard-coded default.
31
 */
32
33
34
35
36
37
38
39
function xmlsitemap_context_check(array $context) {
  $hash = md5(serialize($context));

  $default = array();
  if (module_exists('xmlsitemap_i18n')) {
    $default['language'] = language_default('language');
  }
  $default_hash = md5(serialize($default));
40

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  module_load_include('inc', 'xmlsitemap', 'xmlsitemap.generate');
  $valid_contexts = array_keys(xmlsitemap_get_contexts());

  if (in_array($hash, $valid_contexts)) {
    return $context;
  }
  elseif (in_array($default_hash, $valid_contexts)) {
    return $default;
  }
  else {
    trigger_error("Could not find fallback XML sitemap context. Context $hash: " . print_r($context, TRUE) . ". Default context $default_hash: " . print_r($default, TRUE), E_USER_ERROR);
    return array();
  }
}

/**
 * Get the sitemap chunk/page of the current request.
 */
function xmlsitemap_get_current_chunk(array $context) {
60
  // Check if we should be displaing the index.
61
  if (!isset($_GET['page']) || !is_numeric($_GET['page'])) {
62
63
    $index_file = xmlsitemap_get_file_from_context($context, 'index');
    if (file_exists($index_file)) {
64
      return 'index';
65
66
    }
    else {
67
      return 1;
68
69
70
    }
  }
  else {
71
    return (int) $_GET['page'];
72
  }
73
}
74

75
76
77
78
79
80
81
82
83
84
85
86
/**
 * Output a sitemap page.
 *
 * @see xmlsitemap_get_current_context()
 * @see xmlsitemap_get_current_chunk()
 * @see xmlsitemap_get_file_from_context()
 * @see xmlsitemap_output_file()
 */
function xmlsitemap_output_chunk() {
  $original_context = xmlsitemap_get_current_context();
  $context = xmlsitemap_context_check($original_context);
  $chunk = xmlsitemap_get_current_chunk($context);
87
  $file = xmlsitemap_get_file_from_context($context, $chunk);
88

89
  // Provide debugging information if enabled.
90
91
92
  if (variable_get('xmlsitemap_developer_mode', 0) && isset($_GET['debug'])) {
    $output = array();
    $output[] = "Chunk: $chunk";
93
    $output[] = "Original context: " . print_r($original_context, TRUE);
94
95
96
97
98
99
    $output[] = "Context: " . print_r($context, TRUE);
    $output[] = "Cache file location: $file";
    $output[] = "Cache file exists: " . (file_exists($file) ? 'Yes' : 'No');
    return implode('<br />', $output);
  }

100
  return xmlsitemap_output_file($file);
101
102
103
104
105
106
107
}

/**
 * Output the contents of a file to the browser and check caching headers.
 */
function xmlsitemap_output_file($file, array $headers = array()) {
  if (!file_exists($file) || !is_readable($file)) {
108
    return MENU_NOT_FOUND;
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  }

  $mtime = filemtime($file);
  $last_modified = gmdate(DATE_RFC1123, $mtime);
  $etag = '"' . md5($last_modified) . '"';

  // See if the client has provided the required HTTP headers.
  $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
  $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;
  if ($if_modified_since && $if_none_match && $if_none_match == $etag && $if_modified_since == $last_modified) {
    header('HTTP/1.1 304 Not Modified');
    // All 304 responses must send an etag if the 200 response for the same object contained an etag
    header('Etag: ' . $etag);
    exit;
  }

  $headers += array(
126
127
128
129
130
131
132
    'Content-type' => 'text/xml; charset=utf-8',
    //'Content-length' => filesize($file),
    'Last-modified' => $last_modified,
    'Etag' => $etag,
    'Expires' => gmdate(DATE_RFC1123, $mtime + variable_get('xmlsitemap_minimum_lifetime', 0)),
    'Cache-Control' => 'must-revalidate',
    'X-Robots-Tag' => 'noindex, follow',
133
134
135
136
137
138
139
140
141
142
143
  );

  // Transfer the file as output.
  xmlsitemap_file_transfer($file, $headers);
}

/**
 * Modified version of file_transfer() that invokes hook_exit()s afterwards.
 *
 * @see file_transfer()
 */
144
function xmlsitemap_file_transfer($uri, $headers) {
145
146
147
148
  if (ob_get_level()) {
    ob_end_clean();
  }

149
150
  foreach ($headers as $name => $value) {
    drupal_add_http_header($name, $value);
151
  }
152
  drupal_send_headers();
153

154
  $scheme = $scheme = variable_get('file_default_scheme', 'public');
155
  // Transfer file in 16 KB chunks to save memory usage.
156
157
  if ($scheme && file_stream_wrapper_valid_scheme($scheme) && $fd = fopen($uri, 'rb')) {
    while (!feof($fd)) {
158
      print fread($fd, 1024*16);
159
    }
160
    fclose($fd);
161
162
163
164
  }
  else {
    drupal_not_found();
  }
165
  drupal_exit();
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
}

/**
 * Output an XML transformation file for the sitemap XML.
 */
function xmlsitemap_output_xsl() {
  // Read the XSL content from the file.
  $module_path = drupal_get_path('module', 'xmlsitemap');
  $xsl_content = file_get_contents($module_path . '/xsl/xmlsitemap.xsl');

  // Make sure the strings in the XSL content are translated properly.
  $replacements = array(
    'Sitemap file' => t('Sitemap file'),
    'Generated by the <a href="http://drupal.org/project/xmlsitemap">Drupal XML sitemap module</a>.' => t('Generated by the <a href="@link-xmlsitemap">Drupal XML sitemap module</a>.', array('@link-xmlsitemap' => 'http://drupal.org/project/xmlsitemap')),
    'Number of sitemaps in this index' => t('Number of sitemaps in this index'),
    'Click on the table headers to change sorting.' => t('Click on the table headers to change sorting.'),
    'Sitemap URL' => t('Sitemap URL'),
    'Last modification date' => t('Last modification date'),
    'Number of URLs in this sitemap' => t('Number of URLs in this sitemap'),
    'URL location' => t('URL location'),
    'Change frequency' => t('Change frequency'),
    'Priority' => t('Priority'),
188
189
    '[jquery]' => base_path() . 'misc/jquery.js',
    '[jquery-tablesort]' => base_path() . $module_path . '/xsl/jquery.tablesorter.min.js',
190
    '[xsl-js]' => base_path() . $module_path . '/xsl/xmlsitemap.xsl.js',
191
    '[xsl-css]' => base_path() . $module_path . '/xsl/xmlsitemap.xsl.css',
192
193
194
195
  );
  $xsl_content = strtr($xsl_content, $replacements);

  // Output the XSL content.
196
197
  drupal_add_http_header('Content-type', 'application/xml; charset=utf-8');
  drupal_add_http_header('X-Robots-Tag', 'noindex, follow');
198
  print $xsl_content;
199
}