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

Contents of /contributions/modules/discussthis/discussthis.module

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


Revision 1.7 - (show annotations) (download) (as text)
Wed Jun 25 14:20:44 2008 UTC (17 months ago) by spiderman
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--1
Changes since 1.6: +2 -2 lines
File MIME type: text/x-php
per http://drupal.org/node/272896#comment-894017, removing extraneous check_plain() call
1 <?php
2 // $Id: discussthis.module,v 1.6 2008/06/24 01:05:27 spiderman Exp $
3
4 // Hook Implementations
5
6 /**
7 * Display help and module information
8 * @param section which section of the site we're displaying help for
9 * @return help text for section
10 **/
11 function discussthis_help($section='') {
12 $output = '';
13
14 switch ($section) {
15 case 'admin/help#discussthis':
16 $output = '<p>'. t('Displays links to discussion forums for a given node. Administrators can select the types of nodes for which this is enabled, and for each of these, which forum new topics should be created in.') .'</p>';
17 $output .= filter_filter('process', 2, null, file_get_contents(dirname(__FILE__) .'/README.txt') );
18 break;
19 }
20
21 return $output;
22 }
23
24 /**
25 * Valid permissions for this module
26 * @return array An array of valid permissions for the discussthis module
27 **/
28 function discussthis_perm() {
29 return array('administer discuss this', 'override discuss this forums', 'access discuss this links');
30 }
31
32 /**
33 * hook_menu implementation
34 * @param may_cache whether the menu items might be cached
35 * @return array list of menu items
36 **/
37 function discussthis_menu($may_cache) {
38 $items = array();
39
40 if (!$may_cache) {
41 $items[] = array(
42 'path' => 'admin/settings/discussthis',
43 'title' => t('Discuss This'),
44 'description' => t('Configure discuss this module defaults, including what node types and what forums to link to.'),
45 'callback' => 'drupal_get_form',
46 'callback arguments' => 'discussthis_admin',
47 'access' => user_access('administer discuss this'),
48 'type' => MENU_NORMAL_ITEM,
49 );
50 $items[] = array(
51 'path' => 'discussthis/new',
52 'callback' => 'discussthis_new',
53 'access' => user_access('access discuss this links'),
54 'type' => MENU_CALLBACK,
55 );
56 $items[] = array('path' => 'discussthis/autocomplete',
57 'title' => t('Autocomplete forum topics'),
58 'callback' => 'discussthis_autocomplete',
59 'access' => user_access('access content'),
60 'type' => MENU_CALLBACK);
61 }
62
63 return $items;
64 }
65
66 /**
67 * hook_nodeapi implementation
68 * This is the meat of the module. Here we add the
69 * Discuss This link which will create a new forum topic if none exists, or
70 * link to the existing one if it does. Also adds forum override dropdown on add/edit screen
71 * for users with override permissions
72 *
73 * @param node the node being acted on
74 * @param op the operation being done to the node
75 * @param teaser whether this is a teaser view
76 * @param page whether this is a full page view
77 **/
78 function discussthis_nodeapi(&$node, $op, $teaser, $page) {
79 $enabled = variable_get('discussthis_node_'. $node->type, 0);
80
81 // If this is an discussthis-enabled node type, treat the per-node fields appropriately
82 if ($enabled) {
83 switch ($op) {
84 case 'validate': // validate forum override selection
85 break;
86 case 'insert': // create or update/move forum topic
87 case 'update':
88 $forum_tid = $node->discussthis['forum'];
89 $typed_title = $node->discussthis['topic'];
90
91 // unquote commas and/or doublequotes created by autocomplete function before storing
92 $typed_title = str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $typed_title));
93 $typed_title = trim($typed_title);
94
95 // store the forum and topic mapping in the db
96 _discussthis_set_forum($node->nid, $forum_tid);
97 _discussthis_set_topic($node->nid, $typed_title);
98
99 break;
100 case 'view':
101 if (!$teaser && user_access('access discuss this links')) {
102 $topic_nid = _discussthis_get_topic($node->nid); // lookup a nid for the topic, if it exists (otherwise 0)
103
104 # grab X most recent comments, and attach them to the node:
105 $result = db_query_range('SELECT c.nid, c.uid, c.name, c.subject, c.cid, c.timestamp FROM {comments} c INNER JOIN {node} n ON n.nid = c.nid WHERE c.nid = %d AND n.status = 1 AND c.status = %d ORDER BY c.cid DESC', $topic_nid, COMMENT_PUBLISHED, 0, variable_get('discussthis_comments',10));
106
107 while ($comment = db_fetch_object($result)) {
108 $comments[] = theme('discussthis_comment',$topic_nid,$comment);
109 }
110
111 $node->content['discussthis_comments'] = array(
112 '#value' => theme('item_list',$comments),
113 '#weight' => 50,
114 );
115 }
116 break;
117 case 'load':
118 // return array of fields => values to be merged into $node
119 break;
120 case 'delete':
121 // drop db row for this node.. what should happen to the forum topic?
122 break;
123 }
124 }
125 elseif ($node->type == 'forum') {
126 switch ($op) {
127 case 'delete':
128 // drop db row for this forum nid
129 $sql = 'DELETE FROM {discussthis} WHERE topic_nid = %d';
130 db_query($sql, $node->nid);
131 break;
132 }
133 }
134 }
135
136 /**
137 * hook_link implementation
138 * This adds the custom link to the node view
139 * @param type An identifier declaring what kind of link is being requested
140 * @param node A node object being passed in the case of node inks
141 * @param teaser A boolean flag depending on whether the node is displayed with its teaser or full form
142 * @return An array of the requested links
143 **/
144 function discussthis_link($type, $node = null, $teaser = false) {
145 $links = array();
146
147 if ($type == 'node') {
148 $enabled = variable_get('discussthis_node_'. $node->type, 0);
149 if ($enabled && user_access('access discuss this links')) {
150 $topic_nid = _discussthis_get_topic($node->nid); // lookup a nid for the topic, if it exists (otherwise 0)
151 if ($topic_nid) { // link to add a comment to the existing topic
152 $links['discussthis'] = array(
153 'title' => t('Discuss This!'),
154 'href' => 'node/'. $topic_nid,
155 );
156 }
157 else { // link to a callback page which creates a topic, then redirects to add a comment to it
158 $links['discussthis'] = array(
159 'title' => t('Discuss This!'),
160 'href' => 'discussthis/new/'. $node->nid,
161 );
162 }
163 }
164 }
165 return $links;
166 }
167
168 /**
169 * hook_form_alter implementation
170 * Adds per-node override forum dropdown and topic autocomplete form for node edit forms
171 * @param form_id the id of the form to be altered
172 * @param form the form itself
173 **/
174 function discussthis_form_alter($form_id, &$form) {
175 // We're only modifying node forms, if the type field isn't set we don't need
176 // to bother; otherwise, store it for later retrieval.
177 if (isset($form['type'])) {
178 $type = $form['type']['#value'];
179 }
180 elseif (isset($form['orig_type'])) {
181 $type = $form['orig_type']['#value'];
182 }
183 else {
184 return;
185 }
186
187 # only modify the form if this node type is discussthis-enabled
188 if (!variable_get('discussthis_node_'.$type,0)) { return; }
189
190 // The discuss this link is enabled on a per node type basis. The variable used to store
191 // this information is named using both the name of this module, to avoid namespace
192 // conflicts, and the node type, because we support multiple node types.
193
194 switch ($form_id) {
195 case $type .'_node_form':
196 // here we need to look up any existing forum topic for this node, for use in the autocomplete field below..
197 $nid = $form['nid']['#value'];
198 $forum_tid = _discussthis_get_forum($nid, $type);
199 $forum_nid = _discussthis_get_topic($nid);
200 $topic = node_load(array('nid' => $forum_nid));
201
202 // If discuss this is enabled for this node type, insert the fieldset
203 if ($forum_tid && user_access('override discuss this forums')) {
204 $forums = forum_get_forums();
205 foreach ($forums as $tid => $forum) {
206 if (!$forum->container) { $discussthis_forums[$tid] = $forum->name; }
207 }
208
209 $form['discussthis'] = array(
210 '#type' => 'fieldset',
211 '#title' => t('Discuss This'),
212 '#collapsible' => true,
213 '#collapsed' => true,
214 '#weight' => 1,
215 '#tree' => true,
216 );
217 $form['discussthis']['forum'] = array(
218 '#type' => 'select',
219 '#title' => t('Forum for new discussions on this node'),
220 '#required' => true,
221 '#description' => t('Select the forum where a NEW discussion will be created if none exist.'),
222 '#options' => $discussthis_forums,
223 '#default_value' => $forum_tid,
224 '#weight' => 2,
225 );
226 $form['discussthis']['topic'] = array(
227 '#type' => 'textfield',
228 '#title' => t('Forum topic for discussions on this node'),
229 '#description' => t('The forum topic where discussions of this node appear'),
230 '#default_value' => $topic->title,
231 '#autocomplete_path' => 'discussthis/autocomplete/'. $forum_tid,
232 '#maxlength' => 255,
233 '#weight' => 3,
234 );
235 }
236 break;
237 }
238 }
239
240 /**
241 * Generate HTML for the discussthis block
242 * @param op the operation from the URL
243 * @param delta offset
244 * @returns block HTML
245 */
246 function discussthis_block($op='list', $delta=0) {
247 if ($op == 'list') {
248 $block[0]['info'] = t('Discuss This');
249 return $block;
250 }
251 else if ($op == 'view') {
252 // show a list of recent comments in the relevant discussion topic for this node
253 $block['subject'] = t('Discuss This!');
254 $block['content'] = '';
255 return $block;
256 }
257 }
258
259 /**
260 * Implementation of hook_token_list provides a list of tokens this module makes available
261 * @param type the context of the tokens being requested
262 * @return array of tokens grouped by context
263 **/
264 function discussthis_token_list($type = 'all') {
265 if ($type == 'discussthis' || $type == 'all') {
266 $tokens['discussthis']['node-link'] = t('A link to the original node under discussion.');
267 $tokens['discussthis']['node-title'] = t('The title of the original node.');
268 $tokens['discussthis']['node-type-name'] = t('The name of the original node\'s type.');
269 $tokens['discussthis']['node-type'] = t('The original node\'s type.');
270 return $tokens;
271 }
272 }
273
274 /**
275 * Implementation of hook_token_values provides the values for discussthis tokens
276 * @param type the context/type of object
277 * @param object the object itself
278 * @param options the options, context and object-sensitive
279 * @return array of token values keyed by their name
280 **/
281 function discussthis_token_values($type, $object = NULL, $options = array()) {
282 if ($type == 'discussthis') {
283 $tokens['node-link'] = l(t($object->title), 'node/'. $object->nid);
284 $tokens['node-title'] = t($object->title);
285 $tokens['node-type-name'] = node_get_types('name',$object);
286 $tokens['node-type'] = $object->type;
287 return $tokens;
288 }
289 }
290
291 // Callback Functions
292
293 /**
294 * Admin settings callback (from menu hook)
295 * The admin/settings/discussthis page provides three sets of configuration options:
296 * 1. The author of newly-created forum discussions
297 * 2. The set of node types for which Discuss This! links should appear
298 * 3. For each selected node type, a default forum in which to create discussion topics
299 **/
300 function discussthis_admin() {
301 $node_types = node_get_types('names');
302 $discussthis_nodetypes = variable_get('discussthis_nodetypes', $node_types);
303
304 $admin = user_load(array('uid' => 1));
305 $form['discussthis_author'] = array(
306 '#type' => 'textfield',
307 '#title' => t('Author of newly created Forum discussions'),
308 '#size' => 30,
309 '#maxlength' => 60,
310 '#autocomplete_path' => 'user/autocomplete',
311 '#default_value' => variable_get('discussthis_author', $admin->name),
312 '#weight' => -4,
313 );
314
315 $form['discussthis_newsubject'] = array(
316 '#type' => 'textfield',
317 '#title' => t('Title for newly created Forum discussions'),
318 '#size' => 30,
319 '#maxlength' => 60,
320 '#default_value' => variable_get('discussthis_newsubject', '[node-title]'),
321 '#weight' => -3,
322 );
323
324 $default_body = 'Following is a discussion on the [node-type-name] item titled: [node-link]'.
325 '.<br /> Below is the discussion so far. Feel free to add your own comments!<br />';
326 $form['discussthis_newtemplate'] = array(
327 '#type' => 'textarea',
328 '#title' => t('Template for auto-created Forum discussions'),
329 '#description' => theme('token_help','discussthis'),
330 '#default_value' => variable_get('discussthis_newtemplate',$default_body),
331 '#weight' => -2,
332 );
333
334 $form['discussthis_comments'] = array(
335 '#type' => 'select',
336 '#title' => t('How many recent comments should be displayed with non-teaser node view?'),
337 '#options' => array(1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 10 => '10'),
338 '#default_value' => variable_get('discussthis_comments',10),
339 );
340
341 $form['discussthis_nodetypes'] = array(
342 '#type' => 'checkboxes',
343 '#title' => t('Which node types should use the Discuss This feature'),
344 '#description' => t('Select all node types which should allow Discuss This links to a corresponding forum topic.'),
345 '#default_value' => $discussthis_nodetypes,
346 '#options' => $node_types,
347 );
348
349 $forums = forum_get_forums();
350 $discussthis_forums = array('0' => '-- select --');
351 foreach ($forums as $tid => $forum) {
352 if (!$forum->container) { $discussthis_forums[$tid] = $forum->name; }
353 }
354
355 // This should probably be within the node type config page, or else use some AJAXy thing to update
356 // these form fields based on the discussthis_nodetypes selection above..
357 foreach ($discussthis_nodetypes as $type => $name) {
358 if ($name) {
359 $form['defaults']['discussthis_node_'. $type] = array(
360 '#type' => 'select',
361 '#title' => 'Default forum for '. $node_types[$type] .' nodes',
362 '#default_value' => variable_get('discussthis_node_'. $type, 0),
363 '#options' => $discussthis_forums,
364 );
365 }
366 else {
367 $form['defaults']['discussthis_node_'. $type] = array(
368 '#type' => 'hidden',
369 '#value' => '0',
370 );
371 }
372 }
373
374 return system_settings_form($form);
375 }
376
377 /**
378 * Create a new discussion forum topic for the given nid, and redirect the user
379 * to post a comment on that topic
380 **/
381 function discussthis_new($nid) {
382 $topic_nid = _discussthis_new_topic($nid);
383 drupal_goto('comment/reply/'. $topic_nid, null, 'comment-form');
384 }
385
386 /**
387 * Autocomplete a forum topic discussion title
388 **/
389 function discussthis_autocomplete($tid, $string = '') {
390 // The user enters a title for a forum topic.
391 if ($string != '') {
392
393 // Grab the first 10 forum nodes with titles like the string typed by the user
394 $result = db_query_range(db_rewrite_sql('SELECT n.nid,n.title FROM {node} n WHERE n.type = "forum" AND LOWER(n.title) LIKE LOWER("%s%%")'), $string, 0, 10);
395
396 $matches = array();
397 while ($node = db_fetch_object($result)) {
398 $n = $node->title;
399
400 // Quote any commas or doublequotes for security reasons (XSS)
401 if (strpos($node->title, ',') !== false || strpos($node->title, '"') !== false) {
402 $n = '"'. str_replace('"', '""', $node->title) .'"';
403 }
404 $matches[$n] = check_plain($node->title);
405 }
406
407 echo drupal_to_js($matches);
408 exit();
409 }
410 }
411
412 // Utility Functions
413
414 /**
415 * Lookup the given nid in the discussthis_forums db table, and return the corresponding forum tid, otherwise 0
416 **/
417 function _discussthis_get_forum($nid, $type) {
418 $sql = 'SELECT forum_tid FROM {discussthis_forums} WHERE nid=%d';
419 $forum_tid = db_result(db_query($sql, $nid));
420 if (!$forum_tid) {
421 $forum_tid = variable_get('discussthis_node_'. $type, 0);
422 }
423 return $forum_tid;
424 }
425
426 /**
427 * Store a mapping between the given nid and a forum tid
428 **/
429 function _discussthis_set_forum($nid, $tid) {
430 $sql = 'REPLACE INTO {discussthis_forums} (nid,forum_tid) VALUES (%d,%d)';
431 db_query($sql, $nid, $tid);
432 }
433
434 /**
435 * Lookup the given nid in the discussthis db table, and return the corresponding forum topic nid, otherwise
436 * return the default for this node type
437 **/
438 function _discussthis_get_topic($nid) {
439 // lookup the nid in the db table, and return it's corresponding forum topic nid, otherwise 0
440 $sql = 'SELECT topic_nid FROM {discussthis} WHERE nid=%d';
441 $topic = db_result(db_query($sql, $nid));
442 if ($topic) { return $topic; }
443 else { return 0; }
444 }
445
446 /**
447 * Store a mapping between the given nid and a forum topic nid
448 **/
449 function _discussthis_set_topic($nid, $title) {
450 // lookup the topic title first, and get a forum_nid from that
451 $sql = 'SELECT n.nid,n.title FROM {node} n WHERE n.title like "%s" and n.type = "forum"';
452 $result = db_query(db_rewrite_sql($sql), $title);
453 $topic = db_fetch_object($result);
454
455 if ($topic->nid) {
456 // now just store the mapping in the discussthis_forums table
457 $sql = 'REPLACE INTO {discussthis} (nid,topic_nid) VALUES (%d,%d)';
458 db_query($sql, $nid, $topic->nid);
459 }
460 else {
461 // if there was no match, ignore, and remove any existing mapping?
462 $sql = 'DELETE FROM {discussthis} WHERE nid=%d';
463 db_query($sql, $nid);
464 }
465 }
466
467 /**
468 * Create a new forum topic for the given nid, store the correspondence in the db, and return the new topic nid
469 **/
470 function _discussthis_new_topic($nid) {
471 // load the node, to get its title/details
472 $node = node_load(array('nid' => $nid));
473 $type = node_get_types('name', $node);
474 $tid = _discussthis_get_forum($nid, $node->type);
475 $admin = user_load(array('uid' => 1));
476
477 // create the new forum topic with relevant stock text (and a link back to the original node)
478 $newnode = array('type' => 'forum');
479 $values = array();
480 $values['name'] = variable_get('discussthis_author', $admin->name);
481 $values['title'] = token_replace(variable_get('discussthis_newsubject','[node-title]'), 'discussthis', $node);
482
483 $default_body = 'Following is a discussion on the [node-type-name] item titled: [node-link]'.
484 '.<br /> Below is the discussion so far. Feel free to add your own comments!<br />';
485 $values['body'] = token_replace(variable_get('discussthis_newtemplate',$default_body), 'discussthis', $node);
486
487 $values['taxonomy'][1] = $tid;
488 $values['status'] = 1;
489 drupal_execute('forum_node_form', $values, $newnode);
490
491 $errors = form_get_errors();
492 if (!empty($errors)) {
493 // do something?
494 ob_start(); print_r($errors);
495 drupal_set_message('there was an error creating the new discussion: '. ob_get_contents());
496 ob_end_clean();
497 }
498
499 // get the new nid (not ideal, but using drupal_execute doesn't return a node object, so this
500 // is the best way I could come up with to retrieve the nid of the node we just created)
501 $newnode['title'] = $values['title'];
502 $topic = node_load($newnode);
503
504 // store the nid -> forum nid mapping in the db
505 $sql = 'REPLACE INTO {discussthis} (nid,topic_nid) VALUES (%d,%d)';
506 db_query($sql, array($node->nid, $topic->nid));
507
508 // return the new topic nid
509 return $topic->nid;
510 }
511
512 // Theme Functions
513
514 function theme_discussthis_comment($topic_nid,$comment) {
515 $output = "<div class=\"discussthis-comment\">\n";
516 $output .= ' <span class="subject">'. l($comment->subject, 'node/'. $topic_nid, null, null, "comment-$comment->cid") .'</span> ';
517 $output .= '<span class="credit">'. t('by') .' '. theme('username', $comment) ."</span>\n";
518 $output .= "</div>\n";
519 return $output;
520 }

  ViewVC Help
Powered by ViewVC 1.1.2