commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / modules / forum / forum.module
1 <?php
2
3 /**
4 * @file
5 * Provides discussion forums.
6 */
7
8 /**
9 * Implements hook_help().
10 */
11 function forum_help($path, $arg) {
12 switch ($path) {
13 case 'admin/help#forum':
14 $output = '';
15 $output .= '<h3>' . t('About') . '</h3>';
16 $output .= '<p>' . t('The Forum module lets you create threaded discussion forums with functionality similar to other message board systems. Forums are useful because they allow community members to discuss topics with one another while ensuring those conversations are archived for later reference. In a forum, users post topics and threads in nested hierarchies, allowing discussions to be categorized and grouped. The forum hierarchy consists of:') . '</p>';
17 $output .= '<ul>';
18 $output .= '<li>' . t('Optional containers (for example, <em>Support</em>), which can hold:') . '</li>';
19 $output .= '<ul><li>' . t('Forums (for example, <em>Installing Drupal</em>), which can hold:') . '</li>';
20 $output .= '<ul><li>' . t('Forum topics submitted by users (for example, <em>How to start a Drupal 6 Multisite</em>), which start discussions and are starting points for:') . '</li>';
21 $output .= '<ul><li>' . t('Threaded comments submitted by users (for example, <em>You have these options...</em>).') . '</li>';
22 $output .= '</ul>';
23 $output .= '</ul>';
24 $output .= '</ul>';
25 $output .= '</ul>';
26 $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@forum">Forum module</a>.', array('@forum' => 'http://drupal.org/documentation/modules/forum')) . '</p>';
27 $output .= '<h3>' . t('Uses') . '</h3>';
28 $output .= '<dl>';
29 $output .= '<dt>' . t('Setting up forum structure') . '</dt>';
30 $output .= '<dd>' . t('Visit the <a href="@forums">Forums page</a> to set up containers and forums to hold your discussion topics.', array('@forums' => url('admin/structure/forum'))) . '</dd>';
31 $output .= '<dt>' . t('Starting a discussion') . '</dt>';
32 $output .= '<dd>' . t('The <a href="@create-topic">Forum topic</a> link on the <a href="@content-add">Add new content</a> page creates the first post of a new threaded discussion, or thread.', array('@create-topic' => url('node/add/forum'), '@content-add' => url('node/add'))) . '</dd>';
33 $output .= '<dt>' . t('Navigation') . '</dt>';
34 $output .= '<dd>' . t('Enabling the Forum module provides a default <em>Forums</em> menu item in the navigation menu that links to the <a href="@forums">Forums page</a>.', array('@forums' => url('forum'))) . '</dd>';
35 $output .= '<dt>' . t('Moving forum topics') . '</dt>';
36 $output .= '<dd>' . t('A forum topic (and all of its comments) may be moved between forums by selecting a different forum while editing a forum topic. When moving a forum topic between forums, the <em>Leave shadow copy</em> option creates a link in the original forum pointing to the new location.') . '</dd>';
37 $output .= '<dt>' . t('Locking and disabling comments') . '</dt>';
38 $output .= '<dd>' . t('Selecting <em>Closed</em> under <em>Comment settings</em> while editing a forum topic will lock (prevent new comments on) the thread. Selecting <em>Hidden</em> under <em>Comment settings</em> while editing a forum topic will hide all existing comments on the thread, and prevent new ones.') . '</dd>';
39 $output .= '</dl>';
40 return $output;
41 case 'admin/structure/forum':
42 $output = '<p>' . t('Forums contain forum topics. Use containers to group related forums.') . '</p>';
43 $output .= theme('more_help_link', array('url' => 'admin/help/forum'));
44 return $output;
45 case 'admin/structure/forum/add/container':
46 return '<p>' . t('Use containers to group related forums.') . '</p>';
47 case 'admin/structure/forum/add/forum':
48 return '<p>' . t('A forum holds related forum topics.') . '</p>';
49 case 'admin/structure/forum/settings':
50 return '<p>' . t('Adjust the display of your forum topics. Organize the forums on the <a href="@forum-structure">forum structure page</a>.', array('@forum-structure' => url('admin/structure/forum'))) . '</p>';
51 }
52 }
53
54 /**
55 * Implements hook_theme().
56 */
57 function forum_theme() {
58 return array(
59 'forums' => array(
60 'template' => 'forums',
61 'variables' => array('forums' => NULL, 'topics' => NULL, 'parents' => NULL, 'tid' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
62 ),
63 'forum_list' => array(
64 'template' => 'forum-list',
65 'variables' => array('forums' => NULL, 'parents' => NULL, 'tid' => NULL),
66 ),
67 'forum_topic_list' => array(
68 'template' => 'forum-topic-list',
69 'variables' => array('tid' => NULL, 'topics' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
70 ),
71 'forum_icon' => array(
72 'template' => 'forum-icon',
73 'variables' => array('new_posts' => NULL, 'num_posts' => 0, 'comment_mode' => 0, 'sticky' => 0, 'first_new' => FALSE),
74 ),
75 'forum_submitted' => array(
76 'template' => 'forum-submitted',
77 'variables' => array('topic' => NULL),
78 ),
79 'forum_form' => array(
80 'render element' => 'form',
81 'file' => 'forum.admin.inc',
82 ),
83 );
84 }
85
86 /**
87 * Implements hook_menu().
88 */
89 function forum_menu() {
90 $items['forum'] = array(
91 'title' => 'Forums',
92 'page callback' => 'forum_page',
93 'access arguments' => array('access content'),
94 'file' => 'forum.pages.inc',
95 );
96 $items['forum/%forum_forum'] = array(
97 'title' => 'Forums',
98 'page callback' => 'forum_page',
99 'page arguments' => array(1),
100 'access arguments' => array('access content'),
101 'file' => 'forum.pages.inc',
102 );
103 $items['admin/structure/forum'] = array(
104 'title' => 'Forums',
105 'description' => 'Control forum hierarchy settings.',
106 'page callback' => 'drupal_get_form',
107 'page arguments' => array('forum_overview'),
108 'access arguments' => array('administer forums'),
109 'file' => 'forum.admin.inc',
110 );
111 $items['admin/structure/forum/list'] = array(
112 'title' => 'List',
113 'type' => MENU_DEFAULT_LOCAL_TASK,
114 'weight' => -10,
115 );
116 $items['admin/structure/forum/add/container'] = array(
117 'title' => 'Add container',
118 'page callback' => 'forum_form_main',
119 'page arguments' => array('container'),
120 'access arguments' => array('administer forums'),
121 'type' => MENU_LOCAL_ACTION,
122 'parent' => 'admin/structure/forum',
123 'file' => 'forum.admin.inc',
124 );
125 $items['admin/structure/forum/add/forum'] = array(
126 'title' => 'Add forum',
127 'page callback' => 'forum_form_main',
128 'page arguments' => array('forum'),
129 'access arguments' => array('administer forums'),
130 'type' => MENU_LOCAL_ACTION,
131 'parent' => 'admin/structure/forum',
132 'file' => 'forum.admin.inc',
133 );
134 $items['admin/structure/forum/settings'] = array(
135 'title' => 'Settings',
136 'page callback' => 'drupal_get_form',
137 'page arguments' => array('forum_admin_settings'),
138 'access arguments' => array('administer forums'),
139 'weight' => 5,
140 'type' => MENU_LOCAL_TASK,
141 'parent' => 'admin/structure/forum',
142 'file' => 'forum.admin.inc',
143 );
144 $items['admin/structure/forum/edit/container/%taxonomy_term'] = array(
145 'title' => 'Edit container',
146 'page callback' => 'forum_form_main',
147 'page arguments' => array('container', 5),
148 'access arguments' => array('administer forums'),
149 'file' => 'forum.admin.inc',
150 );
151 $items['admin/structure/forum/edit/forum/%taxonomy_term'] = array(
152 'title' => 'Edit forum',
153 'page callback' => 'forum_form_main',
154 'page arguments' => array('forum', 5),
155 'access arguments' => array('administer forums'),
156 'file' => 'forum.admin.inc',
157 );
158 return $items;
159 }
160
161 /**
162 * Implements hook_menu_local_tasks_alter().
163 */
164 function forum_menu_local_tasks_alter(&$data, $router_item, $root_path) {
165 global $user;
166
167 // Add action link to 'node/add/forum' on 'forum' sub-pages.
168 if ($root_path == 'forum' || $root_path == 'forum/%') {
169 $tid = (isset($router_item['page_arguments'][0]) ? $router_item['page_arguments'][0]->tid : 0);
170 $forum_term = forum_forum_load($tid);
171 if ($forum_term) {
172 $links = array();
173 // Loop through all bundles for forum taxonomy vocabulary field.
174 $field = field_info_field('taxonomy_forums');
175 foreach ($field['bundles']['node'] as $type) {
176 if (node_access('create', $type)) {
177 $links[$type] = array(
178 '#theme' => 'menu_local_action',
179 '#link' => array(
180 'title' => t('Add new @node_type', array('@node_type' => node_type_get_name($type))),
181 'href' => 'node/add/' . str_replace('_', '-', $type) . '/' . $forum_term->tid,
182 ),
183 );
184 }
185 }
186 if (empty($links)) {
187 // Authenticated user does not have access to create new topics.
188 if ($user->uid) {
189 $links['disallowed'] = array(
190 '#theme' => 'menu_local_action',
191 '#link' => array(
192 'title' => t('You are not allowed to post new content in the forum.'),
193 ),
194 );
195 }
196 // Anonymous user does not have access to create new topics.
197 else {
198 $links['login'] = array(
199 '#theme' => 'menu_local_action',
200 '#link' => array(
201 'title' => t('<a href="@login">Log in</a> to post new content in the forum.', array(
202 '@login' => url('user/login', array('query' => drupal_get_destination())),
203 )),
204 'localized_options' => array('html' => TRUE),
205 ),
206 );
207 }
208 }
209 $data['actions']['output'] = array_merge($data['actions']['output'], $links);
210 }
211 }
212 }
213
214 /**
215 * Implements hook_entity_info_alter().
216 */
217 function forum_entity_info_alter(&$info) {
218 // Take over URI construction for taxonomy terms that are forums.
219 if ($vid = variable_get('forum_nav_vocabulary', 0)) {
220 // Within hook_entity_info(), we can't invoke entity_load() as that would
221 // cause infinite recursion, so we call taxonomy_vocabulary_get_names()
222 // instead of taxonomy_vocabulary_load(). All we need is the machine name
223 // of $vid, so retrieving and iterating all the vocabulary names is somewhat
224 // inefficient, but entity info is cached across page requests, and an
225 // iteration of all vocabularies once per cache clearing isn't a big deal,
226 // and is done as part of taxonomy_entity_info() anyway.
227 foreach (taxonomy_vocabulary_get_names() as $machine_name => $vocabulary) {
228 if ($vid == $vocabulary->vid) {
229 $info['taxonomy_term']['bundles'][$machine_name]['uri callback'] = 'forum_uri';
230 }
231 }
232 }
233 }
234
235 /**
236 * Implements callback_entity_info_uri().
237 *
238 * Entity URI callback used in forum_entity_info_alter().
239 */
240 function forum_uri($forum) {
241 return array(
242 'path' => 'forum/' . $forum->tid,
243 );
244 }
245
246 /**
247 * Checks whether a node can be used in a forum, based on its content type.
248 *
249 * @param $node
250 * A node object.
251 *
252 * @return
253 * Boolean indicating if the node can be assigned to a forum.
254 */
255 function _forum_node_check_node_type($node) {
256 // Fetch information about the forum field.
257 $field = field_info_instance('node', 'taxonomy_forums', $node->type);
258
259 return is_array($field);
260 }
261
262 /**
263 * Implements hook_node_view().
264 */
265 function forum_node_view($node, $view_mode) {
266 if (_forum_node_check_node_type($node)) {
267 if ($view_mode == 'full' && node_is_page($node)) {
268 $vid = variable_get('forum_nav_vocabulary', 0);
269 $vocabulary = taxonomy_vocabulary_load($vid);
270 // Breadcrumb navigation
271 $breadcrumb[] = l(t('Home'), NULL);
272 $breadcrumb[] = l($vocabulary->name, 'forum');
273 if ($parents = taxonomy_get_parents_all($node->forum_tid)) {
274 $parents = array_reverse($parents);
275 foreach ($parents as $parent) {
276 $breadcrumb[] = l($parent->name, 'forum/' . $parent->tid);
277 }
278 }
279 drupal_set_breadcrumb($breadcrumb);
280
281 }
282 }
283 }
284
285 /**
286 * Implements hook_node_validate().
287 *
288 * Checks in particular that the node is assigned only a "leaf" term in the
289 * forum taxonomy.
290 */
291 function forum_node_validate($node, $form) {
292 if (_forum_node_check_node_type($node)) {
293 $langcode = $form['taxonomy_forums']['#language'];
294 // vocabulary is selected, not a "container" term.
295 if (!empty($node->taxonomy_forums[$langcode])) {
296 // Extract the node's proper topic ID.
297 $containers = variable_get('forum_containers', array());
298 foreach ($node->taxonomy_forums[$langcode] as $delta => $item) {
299 // If no term was selected (e.g. when no terms exist yet), remove the
300 // item.
301 if (empty($item['tid'])) {
302 unset($node->taxonomy_forums[$langcode][$delta]);
303 continue;
304 }
305 $term = taxonomy_term_load($item['tid']);
306 if (!$term) {
307 form_set_error('taxonomy_forums', t('Select a forum.'));
308 continue;
309 }
310 $used = db_query_range('SELECT 1 FROM {taxonomy_term_data} WHERE tid = :tid AND vid = :vid',0 , 1, array(
311 ':tid' => $term->tid,
312 ':vid' => $term->vid,
313 ))->fetchField();
314 if ($used && in_array($term->tid, $containers)) {
315 form_set_error('taxonomy_forums', t('The item %forum is a forum container, not a forum. Select one of the forums below instead.', array('%forum' => $term->name)));
316 }
317 }
318 }
319 }
320 }
321
322 /**
323 * Implements hook_node_presave().
324 *
325 * Assigns the forum taxonomy when adding a topic from within a forum.
326 */
327 function forum_node_presave($node) {
328 if (_forum_node_check_node_type($node)) {
329 // Make sure all fields are set properly:
330 $node->icon = !empty($node->icon) ? $node->icon : '';
331 reset($node->taxonomy_forums);
332 $langcode = key($node->taxonomy_forums);
333 if (!empty($node->taxonomy_forums[$langcode])) {
334 $node->forum_tid = $node->taxonomy_forums[$langcode][0]['tid'];
335 if (isset($node->nid)) {
336 $old_tid = db_query_range("SELECT f.tid FROM {forum} f INNER JOIN {node} n ON f.vid = n.vid WHERE n.nid = :nid ORDER BY f.vid DESC", 0, 1, array(':nid' => $node->nid))->fetchField();
337 if ($old_tid && isset($node->forum_tid) && ($node->forum_tid != $old_tid) && !empty($node->shadow)) {
338 // A shadow copy needs to be created. Retain new term and add old term.
339 $node->taxonomy_forums[$langcode][] = array('tid' => $old_tid);
340 }
341 }
342 }
343 }
344 }
345
346 /**
347 * Implements hook_node_update().
348 */
349 function forum_node_update($node) {
350 if (_forum_node_check_node_type($node)) {
351 if (empty($node->revision) && db_query('SELECT tid FROM {forum} WHERE nid=:nid', array(':nid' => $node->nid))->fetchField()) {
352 if (!empty($node->forum_tid)) {
353 db_update('forum')
354 ->fields(array('tid' => $node->forum_tid))
355 ->condition('vid', $node->vid)
356 ->execute();
357 }
358 // The node is removed from the forum.
359 else {
360 db_delete('forum')
361 ->condition('nid', $node->nid)
362 ->execute();
363 }
364 }
365 else {
366 if (!empty($node->forum_tid)) {
367 db_insert('forum')
368 ->fields(array(
369 'tid' => $node->forum_tid,
370 'vid' => $node->vid,
371 'nid' => $node->nid,
372 ))
373 ->execute();
374 }
375 }
376 // If the node has a shadow forum topic, update the record for this
377 // revision.
378 if (!empty($node->shadow)) {
379 db_delete('forum')
380 ->condition('nid', $node->nid)
381 ->condition('vid', $node->vid)
382 ->execute();
383 db_insert('forum')
384 ->fields(array(
385 'nid' => $node->nid,
386 'vid' => $node->vid,
387 'tid' => $node->forum_tid,
388 ))
389 ->execute();
390 }
391 }
392 }
393
394 /**
395 * Implements hook_node_insert().
396 */
397 function forum_node_insert($node) {
398 if (_forum_node_check_node_type($node)) {
399 if (!empty($node->forum_tid)) {
400 $nid = db_insert('forum')
401 ->fields(array(
402 'tid' => $node->forum_tid,
403 'vid' => $node->vid,
404 'nid' => $node->nid,
405 ))
406 ->execute();
407 }
408 }
409 }
410
411 /**
412 * Implements hook_node_delete().
413 */
414 function forum_node_delete($node) {
415 if (_forum_node_check_node_type($node)) {
416 db_delete('forum')
417 ->condition('nid', $node->nid)
418 ->execute();
419 db_delete('forum_index')
420 ->condition('nid', $node->nid)
421 ->execute();
422 }
423 }
424
425 /**
426 * Implements hook_node_load().
427 */
428 function forum_node_load($nodes) {
429 $node_vids = array();
430 foreach ($nodes as $node) {
431 if (_forum_node_check_node_type($node)) {
432 $node_vids[] = $node->vid;
433 }
434 }
435 if (!empty($node_vids)) {
436 $query = db_select('forum', 'f');
437 $query
438 ->fields('f', array('nid', 'tid'))
439 ->condition('f.vid', $node_vids);
440 $result = $query->execute();
441 foreach ($result as $record) {
442 $nodes[$record->nid]->forum_tid = $record->tid;
443 }
444 }
445 }
446
447 /**
448 * Implements hook_node_info().
449 */
450 function forum_node_info() {
451 return array(
452 'forum' => array(
453 'name' => t('Forum topic'),
454 'base' => 'forum',
455 'description' => t('A <em>forum topic</em> starts a new discussion thread within a forum.'),
456 'title_label' => t('Subject'),
457 )
458 );
459 }
460
461 /**
462 * Implements hook_permission().
463 */
464 function forum_permission() {
465 $perms = array(
466 'administer forums' => array(
467 'title' => t('Administer forums'),
468 ),
469 );
470 return $perms;
471 }
472
473 /**
474 * Implements hook_taxonomy_term_delete().
475 */
476 function forum_taxonomy_term_delete($term) {
477 // For containers, remove the tid from the forum_containers variable.
478 $containers = variable_get('forum_containers', array());
479 $key = array_search($term->tid, $containers);
480 if ($key !== FALSE) {
481 unset($containers[$key]);
482 }
483 variable_set('forum_containers', $containers);
484 }
485
486 /**
487 * Implements hook_comment_publish().
488 *
489 * This actually handles the insertion and update of published nodes since
490 * comment_save() calls hook_comment_publish() for all published comments.
491 */
492 function forum_comment_publish($comment) {
493 _forum_update_forum_index($comment->nid);
494 }
495
496 /**
497 * Implements hook_comment_update().
498 *
499 * The Comment module doesn't call hook_comment_unpublish() when saving
500 * individual comments, so we need to check for those here.
501 */
502 function forum_comment_update($comment) {
503 // comment_save() calls hook_comment_publish() for all published comments,
504 // so we need to handle all other values here.
505 if (!$comment->status) {
506 _forum_update_forum_index($comment->nid);
507 }
508 }
509
510 /**
511 * Implements hook_comment_unpublish().
512 */
513 function forum_comment_unpublish($comment) {
514 _forum_update_forum_index($comment->nid);
515 }
516
517 /**
518 * Implements hook_comment_delete().
519 */
520 function forum_comment_delete($comment) {
521 _forum_update_forum_index($comment->nid);
522 }
523
524 /**
525 * Implements hook_field_storage_pre_insert().
526 */
527 function forum_field_storage_pre_insert($entity_type, $entity, &$skip_fields) {
528 if ($entity_type == 'node' && $entity->status && _forum_node_check_node_type($entity)) {
529 $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
530 foreach ($entity->taxonomy_forums as $language) {
531 foreach ($language as $item) {
532 $query->values(array(
533 'nid' => $entity->nid,
534 'title' => $entity->title,
535 'tid' => $item['tid'],
536 'sticky' => $entity->sticky,
537 'created' => $entity->created,
538 'comment_count' => 0,
539 'last_comment_timestamp' => $entity->created,
540 ));
541 }
542 }
543 $query->execute();
544 }
545 }
546
547 /**
548 * Implements hook_field_storage_pre_update().
549 */
550 function forum_field_storage_pre_update($entity_type, $entity, &$skip_fields) {
551 $first_call = &drupal_static(__FUNCTION__, array());
552
553 if ($entity_type == 'node' && _forum_node_check_node_type($entity)) {
554
555 // If the node is published, update the forum index.
556 if ($entity->status) {
557
558 // We don't maintain data for old revisions, so clear all previous values
559 // from the table. Since this hook runs once per field, per object, make
560 // sure we only wipe values once.
561 if (!isset($first_call[$entity->nid])) {
562 $first_call[$entity->nid] = FALSE;
563 db_delete('forum_index')->condition('nid', $entity->nid)->execute();
564 }
565 $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
566 foreach ($entity->taxonomy_forums as $language) {
567 foreach ($language as $item) {
568 $query->values(array(
569 'nid' => $entity->nid,
570 'title' => $entity->title,
571 'tid' => $item['tid'],
572 'sticky' => $entity->sticky,
573 'created' => $entity->created,
574 'comment_count' => 0,
575 'last_comment_timestamp' => $entity->created,
576 ));
577 }
578 }
579 $query->execute();
580 // The logic for determining last_comment_count is fairly complex, so
581 // call _forum_update_forum_index() too.
582 _forum_update_forum_index($entity->nid);
583 }
584
585 // When a forum node is unpublished, remove it from the forum_index table.
586 else {
587 db_delete('forum_index')->condition('nid', $entity->nid)->execute();
588 }
589
590 }
591 }
592
593 /**
594 * Implements hook_form_FORM_ID_alter() for taxonomy_form_vocabulary().
595 */
596 function forum_form_taxonomy_form_vocabulary_alter(&$form, &$form_state, $form_id) {
597 $vid = variable_get('forum_nav_vocabulary', 0);
598 if (isset($form['vid']['#value']) && $form['vid']['#value'] == $vid) {
599 $form['help_forum_vocab'] = array(
600 '#markup' => t('This is the designated forum vocabulary. Some of the normal vocabulary options have been removed.'),
601 '#weight' => -1,
602 );
603 // Forum's vocabulary always has single hierarchy. Forums and containers
604 // have only one parent or no parent for root items. By default this value
605 // is 0.
606 $form['hierarchy']['#value'] = 1;
607 // Do not allow to delete forum's vocabulary.
608 $form['actions']['delete']['#access'] = FALSE;
609 }
610 }
611
612 /**
613 * Implements hook_form_FORM_ID_alter() for taxonomy_form_term().
614 */
615 function forum_form_taxonomy_form_term_alter(&$form, &$form_state, $form_id) {
616 $vid = variable_get('forum_nav_vocabulary', 0);
617 if (isset($form['vid']['#value']) && $form['vid']['#value'] == $vid) {
618 // Hide multiple parents select from forum terms.
619 $form['relations']['parent']['#access'] = FALSE;
620 }
621 }
622
623 /**
624 * Implements hook_form_BASE_FORM_ID_alter() for node_form().
625 */
626 function forum_form_node_form_alter(&$form, &$form_state, $form_id) {
627 if (isset($form['taxonomy_forums'])) {
628 $langcode = $form['taxonomy_forums']['#language'];
629 // Make the vocabulary required for 'real' forum-nodes.
630 $form['taxonomy_forums'][$langcode]['#required'] = TRUE;
631 $form['taxonomy_forums'][$langcode]['#multiple'] = FALSE;
632 if (empty($form['taxonomy_forums'][$langcode]['#default_value'])) {
633 // If there is no default forum already selected, try to get the forum
634 // ID from the URL (e.g., if we are on a page like node/add/forum/2, we
635 // expect "2" to be the ID of the forum that was requested).
636 $requested_forum_id = arg(3);
637 $form['taxonomy_forums'][$langcode]['#default_value'] = is_numeric($requested_forum_id) ? $requested_forum_id : '';
638 }
639 }
640 }
641
642 /**
643 * Implements hook_block_info().
644 */
645 function forum_block_info() {
646 $blocks['active'] = array(
647 'info' => t('Active forum topics'),
648 'cache' => DRUPAL_CACHE_CUSTOM,
649 'properties' => array('administrative' => TRUE),
650 );
651 $blocks['new'] = array(
652 'info' => t('New forum topics'),
653 'cache' => DRUPAL_CACHE_CUSTOM,
654 'properties' => array('administrative' => TRUE),
655 );
656 return $blocks;
657 }
658
659 /**
660 * Implements hook_block_configure().
661 */
662 function forum_block_configure($delta = '') {
663 $form['forum_block_num_' . $delta] = array(
664 '#type' => 'select',
665 '#title' => t('Number of topics'),
666 '#default_value' => variable_get('forum_block_num_' . $delta, '5'),
667 '#options' => drupal_map_assoc(range(2, 20))
668 );
669 return $form;
670 }
671
672 /**
673 * Implements hook_block_save().
674 */
675 function forum_block_save($delta = '', $edit = array()) {
676 variable_set('forum_block_num_' . $delta, $edit['forum_block_num_' . $delta]);
677 }
678
679 /**
680 * Implements hook_block_view().
681 *
682 * Generates a block containing the currently active forum topics and the most
683 * recently added forum topics.
684 */
685 function forum_block_view($delta = '') {
686 $query = db_select('forum_index', 'f')
687 ->fields('f')
688 ->addTag('node_access');
689 switch ($delta) {
690 case 'active':
691 $title = t('Active forum topics');
692 $query
693 ->orderBy('f.last_comment_timestamp', 'DESC')
694 ->range(0, variable_get('forum_block_num_active', '5'));
695 break;
696
697 case 'new':
698 $title = t('New forum topics');
699 $query
700 ->orderBy('f.created', 'DESC')
701 ->range(0, variable_get('forum_block_num_new', '5'));
702 break;
703 }
704
705 $block['subject'] = $title;
706 // Cache based on the altered query. Enables us to cache with node access enabled.
707 $block['content'] = drupal_render_cache_by_query($query, 'forum_block_view');
708 $block['content']['#access'] = user_access('access content');
709 return $block;
710 }
711
712 /**
713 * Render API callback: Lists nodes based on the element's #query property.
714 *
715 * This function can be used as a #pre_render callback.
716 *
717 * @see forum_block_view()
718 */
719 function forum_block_view_pre_render($elements) {
720 $result = $elements['#query']->execute();
721 if ($node_title_list = node_title_list($result)) {
722 $elements['forum_list'] = $node_title_list;
723 $elements['forum_more'] = array('#theme' => 'more_link', '#url' => 'forum', '#title' => t('Read the latest forum topics.'));
724 }
725 return $elements;
726 }
727
728 /**
729 * Implements hook_form().
730 */
731 function forum_form($node, $form_state) {
732 $type = node_type_get_type($node);
733 $form['title'] = array(
734 '#type' => 'textfield',
735 '#title' => check_plain($type->title_label),
736 '#default_value' => !empty($node->title) ? $node->title : '',
737 '#required' => TRUE, '#weight' => -5
738 );
739
740 if (!empty($node->nid)) {
741 $forum_terms = $node->taxonomy_forums;
742 // If editing, give option to leave shadows.
743 $shadow = (count($forum_terms) > 1);
744 $form['shadow'] = array('#type' => 'checkbox', '#title' => t('Leave shadow copy'), '#default_value' => $shadow, '#description' => t('If you move this topic, you can leave a link in the old forum to the new forum.'));
745 $form['forum_tid'] = array('#type' => 'value', '#value' => $node->forum_tid);
746 }
747
748 return $form;
749 }
750
751 /**
752 * Returns a tree of all forums for a given taxonomy term ID.
753 *
754 * @param $tid
755 * (optional) Taxonomy term ID of the forum. If not given all forums will be
756 * returned.
757 *
758 * @return
759 * A tree of taxonomy objects, with the following additional properties:
760 * - num_topics: Number of topics in the forum.
761 * - num_posts: Total number of posts in all topics.
762 * - last_post: Most recent post for the forum.
763 * - forums: An array of child forums.
764 */
765 function forum_forum_load($tid = NULL) {
766 $cache = &drupal_static(__FUNCTION__, array());
767
768 // Return a cached forum tree if available.
769 if (!isset($tid)) {
770 $tid = 0;
771 }
772 if (isset($cache[$tid])) {
773 return $cache[$tid];
774 }
775
776 $vid = variable_get('forum_nav_vocabulary', 0);
777
778 // Load and validate the parent term.
779 if ($tid) {
780 $forum_term = taxonomy_term_load($tid);
781 if (!$forum_term || ($forum_term->vid != $vid)) {
782 return $cache[$tid] = FALSE;
783 }
784 }
785 // If $tid is 0, create an empty object to hold the child terms.
786 elseif ($tid === 0) {
787 $forum_term = (object) array(
788 'tid' => 0,
789 );
790 }
791
792 // Determine if the requested term is a container.
793 if (!$forum_term->tid || in_array($forum_term->tid, variable_get('forum_containers', array()))) {
794 $forum_term->container = 1;
795 }
796
797 // Load parent terms.
798 $forum_term->parents = taxonomy_get_parents_all($forum_term->tid);
799
800 // Load the tree below.
801 $forums = array();
802 $_forums = taxonomy_get_tree($vid, $tid);
803
804 if (count($_forums)) {
805 $query = db_select('node', 'n');
806 $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
807 $query->join('forum', 'f', 'n.vid = f.vid');
808 $query->addExpression('COUNT(n.nid)', 'topic_count');
809 $query->addExpression('SUM(ncs.comment_count)', 'comment_count');
810 $counts = $query
811 ->fields('f', array('tid'))
812 ->condition('n.status', 1)
813 ->groupBy('tid')
814 ->addTag('node_access')
815 ->execute()
816 ->fetchAllAssoc('tid');
817 }
818
819 foreach ($_forums as $forum) {
820 // Determine if the child term is a container.
821 if (in_array($forum->tid, variable_get('forum_containers', array()))) {
822 $forum->container = 1;
823 }
824
825 // Merge in the topic and post counters.
826 if (!empty($counts[$forum->tid])) {
827 $forum->num_topics = $counts[$forum->tid]->topic_count;
828 $forum->num_posts = $counts[$forum->tid]->topic_count + $counts[$forum->tid]->comment_count;
829 }
830 else {
831 $forum->num_topics = 0;
832 $forum->num_posts = 0;
833 }
834
835 // Query "Last Post" information for this forum.
836 $query = db_select('node', 'n');
837 $query->join('users', 'u1', 'n.uid = u1.uid');
838 $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', array(':tid' => $forum->tid));
839 $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
840 $query->join('users', 'u2', 'ncs.last_comment_uid = u2.uid');
841 $query->addExpression('CASE ncs.last_comment_uid WHEN 0 THEN ncs.last_comment_name ELSE u2.name END', 'last_comment_name');
842
843 $topic = $query
844 ->fields('ncs', array('last_comment_timestamp', 'last_comment_uid'))
845 ->condition('n.status', 1)
846 ->orderBy('last_comment_timestamp', 'DESC')
847 ->range(0, 1)
848 ->addTag('node_access')
849 ->execute()
850 ->fetchObject();
851
852 // Merge in the "Last Post" information.
853 $last_post = new stdClass();
854 if (!empty($topic->last_comment_timestamp)) {
855 $last_post->created = $topic->last_comment_timestamp;
856 $last_post->name = $topic->last_comment_name;
857 $last_post->uid = $topic->last_comment_uid;
858 }
859 $forum->last_post = $last_post;
860
861 $forums[$forum->tid] = $forum;
862 }
863
864 // Cache the result, and return the tree.
865 $forum_term->forums = $forums;
866 $cache[$tid] = $forum_term;
867 return $forum_term;
868 }
869
870 /**
871 * Calculates the number of new posts in a forum that the user has not yet read.
872 *
873 * Nodes are new if they are newer than NODE_NEW_LIMIT.
874 *
875 * @param $term
876 * The term ID of the forum.
877 * @param $uid
878 * The user ID.
879 *
880 * @return
881 * The number of new posts in the forum that have not been read by the user.
882 */
883 function _forum_topics_unread($term, $uid) {
884 $query = db_select('node', 'n');
885 $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', array(':tid' => $term));
886 $query->leftJoin('history', 'h', 'n.nid = h.nid AND h.uid = :uid', array(':uid' => $uid));
887 $query->addExpression('COUNT(n.nid)', 'count');
888 return $query
889 ->condition('status', 1)
890 ->condition('n.created', NODE_NEW_LIMIT, '>')
891 ->isNull('h.nid')
892 ->addTag('node_access')
893 ->execute()
894 ->fetchField();
895 }
896
897 /**
898 * Gets all the topics in a forum.
899 *
900 * @param $tid
901 * The term ID of the forum.
902 * @param $sortby
903 * One of the following integers indicating the sort criteria:
904 * - 1: Date - newest first.
905 * - 2: Date - oldest first.
906 * - 3: Posts with the most comments first.
907 * - 4: Posts with the least comments first.
908 * @param $forum_per_page
909 * The maximum number of topics to display per page.
910 *
911 * @return
912 * A list of all the topics in a forum.
913 */
914 function forum_get_topics($tid, $sortby, $forum_per_page) {
915 global $user, $forum_topic_list_header;
916
917 $forum_topic_list_header = array(
918 NULL,
919 array('data' => t('Topic'), 'field' => 'f.title'),
920 array('data' => t('Replies'), 'field' => 'f.comment_count'),
921 array('data' => t('Last reply'), 'field' => 'f.last_comment_timestamp'),
922 );
923
924 $order = _forum_get_topic_order($sortby);
925 for ($i = 0; $i < count($forum_topic_list_header); $i++) {
926 if ($forum_topic_list_header[$i]['field'] == $order['field']) {
927 $forum_topic_list_header[$i]['sort'] = $order['sort'];
928 }
929 }
930
931 $query = db_select('forum_index', 'f')->extend('PagerDefault')->extend('TableSort');
932 $query->fields('f');
933 $query
934 ->condition('f.tid', $tid)
935 ->addTag('node_access')
936 ->orderBy('f.sticky', 'DESC')
937 ->orderByHeader($forum_topic_list_header)
938 ->limit($forum_per_page);
939
940 $count_query = db_select('forum_index', 'f');
941 $count_query->condition('f.tid', $tid);
942 $count_query->addExpression('COUNT(*)');
943 $count_query->addTag('node_access');
944
945 $query->setCountQuery($count_query);
946 $result = $query->execute();
947 $nids = array();
948 foreach ($result as $record) {
949 $nids[] = $record->nid;
950 }
951 if ($nids) {
952 $query = db_select('node', 'n')->extend('TableSort');
953 $query->fields('n', array('title', 'nid', 'type', 'sticky', 'created', 'uid'));
954 $query->addField('n', 'comment', 'comment_mode');
955
956 $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
957 $query->fields('ncs', array('cid', 'last_comment_uid', 'last_comment_timestamp', 'comment_count'));
958
959 $query->join('forum_index', 'f', 'f.nid = ncs.nid');
960 $query->addField('f', 'tid', 'forum_tid');
961
962 $query->join('users', 'u', 'n.uid = u.uid');
963 $query->addField('u', 'name');
964
965 $query->join('users', 'u2', 'ncs.last_comment_uid = u2.uid');
966
967 $query->addExpression('CASE ncs.last_comment_uid WHEN 0 THEN ncs.last_comment_name ELSE u2.name END', 'last_comment_name');
968
969 $query
970 ->orderBy('f.sticky', 'DESC')
971 ->orderByHeader($forum_topic_list_header)
972 ->condition('n.nid', $nids);
973
974 $result = $query->execute();
975 }
976 else {
977 $result = array();
978 }
979
980 $topics = array();
981 $first_new_found = FALSE;
982 foreach ($result as $topic) {
983 if ($user->uid) {
984 // A forum is new if the topic is new, or if there are new comments since
985 // the user's last visit.
986 if ($topic->forum_tid != $tid) {
987 $topic->new = 0;
988 }
989 else {
990 $history = _forum_user_last_visit($topic->nid);
991 $topic->new_replies = comment_num_new($topic->nid, $history);
992 $topic->new = $topic->new_replies || ($topic->last_comment_timestamp > $history);
993 }
994 }
995 else {
996 // Do not track "new replies" status for topics if the user is anonymous.
997 $topic->new_replies = 0;
998 $topic->new = 0;
999 }
1000
1001 // Make sure only one topic is indicated as the first new topic.
1002 $topic->first_new = FALSE;
1003 if ($topic->new != 0 && !$first_new_found) {
1004 $topic->first_new = TRUE;
1005 $first_new_found = TRUE;
1006 }
1007
1008 if ($topic->comment_count > 0) {
1009 $last_reply = new stdClass();
1010 $last_reply->created = $topic->last_comment_timestamp;
1011 $last_reply->name = $topic->last_comment_name;
1012 $last_reply->uid = $topic->last_comment_uid;
1013 $topic->last_reply = $last_reply;
1014 }
1015 $topics[] = $topic;
1016 }
1017
1018 return $topics;
1019 }
1020
1021 /**
1022 * Preprocesses variables for forums.tpl.php.
1023 *
1024 * @param $variables
1025 * An array containing the following elements:
1026 * - forums: An array of all forum objects to display for the given taxonomy
1027 * term ID. If tid = 0 then all the top-level forums are displayed.
1028 * - topics: An array of all the topics in the current forum.
1029 * - parents: An array of taxonomy term objects that are ancestors of the
1030 * current term ID.
1031 * - tid: Taxonomy term ID of the current forum.
1032 * - sortby: One of the following integers indicating the sort criteria:
1033 * - 1: Date - newest first.
1034 * - 2: Date - oldest first.
1035 * - 3: Posts with the most comments first.
1036 * - 4: Posts with the least comments first.
1037 * - forum_per_page: The maximum number of topics to display per page.
1038 *
1039 * @see forums.tpl.php
1040 */
1041 function template_preprocess_forums(&$variables) {
1042 global $user;
1043
1044 $vid = variable_get('forum_nav_vocabulary', 0);
1045 $vocabulary = taxonomy_vocabulary_load($vid);
1046 $title = !empty($vocabulary->name) ? $vocabulary->name : '';
1047
1048 // Breadcrumb navigation:
1049 $breadcrumb[] = l(t('Home'), NULL);
1050 if ($variables['tid']) {
1051 $breadcrumb[] = l($vocabulary->name, 'forum');
1052 }
1053 if ($variables['parents']) {
1054 $variables['parents'] = array_reverse($variables['parents']);
1055 foreach ($variables['parents'] as $p) {
1056 if ($p->tid == $variables['tid']) {
1057 $title = $p->name;
1058 }
1059 else {
1060 $breadcrumb[] = l($p->name, 'forum/' . $p->tid);
1061 }
1062 }
1063 }
1064 drupal_set_breadcrumb($breadcrumb);
1065 drupal_set_title($title);
1066
1067 if ($variables['forums_defined'] = count($variables['forums']) || count($variables['parents'])) {
1068 if (!empty($variables['forums'])) {
1069 $variables['forums'] = theme('forum_list', $variables);
1070 }
1071 else {
1072 $variables['forums'] = '';
1073 }
1074
1075 if ($variables['tid'] && !in_array($variables['tid'], variable_get('forum_containers', array()))) {
1076 $variables['topics'] = theme('forum_topic_list', $variables);
1077 drupal_add_feed('taxonomy/term/' . $variables['tid'] . '/feed', 'RSS - ' . $title);
1078 }
1079 else {
1080 $variables['topics'] = '';
1081 }
1082
1083 // Provide separate template suggestions based on what's being output. Topic id is also accounted for.
1084 // Check both variables to be safe then the inverse. Forums with topic ID's take precedence.
1085 if ($variables['forums'] && !$variables['topics']) {
1086 $variables['theme_hook_suggestions'][] = 'forums__containers';
1087 $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid'];
1088 $variables['theme_hook_suggestions'][] = 'forums__containers__' . $variables['tid'];
1089 }
1090 elseif (!$variables['forums'] && $variables['topics']) {
1091 $variables['theme_hook_suggestions'][] = 'forums__topics';
1092 $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid'];
1093 $variables['theme_hook_suggestions'][] = 'forums__topics__' . $variables['tid'];
1094 }
1095 else {
1096 $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid'];
1097 }
1098
1099 }
1100 else {
1101 drupal_set_title(t('No forums defined'));
1102 $variables['forums'] = '';
1103 $variables['topics'] = '';
1104 }
1105 }
1106
1107 /**
1108 * Preprocesses variables for forum-list.tpl.php.
1109 *
1110 * @param $variables
1111 * An array containing the following elements:
1112 * - forums: An array of all forum objects to display for the given taxonomy
1113 * term ID. If tid = 0 then all the top-level forums are displayed.
1114 * - parents: An array of taxonomy term objects that are ancestors of the
1115 * current term ID.
1116 * - tid: Taxonomy term ID of the current forum.
1117 *
1118 * @see forum-list.tpl.php
1119 * @see theme_forum_list()
1120 */
1121 function template_preprocess_forum_list(&$variables) {
1122 global $user;
1123 $row = 0;
1124 // Sanitize each forum so that the template can safely print the data.
1125 foreach ($variables['forums'] as $id => $forum) {
1126 $variables['forums'][$id]->description = !empty($forum->description) ? filter_xss_admin($forum->description) : '';
1127 $variables['forums'][$id]->link = url("forum/$forum->tid");
1128 $variables['forums'][$id]->name = check_plain($forum->name);
1129 $variables['forums'][$id]->is_container = !empty($forum->container);
1130 $variables['forums'][$id]->zebra = $row % 2 == 0 ? 'odd' : 'even';
1131 $row++;
1132
1133 $variables['forums'][$id]->new_text = '';
1134 $variables['forums'][$id]->new_url = '';
1135 $variables['forums'][$id]->new_topics = 0;
1136 $variables['forums'][$id]->old_topics = $forum->num_topics;
1137 $variables['forums'][$id]->icon_class = 'default';
1138 $variables['forums'][$id]->icon_title = t('No new posts');
1139 if ($user->uid) {
1140 $variables['forums'][$id]->new_topics = _forum_topics_unread($forum->tid, $user->uid);
1141 if ($variables['forums'][$id]->new_topics) {
1142 $variables['forums'][$id]->new_text = format_plural($variables['forums'][$id]->new_topics, '1 new', '@count new');
1143 $variables['forums'][$id]->new_url = url("forum/$forum->tid", array('fragment' => 'new'));
1144 $variables['forums'][$id]->icon_class = 'new';
1145 $variables['forums'][$id]->icon_title = t('New posts');
1146 }
1147 $variables['forums'][$id]->old_topics = $forum->num_topics - $variables['forums'][$id]->new_topics;
1148 }
1149 $variables['forums'][$id]->last_reply = theme('forum_submitted', array('topic' => $forum->last_post));
1150 }
1151 // Give meaning to $tid for themers. $tid actually stands for term id.
1152 $variables['forum_id'] = $variables['tid'];
1153 unset($variables['tid']);
1154 }
1155
1156 /**
1157 * Preprocesses variables for forum-topic-list.tpl.php.
1158 *
1159 * @param $variables
1160 * An array containing the following elements:
1161 * - tid: Taxonomy term ID of the current forum.
1162 * - topics: An array of all the topics in the current forum.
1163 * - forum_per_page: The maximum number of topics to display per page.
1164 *
1165 * @see forum-topic-list.tpl.php
1166 * @see theme_forum_topic_list()
1167 */
1168 function template_preprocess_forum_topic_list(&$variables) {
1169 global $forum_topic_list_header;
1170
1171 // Create the tablesorting header.
1172 $ts = tablesort_init($forum_topic_list_header);
1173 $header = '';
1174 foreach ($forum_topic_list_header as $cell) {
1175 $cell = tablesort_header($cell, $forum_topic_list_header, $ts);
1176 $header .= _theme_table_cell($cell, TRUE);
1177 }
1178 $variables['header'] = $header;
1179
1180 if (!empty($variables['topics'])) {
1181 $row = 0;
1182 foreach ($variables['topics'] as $id => $topic) {
1183 $variables['topics'][$id]->icon = theme('forum_icon', array('new_posts' => $topic->new, 'num_posts' => $topic->comment_count, 'comment_mode' => $topic->comment_mode, 'sticky' => $topic->sticky, 'first_new' => $topic->first_new));
1184 $variables['topics'][$id]->zebra = $row % 2 == 0 ? 'odd' : 'even';
1185 $row++;
1186
1187 // We keep the actual tid in forum table, if it's different from the
1188 // current tid then it means the topic appears in two forums, one of
1189 // them is a shadow copy.
1190 if ($variables['tid'] != $topic->forum_tid) {
1191 $variables['topics'][$id]->moved = TRUE;
1192 $variables['topics'][$id]->title = check_plain($topic->title);
1193 $variables['topics'][$id]->message = l(t('This topic has been moved'), "forum/$topic->forum_tid");
1194 }
1195 else {
1196 $variables['topics'][$id]->moved = FALSE;
1197 $variables['topics'][$id]->title = l($topic->title, "node/$topic->nid");
1198 $variables['topics'][$id]->message = '';
1199 }
1200 $variables['topics'][$id]->created = theme('forum_submitted', array('topic' => $topic));
1201 $variables['topics'][$id]->last_reply = theme('forum_submitted', array('topic' => isset($topic->last_reply) ? $topic->last_reply : NULL));
1202
1203 $variables['topics'][$id]->new_text = '';
1204 $variables['topics'][$id]->new_url = '';
1205 if ($topic->new_replies) {
1206 $variables['topics'][$id]->new_text = format_plural($topic->new_replies, '1 new', '@count new');
1207 $variables['topics'][$id]->new_url = url("node/$topic->nid", array('query' => comment_new_page_count($topic->comment_count, $topic->new_replies, $topic), 'fragment' => 'new'));
1208 }
1209
1210 }
1211 }
1212 else {
1213 // Make this safe for the template.
1214 $variables['topics'] = array();
1215 }
1216 // Give meaning to $tid for themers. $tid actually stands for term id.
1217 $variables['topic_id'] = $variables['tid'];
1218 unset($variables['tid']);
1219
1220 $variables['pager'] = theme('pager');
1221 }
1222
1223 /**
1224 * Preprocesses variables for forum-icon.tpl.php.
1225 *
1226 * @param $variables
1227 * An array containing the following elements:
1228 * - new_posts: Indicates whether or not the topic contains new posts.
1229 * - num_posts: The total number of posts in all topics.
1230 * - comment_mode: An integer indicating whether comments are open, closed,
1231 * or hidden.
1232 * - sticky: Indicates whether the topic is sticky.
1233 * - first_new: Indicates whether this is the first topic with new posts.
1234 *
1235 * @see forum-icon.tpl.php
1236 * @see theme_forum_icon()
1237 */
1238 function template_preprocess_forum_icon(&$variables) {
1239 $variables['hot_threshold'] = variable_get('forum_hot_topic', 15);
1240 if ($variables['num_posts'] > $variables['hot_threshold']) {
1241 $variables['icon_class'] = $variables['new_posts'] ? 'hot-new' : 'hot';
1242 $variables['icon_title'] = $variables['new_posts'] ? t('Hot topic, new comments') : t('Hot topic');
1243 }
1244 else {
1245 $variables['icon_class'] = $variables['new_posts'] ? 'new' : 'default';
1246 $variables['icon_title'] = $variables['new_posts'] ? t('New comments') : t('Normal topic');
1247 }
1248
1249 if ($variables['comment_mode'] == COMMENT_NODE_CLOSED || $variables['comment_mode'] == COMMENT_NODE_HIDDEN) {
1250 $variables['icon_class'] = 'closed';
1251 $variables['icon_title'] = t('Closed topic');
1252 }
1253
1254 if ($variables['sticky'] == 1) {
1255 $variables['icon_class'] = 'sticky';
1256 $variables['icon_title'] = t('Sticky topic');
1257 }
1258 }
1259
1260 /**
1261 * Preprocesses variables for forum-submitted.tpl.php.
1262 *
1263 * The submission information will be displayed in the forum list and topic
1264 * list.
1265 *
1266 * @param $variables
1267 * An array containing the following elements:
1268 * - topic: The topic object.
1269 *
1270 * @see forum-submitted.tpl.php
1271 * @see theme_forum_submitted()
1272 */
1273 function template_preprocess_forum_submitted(&$variables) {
1274 $variables['author'] = isset($variables['topic']->uid) ? theme('username', array('account' => $variables['topic'])) : '';
1275 $variables['time'] = isset($variables['topic']->created) ? format_interval(REQUEST_TIME - $variables['topic']->created) : '';
1276 }
1277
1278 /**
1279 * Gets the last time the user viewed a node.
1280 *
1281 * @param $nid
1282 * The node ID.
1283 *
1284 * @return
1285 * The timestamp when the user last viewed this node, if the user has
1286 * previously viewed the node; otherwise NODE_NEW_LIMIT.
1287 */
1288 function _forum_user_last_visit($nid) {
1289 global $user;
1290 $history = &drupal_static(__FUNCTION__, array());
1291
1292 if (empty($history)) {
1293 $result = db_query('SELECT nid, timestamp FROM {history} WHERE uid = :uid', array(':uid' => $user->uid));
1294 foreach ($result as $t) {
1295 $history[$t->nid] = $t->timestamp > NODE_NEW_LIMIT ? $t->timestamp : NODE_NEW_LIMIT;
1296 }
1297 }
1298 return isset($history[$nid]) ? $history[$nid] : NODE_NEW_LIMIT;
1299 }
1300
1301 /**
1302 * Gets topic sorting information based on an integer code.
1303 *
1304 * @param $sortby
1305 * One of the following integers indicating the sort criteria:
1306 * - 1: Date - newest first.
1307 * - 2: Date - oldest first.
1308 * - 3: Posts with the most comments first.
1309 * - 4: Posts with the least comments first.
1310 *
1311 * @return
1312 * An array with the following values:
1313 * - field: A field for an SQL query.
1314 * - sort: 'asc' or 'desc'.
1315 */
1316 function _forum_get_topic_order($sortby) {
1317 switch ($sortby) {
1318 case 1:
1319 return array('field' => 'f.last_comment_timestamp', 'sort' => 'desc');
1320 break;
1321 case 2:
1322 return array('field' => 'f.last_comment_timestamp', 'sort' => 'asc');
1323 break;
1324 case 3:
1325 return array('field' => 'f.comment_count', 'sort' => 'desc');
1326 break;
1327 case 4:
1328 return array('field' => 'f.comment_count', 'sort' => 'asc');
1329 break;
1330 }
1331 }
1332
1333 /**
1334 * Updates the taxonomy index for a given node.
1335 *
1336 * @param $nid
1337 * The ID of the node to update.
1338 */
1339 function _forum_update_forum_index($nid) {
1340 $count = db_query('SELECT COUNT(cid) FROM {comment} c INNER JOIN {forum_index} i ON c.nid = i.nid WHERE c.nid = :nid AND c.status = :status', array(
1341 ':nid' => $nid,
1342 ':status' => COMMENT_PUBLISHED,
1343 ))->fetchField();
1344
1345 if ($count > 0) {
1346 // Comments exist.
1347 $last_reply = db_query_range('SELECT cid, name, created, uid FROM {comment} WHERE nid = :nid AND status = :status ORDER BY cid DESC', 0, 1, array(
1348 ':nid' => $nid,
1349 ':status' => COMMENT_PUBLISHED,
1350 ))->fetchObject();
1351 db_update('forum_index')
1352 ->fields( array(
1353 'comment_count' => $count,
1354 'last_comment_timestamp' => $last_reply->created,
1355 ))
1356 ->condition('nid', $nid)
1357 ->execute();
1358 }
1359 else {
1360 // Comments do not exist.
1361 $node = db_query('SELECT uid, created FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchObject();
1362 db_update('forum_index')
1363 ->fields( array(
1364 'comment_count' => 0,
1365 'last_comment_timestamp' => $node->created,
1366 ))
1367 ->condition('nid', $nid)
1368 ->execute();
1369 }
1370 }
1371
1372 /**
1373 * Implements hook_rdf_mapping().
1374 */
1375 function forum_rdf_mapping() {
1376 return array(
1377 array(
1378 'type' => 'node',
1379 'bundle' => 'forum',
1380 'mapping' => array(
1381 'rdftype' => array('sioc:Post', 'sioct:BoardPost'),
1382 'taxonomy_forums' => array(
1383 'predicates' => array('sioc:has_container'),
1384 'type' => 'rel',
1385 ),
1386 ),
1387 ),
1388 array(
1389 'type' => 'taxonomy_term',
1390 'bundle' => 'forums',
1391 'mapping' => array(
1392 'rdftype' => array('sioc:Container', 'sioc:Forum'),
1393 ),
1394 ),
1395 );
1396 }