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

Contents of /contributions/modules/recent_changes/recent_changes.module

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


Revision 1.5 - (show annotations) (download) (as text)
Wed Apr 4 23:54:17 2007 UTC (2 years, 7 months ago) by roetzi
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--1
Changes since 1.4: +94 -26 lines
File MIME type: text/x-php
Added node access check (#133676 by Dio). Added MySQL version check (#132854). Added RSS feed per node type.
1 <?php
2 // $Id: recent_changes.module,v 1.4 2007/03/07 03:20:03 roetzi Exp $
3
4 /**
5 * @file
6 * List of recent changes over all node revisions and comments
7 */
8
9 /**
10 * Implementation of hook_help().
11 */
12 function recent_changes_help($section) {
13 switch ($section) {
14 case 'admin/help#recent_changes':
15 $output = '<p>'.t('The recent changes module offers a page which shows all recent changes on nodes. The page can be viewed by anyone with the <em>view revisions</em> permission. If you have the <em>diff</em> module installed, a link to the diff-view will be offered in the table.').'</p>';
16 return $output;
17 break;
18 }
19 }
20
21 /**
22 * Implementation of hook_menu().
23 */
24 function recent_changes_menu($may_cache) {
25 $items = array();
26 if ($may_cache) {
27 $items[] = array(
28 'title' => t('Recent changes'),
29 'path' => 'recent_changes',
30 'callback' => 'recent_changes_view',
31 'access' => user_access('view revisions'),
32 'type' => MENU_NORMAL_ITEM
33 );
34 }
35 return $items;
36 }
37
38 /**
39 * Menu callback which displays a list of recent changes.
40 */
41 function recent_changes_view() {
42 // Print feed if requested or display normal page otherwise
43 if (arg(1) == 'feed') {
44 return recent_changes_feed(arg(2));
45 }
46 else {
47 return recent_changes_page();
48 }
49 }
50
51 /**
52 * Page display of recent changes.
53 */
54 function recent_changes_page() {
55 $output = '';
56
57 // Add stylesheet for page.
58 drupal_add_css(drupal_get_path('module', 'recent_changes').'/recent_changes.css');
59
60 // Show filter if enabled
61 if (recent_changes_show_filter()) {
62 $output .= drupal_get_form('recent_changes_filter_form');
63 }
64
65 if ($_REQUEST['op'] == t('Filter')) {
66 // Use submitted values if filter form was used.
67 $show_comments = check_plain($_REQUEST['recent_changes_show_comments']);
68 $node_type = check_plain($_REQUEST['recent_changes_show_node_type']);
69 }
70 else {
71 // Default values
72 $show_comments = recent_changes_show_comments();
73 $node_type = '';
74 }
75
76 // Add feed
77 if ($node_type) {
78 // Filter by node type is active
79 drupal_add_feed(url('recent_changes/feed/' . $node_type, null, null, true), t('recent changes of !site filtered by node type !node_type', array('!site' => variable_get('site_name', 'drupal'), '!node_type' => $node_type)));
80 }
81 else {
82 // No filtering
83 drupal_add_feed(url('recent_changes/feed', null, null, true), t('recent changes of !site', array('!site' => variable_get('site_name', 'drupal'))));
84 }
85
86 // Header for recent changes table.
87 $header = array(
88 '', // operations
89 '', // time
90 t('Type'),
91 t('Title'),
92 t('User'),
93 t('Log'),
94 );
95 $rows = array();
96
97 // Build SQL query depending on node type filter and comments filter.
98
99 // Query to get all revisions of the selected node types
100 $revisions_query = recent_changes_revisions_query($node_type);
101 if (module_exists('comment') && $show_comments) {
102 // Query to get all comments of the selected node types
103 $comments_query = recent_changes_comments_query($node_type);
104 // Final query as a combination of node revisions and comments
105 $sql = "($revisions_query) UNION ALL ($comments_query) ORDER BY timestamp DESC";
106 if ($node_type) {
107 // Number of total nodes and comments if a specific node type is selected
108 $count_sql = "SELECT (SELECT COUNT(*) FROM {node_revisions} r JOIN {node} n ON r.nid = n.nid WHERE n.type = '" . check_plain($node_type) . "') + (SELECT COUNT(*) FROM {comments} c LEFT JOIN {node} n ON c.nid = n.nid WHERE n.type = '" . check_plain($node_type) . "')";
109 }
110 else {
111 // Number of total nodes and comments if all node types are shown
112 $count_sql = "SELECT (SELECT COUNT(*) FROM {node_revisions}) + (SELECT COUNT(*) FROM {comments})";
113 }
114 // Check MySQL version
115 $version = db_version();
116 if ($GLOBALS['db_type'] == 'mysql' && version_compare($version, '4.1.0') < 0) {
117 // MySQL before 4.1.0 cannot execute the subqueries. Just ignore comments for total count.
118 if ($node_type) {
119 // Number of total node revisions if a specific node type is selected
120 $count_sql = "SELECT COUNT(*) FROM {node_revisions} r JOIN {node} n ON r.nid = n.nid WHERE n.type = '" . check_plain($node_type) . "'";
121 }
122 else {
123 // Number of total node revisions if all node types are shown
124 $count_sql = "SELECT COUNT(*) FROM {node_revisions}";
125 }
126 }
127 }
128 else {
129 // Query to select all node revisions
130 $sql = "$revisions_query ORDER BY timestamp DESC";
131 if ($node_type) {
132 // Number of total node revisions if a specific node type is selected
133 $count_sql = "SELECT COUNT(*) FROM {node_revisions} r JOIN {node} n ON r.nid = n.nid WHERE n.type = '" . check_plain($node_type) . "'";
134 }
135 else {
136 // Number of total node revisions if all node types are shown
137 $count_sql = "SELECT COUNT(*) FROM {node_revisions}";
138 }
139 }
140
141 // SQL result provided by pager implementation
142 $result = pager_query($sql, recent_changes_count_per_page(), 0, $count_sql);
143
144 $day = -1;
145 $has_revisions = array();
146 // List all results
147 while ($change = db_fetch_object($result)) {
148 // Check if day of week changed and if yes output the new day.
149 $current_day = format_date($change->timestamp, 'custom', 'z');
150 if ($day != $current_day) {
151 $day = $current_day;
152 $rows[] = array(
153 array(
154 'data' => format_date($change->timestamp, 'custom', 'D, j F Y'),
155 'colspan' => '6',
156 'class' => 'date'
157 )
158 );
159 }
160 // Comments have a non-zero 'cid', node revisions a non-zero 'vid'. Both have a 'nid'
161 $is_comment = ($change->cid != 0);
162 if ($is_comment) {
163 // Format the row if the change is a comment
164 $rows[] = array(
165 // Link to comment
166 l('(comment)', "node/$change->nid", null, null, "comment-$change->cid"),
167 // Time of submit
168 format_date($change->timestamp, 'custom', 'H:i'),
169 // Node type of comment's node
170 $change->type,
171 // Title of comment's node
172 l($change->title, "node/$change->nid"),
173 // User which submitted comment
174 theme('username', $change),
175 // Title of comment
176 filter_xss($change->log),
177 );
178 } else {
179 // Format the row if the change is a node revision
180
181 // Check if that node has any revisions at all and cache the result.
182 if (!isset($has_revisions[$change->nid])) {
183 $has_revisions[$change->nid] = db_result(db_query('SELECT COUNT(*) FROM {node_revisions} WHERE nid=%d', $change->nid)) > 1;
184 }
185
186 // Get the version and title of the previous revision
187 $old_node = db_fetch_object(db_query('SELECT vid, title FROM {node_revisions} WHERE nid=%d AND vid<%d ORDER BY vid DESC LIMIT 1', $change->nid, $change->vid));
188
189 $operations = '';
190 // Add a link to the diff between the current version and the one before that.
191 if (module_exists('diff')) {
192 if ($old_node->vid) {
193 $operations .= l('(diff)', "node/$change->nid/revisions/view/$old_node->vid/$change->vid").'&nbsp;';
194 }
195 else {
196 $operations = '(diff)&nbsp;';
197 }
198 }
199 // Add a link to the revisions history of the node.
200 if ($has_revisions[$change->nid]) {
201 $operations .= l('(hist)', "node/$change->nid/revisions");
202 }
203 else {
204 $operations .= '(hist)';
205 }
206
207 $special = '';
208 if ($old_node->vid == 0) {
209 // The node is new.
210 $special = '<span class="change-flag">new</span> ';
211 }
212 elseif ($old_node->title != $change->title) {
213 // The node title changed.
214 $special = '<span class="change-flag">moved</span> ';
215 }
216 $rows[] = array(
217 // Link to diff and history
218 $operations,
219 // Submit time
220 format_date($change->timestamp, 'custom', 'H:i'),
221 // Node type
222 $change->type,
223 // Title of node
224 $special . l($change->title, "node/$change->nid"),
225 // User which submitted node
226 theme('username', $change),
227 // Log message of revision
228 filter_xss($change->log),
229 );
230 }
231 }
232
233 // Print table and pager
234 $output .= theme('table', $header, $rows, array('class' => 'recent-changes'));
235 $output .= theme('pager', NULL, recent_changes_count_per_page, 0);
236
237 return $output;
238 }
239
240 /**
241 * Form for input filter.
242 */
243 function recent_changes_filter_form() {
244 $form = array();
245 $form['#attributes'] = array('class' => 'inline');
246 // Enable/Disable comments in recent changes view
247 if (module_exists('comment')) {
248 $form['recent_changes_show_comments'] = array(
249 '#type' => 'checkbox',
250 '#title' => t('Show comments'),
251 '#default_value' => isset($_REQUEST['form_id']) ? isset($_REQUEST['recent_changes_show_comments']) : recent_changes_show_comments(),
252 );
253 }
254 // Filter node type of recent changes view
255 $node_types = array('' => 'all node types');
256 $node_types = array_merge($node_types, node_get_types('names'));
257 $form['recent_changes_show_node_type'] = array(
258 '#type' => 'select',
259 '#default_value' => isset($_REQUEST['recent_changes_show_node_type']) ? $_REQUEST['recent_changes_show_node_type'] : '',
260 '#options' => $node_types,
261 );
262 $form['submit'] = array(
263 '#type' => 'button',
264 '#value' => t('Filter')
265 );
266 return $form;
267 }
268
269 /**
270 * RSS feed of recent changes.
271 */
272 function recent_changes_feed($node_type = NULL) {
273 global $base_url;
274
275 // Query to get all revisions of the selected node types
276 $revisions_query = recent_changes_revisions_query($node_type);
277 if (module_exists('comment')) {
278 // Query to get all comments of the selected node types
279 $comments_query = recent_changes_comments_query($node_type);
280 // Final query as a combination of node revisions and comments
281 $sql = "($revisions_query) UNION ALL ($comments_query) ORDER BY timestamp DESC LIMIT %d";
282 }
283 else {
284 $sql = "$revisions_query ORDER BY timestamp DESC LIMIT %d";
285 }
286 $result = db_query($sql, variable_get('feed_default_items', 10));
287
288 // Type of RSS feed: 'title', 'teaser', 'full'
289 $item_length = variable_get('feed_item_length', 'teaser');
290 // Is node teaser displayed (only used if $item_length not 'title')
291 $teaser = ($item_length == 'teaser') ? true : false;
292 // RSS namespace used
293 $namespaces = array('xmlns:dc="http://purl.org/dc/elements/1.1/"');
294 // RSS text
295 $items = '';
296 while ($item = db_fetch_object($result)) {
297 // Comments have a non-zero 'cid', node revisions a non-zero 'vid'. Both have a 'nid'
298 $is_comment = ($item->cid != 0);
299 if ($is_comment) {
300 // Format comment for RSS: Comment title as item title, comment body as text
301 $comment = db_fetch_object(db_query('SELECT * FROM {comments} WHERE cid = %d', $item->cid));
302
303 // RSS Title: title of comment with comment mark
304 $title = t('Comment') . ': ' . $comment->subject;
305 // RSS Link: Direct link to comment
306 $link = url("node/$comment->nid", null, "comment-$comment->cid", 1);
307 // Node title comment belongs to (used in feed body)
308 $node_title = l($item->title,"node/$item->nid", null, null, null, 1);
309 // User which submitted comment (used in feed body)
310 $username = ($comment->uid ? l($comment->name, "user/$comment->uid", null, null, null, 1) : $comment->name);
311 // RSS body: has meta info about comment
312 $item_text = '<p><em>' . t('Comment on !node_title from !user', array('!node_title' => $node_title, '!user' => $username)) . '</em></p><hr/>';
313 if ($item_length != 'title') {
314 // If not just titles are in RSS feeds, put the comment in RSS body
315 $item_text .= check_markup($comment->comment, $comment->format);
316 }
317 // No extras here (like attachments or taxonomy)
318 $extra = array();
319 }
320 else {
321 // Format comment for RSS
322 $node = node_load($item->nid, $item->vid);
323 // Select the older revision if available. Set flag accordingly
324 $old_entry = db_fetch_object(db_query('SELECT vid, timestamp FROM {node_revisions} WHERE nid = %d AND vid < %d ORDER BY vid DESC LIMIT 1', $item->nid, $item->vid));
325 if ($old_entry->vid) {
326 $old_node = node_load($node->nid, $old_entry->vid);
327 if ($old_node->title != $node->title) {
328 $flag = t('Moved');
329 }
330 else {
331 $flag = t('Edited');
332 }
333 }
334 else {
335 $flag = t('New');
336 }
337
338 // RSS title
339 $title = $flag . ' ' . $node->type . ': ' . $node->title;
340 // RSS link: link to node
341 $link = url("node/$node->nid", null, null, 1);
342
343 // RSS body
344 $item_text = '';
345 if ($old_entry->vid && module_exists('diff')) {
346 // If diff module exists, put a diff in the RSS feed
347 $rows = array();
348 $header = array(
349 '',
350 t('Revision of %date', array('%date' => format_date($old_entry->timestamp))),
351 '',
352 t('Revision of %date', array('%date' => format_date($item->timestamp)))
353 );
354 $rows = _diff_body_rows($old_node, $node);
355 $item_text .= theme('table', $header, $rows, array('style' => 'width:100%'));
356 // Replace css classes from diff with actual style elements since we cannot reference
357 // a css file from the rss feed.
358 $patterns = array(
359 '/class=["\']diff-section-title["\']/',
360 '/class=["\']diff-deletedline["\']/',
361 '/class=["\']diff-addedline["\']/',
362 '/class=["\']diff-context["\']/',
363 '/class=["\']diffchange["\']/',
364 );
365 $replacements = array(
366 'style="background-color: #f0f0ff;"',
367 'style="background-color: #ffa;width: 50%"',
368 'style="background-color: #afa;width: 50%"',
369 'style="background-color: #fafafa"',
370 'style="color: #f00;font-weight:bold"',
371 );
372 $item_text = preg_replace($patterns, $replacements, $item_text);
373 $item_text .= "<hr/>\n";
374 }
375
376 if ($item_length != 'title') {
377 // Load node to display in feed
378 if (node_hook($node, 'view')) {
379 $node = node_invoke($node, 'view', $teaser, false);
380 }
381 else {
382 $node = node_prepare($node, $teaser);
383 }
384 // If only teaser is shown, add 'read more' link to feed
385 if ($teaser) {
386 $item_text .= $node->teaser;
387 if ($node->readmore) {
388 $item_text .= '<p>'. l(t('read more'), 'node/'. $node->nid, null, null, null, true) .'</p>';
389 }
390 }
391 else {
392 $item_text .= $node->body;
393 }
394 }
395
396 // RSS extras: load extras from node modules
397 $extra = node_invoke_nodeapi($node, 'rss item');
398 }
399 // Put in normal extra information
400 $extra = array_merge($extra,
401 array(
402 array('key' => 'pubDate', 'value' => date('r', $item->timestamp)),
403 array('key' => 'dc:creator', 'value' => $item->name),
404 array('key' => 'guid', 'value' => $item->nid .' at '. $base_url . ($is_comment ? ' comment '.$item->cid : ' revision '.$item->vid), 'attributes' => array('isPermaLink' => 'false'))
405 )
406 );
407 foreach ($extra as $element) {
408 if ($element['namespace']) {
409 $namespaces = array_merge($namespaces, $element['namespace']);
410 }
411 }
412 // Add rss feed item to output
413 $items .= format_rss_item($title, $link, $item_text, $extra);
414 }
415 // RSS channel information
416 $channel = array(
417 'version' => '2.0',
418 'link' => $base_url,
419 'description' => t('Recent changes of nodes and comments on %site', array('%site' => variable_get('site_name', 'drupal'))),
420 'language' => $GLOBALS['locale'],
421 );
422 if ($node_type) {
423 $channel['title'] = variable_get('site_name', 'drupal') .' - ' . t('Recent changes filtered by !node_type', array('!node_type' => $node_type));
424 }
425 else {
426 $channel['title'] = variable_get('site_name', 'drupal') .' - ' . t('Recent changes');
427 }
428 // Construct actual RSS feed text
429 $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
430 $output .= "<rss version=\"". $channel["version"] . "\" xml:base=\"". $base_url ."\" ". implode(' ', $namespaces) .">\n";
431 $output .= format_rss_channel($channel['title'], $channel['link'], $channel['description'], $items, $channel['language']);
432 $output .= "</rss>\n";
433
434 // Set MIME type in HTML header
435 drupal_set_header('Content-Type: application/rss+xml; charset=utf-8');
436 // Print feed text
437 print $output;
438 }
439
440 /*
441 * Settings
442 */
443
444 /**
445 * Number of entries per page.
446 */
447 function recent_changes_count_per_page($value = NULL) {
448 if (is_null($value)) {
449 return variable_get('recent_changes_entries_per_page', 50);
450 }
451 variable_set('recent_changes_entries_per_page', $value);
452 }
453
454 /**
455 * Is the filter form shown on the recent changes page?
456 */
457 function recent_changes_show_filter($value = NULL) {
458 if (is_null($value)) {
459 return variable_get('recent_changes_show_filter', true);
460 }
461 variable_set('recent_changes_show_filter', $value);
462 }
463
464 /**
465 * Are comments shown by default?
466 */
467 function recent_changes_show_comments($value = NULL) {
468 if (is_null($value)) {
469 return variable_get('recent_changes_show_comments', true);
470 }
471 variable_set('recent_changes_show_comments', $value);
472 }
473
474
475 /**
476 * SQL query for all node revisions.
477 */
478 function recent_changes_revisions_query($node_type = NULL) {
479 $result = 'SELECT n.type, r.nid, r.vid, 0 AS cid, r.title, r.timestamp, r.log, r.uid, u.name FROM {node_revisions} r LEFT JOIN {node} n ON r.nid = n.nid LEFT JOIN {users} u ON r.uid = u.uid';
480 // Change for query if access right restrictions apply
481 list($join, $where, $distinct) = _db_rewrite_sql($result, 'r', 'nid');
482 if ($node_type) {
483 // Filter on node type
484 $where .= " AND n.type = '" . check_plain($node_type) . "'";
485 }
486 if ($join) {
487 $result .= ' ' . $join;
488 }
489 if ($where) {
490 $result .= ' WHERE ' . $where;
491 }
492 return $result;
493 }
494
495 /**
496 * SQL query for all comments.
497 */
498 function recent_changes_comments_query($node_type = NULL) {
499 $result = 'SELECT n.type, c.nid, 0 AS vid, c.cid, n.title, c.timestamp, c.subject AS log, c.uid, c.name FROM {comments} c LEFT JOIN {node} n ON c.nid = n.nid';
500 list($join, $where, $distinct) = _db_rewrite_sql($result, 'c', 'nid');
501 if ($node_type) {
502 $where .= " AND n.type = '" . check_plain($node_type) . "'";
503 }
504 if ($join) {
505 $result .= ' ' . $join;
506 }
507 if ($where) {
508 $result .= ' WHERE ' . $where;
509 }
510 return $result;
511 }

  ViewVC Help
Powered by ViewVC 1.1.2