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

Contents of /contributions/modules/faq_ask/faq_ask.module

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


Revision 1.43 - (show annotations) (download) (as text)
Sat Feb 28 19:48:41 2009 UTC (8 months, 4 weeks ago) by nancyw
Branch: MAIN
CVS Tags: HEAD
Changes since 1.42: +36 -11 lines
File MIME type: text/x-php
#369308 by NancyDru for msowsett - Add setting to give all questions to expert.
1 <?php
2 // $Id: faq_ask.module,v 1.42 2009/02/28 16:39:29 nancyw Exp $
3
4 /**
5 * @file
6 * This module is an add-on to the FAQ module that allows users with the 'ask question'
7 * permission to create a question which will be queued for an 'expert' to answer.
8 */
9
10 /**
11 * Implementation of hook_help().
12 */
13 function faq_ask_help($section='') {
14 $output = '';
15
16 switch ($section) {
17 case 'admin/help#faq_ask':
18 $output .= '<p>'. t("This module is an add-on to the FAQ module that allows users with the 'ask question' permission to create a question which will be queued for an 'expert' to answer.") .'</p>'.
19 '<p>'. t("The module shows an abbreviated version of the FAQ form without an answer field. The node is created without the 'published' attribute. There is a block that will show the unanswered questions to the 'expert' (generally, this requires a separate role).") .'</p>'.
20 '<p>'. t("Viewing of the completed question and answer pair is done by the FAQ module.") .'</p>'.
21 '<p>'. t("Simply adding the 'FAQ' content type to a vocabulary will not make it eligible for experts; you must go to the settings page and add it there.") .'</p>';
22 return $output;
23
24 case 'admin/settings/faq/ask':
25 return theme('box', NULL, '<big>'. t('In order for the Faq_Ask module to operate, you must, at the least,: 1) select one or more roles as experts (and you may have to "Save configuration"); 2) select at least one category and expert combination; 3) click the "Save configuration" button.') .'</big>');
26
27 case 'faq_ask/more':
28 $output = '<p>'. variable_get('faq_ask_expert_advice', _faq_ask_advice_default('expert')) .'</p>';
29
30 if (user_access('administer blocks')) {
31 $output .= '<p><em>'. t('You may go <a href="!setting">here</a> to change the block limit.', array('!setting' => url('admin/build/block/configure/faq_ask/0'))) .'</em></p>';
32 }
33
34 return $output;
35
36 case 'faq_ask/'. arg(1):
37 case 'faq_ask':
38 return variable_get('faq_ask_help_text', _faq_ask_help_default()); }
39 }
40
41 /**
42 * Implementation of hook_perm().
43 * Define the permissions this module uses
44 */
45 function faq_ask_perm() {
46 return array('ask question', 'answer question');
47 }
48
49 /**
50 * Implementation of hook_access().
51 */
52 function faq_ask_access($op, $node) {
53 global $user;
54 // If node is already published, it's not ours any more.
55 if ($node->status == 1) {
56 drupal_set_message('Already published.');
57 return NULL;
58 }
59 else {
60 drupal_set_message('Not published yet.');
61 }
62 if ($op == 'create') {
63 return user_access(t('ask_question')) || user_access('answer question');
64 }
65 else {
66 // return user_access('answer question') || user_access('edit_own_faq');
67 // We don't include "edit own" because the intent is they can edit their own until it's published.
68 return user_access('answer question') || ($user->uid == $node->uid && $node->status == 0);
69 }
70 }
71
72 /**
73 * Implementation of hook_enable().
74 * This function reminds the user to configure the module.
75 */
76 function faq_ask_enable() {
77 if (_faq_ask_check_dependencies()) {
78 drupal_set_message(t('The Faq_Ask module has been enabled.') .' '. t('Please go to the <a href="!url">settings page</a> to configure this module.', array('!url' => url('admin/settings/faq/ask'))), 'notice');
79 }
80 }
81
82 /**
83 * Implementation of hook_menu().
84 */
85 function faq_ask_menu($may_cache) {
86 if (!_faq_ask_check_dependencies()) {
87 return array();
88 }
89
90 $items = array();
91
92 if ($may_cache) {
93 $items[] = array(
94 'path' => 'admin/settings/faq/ask',
95 'title' => t('Experts'),
96 'callback' => 'drupal_get_form',
97 'callback arguments' => array('faq_ask_settings_form'),
98 'access' => user_access('administer faq'),
99 'description' => t('Allows the user to configure the Ask_FAQ module.'),
100 'type' => MENU_LOCAL_TASK,
101 'weight' => -2,
102 );
103
104 $items[] = array(
105 'path' => 'faq_ask',
106 'title' => t('Ask a question'),
107 'callback' => 'faq_ask_page',
108 'access' => user_access('ask question'),
109 );
110 }
111 else {
112 $items[] = array(
113 'path' => 'faq_ask/answer',
114 'title' => t('Answer a question'),
115 'callback' => 'faq_ask_answer',
116 'access' => user_access('answer question'),
117 'type' => MENU_CALLBACK,
118 );
119
120 $items[] = array(
121 'path' => 'faq_ask/edit',
122 'title' => t('Edit a question'),
123 'callback' => 'faq_ask_edit',
124 // 'access' => user_access('answer question'),
125 'access' => user_access('access content'),
126 'type' => MENU_CALLBACK,
127 );
128
129 $items[] = array(
130 'path' => 'faq_ask/more',
131 'title' => t('List more unanswered questions'),
132 'callback' => 'faq_ask_list_more',
133 'access' => user_access('answer question') || user_access('ask question'),
134 'type' => MENU_CALLBACK,
135 );
136 }
137
138 return $items;
139 }
140
141 /**
142 * Get the ask question form.
143 */
144 function faq_ask_page($tid = NULL) {
145 $output = NULL;
146 if ($tid) {
147 $term = taxonomy_get_term($tid);
148 $taxo_image = NULL;
149 if (module_exists('taxonomy_image')) {
150 $taxo_image = taxonomy_image_display($tid, 'align="left"');
151 }
152 $output .= '<table id="faq_ask_description"><tr><td>'. $taxo_image . filter_xss($term->description) .'</td></tr></table>';
153 $output .= '<div class="clear-block"></div>';
154 }
155 $output .= drupal_get_form('faq_ask_form', $tid, NULL);
156 return $output;
157 }
158
159 /**
160 * Get the edit question form.
161 */
162 function faq_ask_edit($nid = NULL) {
163 global $user;
164 $node = node_load($nid);
165 if ($node->status == 1) {
166 drupal_set_message(t('That question has already been answered.'), 'status');
167 }
168 else {
169 if ($user->uid == $node->uid) {
170 $form = drupal_get_form('faq_ask_form', NULL, $nid);
171 return $form;
172 }
173 else {
174 drupal_set_message(t('You are not allowed to edit that question.'), 'error');
175 }
176 }
177 drupal_goto('node');
178 }
179
180 /**
181 * Implementation of hook_form().
182 * This is the "ask question" form.
183 */
184 function faq_ask_form($tid, $nid, $form_values = NULL) {
185 $form = array();
186
187 // Find all the categories for which we have experts.
188 $cat_sql = db_rewrite_sql('SELECT DISTINCT(t.name), e.tid FROM {term_data} t JOIN {faq_expert} e USING (tid)', 't', 'tid');
189 $cat_list = array_flip(db_result_array(db_query($cat_sql)));
190 if (count($cat_list) == 0) {
191 $msg = t('Currently, there are no categories defined. ') .' ';
192 if (user_access('administer faq')) {
193 $msg .= t('Please go to the <a href="!url">settings page</a> to configure this module.', array('!url' => url('admin/settings/faq/ask')));
194 }
195 else {
196 $msg .= t('Please <a href="!url">ask your site administrator</a> to set up this feature.', array('!url' => url('user/1/contact')));
197 }
198 drupal_set_message($msg, 'error');
199 }
200
201 // We will allow term suggestions only if the setting is chosen
202 // AND there is a vocabulary named "FAQ."
203 $vid = db_result(db_query_range("SELECT vid FROM {vocabulary} WHERE name='FAQ'", 0, 1));
204 $suggest = variable_get('faq_ask_suggest', FALSE) && $vid;
205
206 if ($suggest) {
207 $cat_list[0] = t('<suggest a category>');
208 $form['#multistep'] = TRUE;
209 $form['#redirect'] = FALSE;
210 $form['faq_vid'] = array(
211 '#type' => 'value',
212 '#value' => $vid,
213 );
214 if (!$tid) {
215 $default_tid = 0;
216 }
217 }
218
219 // If a nid exists, then get the existing values.
220 if ($nid) {
221 $node = node_load($nid);
222 $title = $node->title;
223 $taxo = array_keys($node->taxonomy);
224 $default_tid = $taxo[0];
225 $detailed_question = $node->detailed_question;
226 }
227 else {
228 $default_tid = $tid;
229 $title = NULL;
230 $detailed_question = NULL;
231 }
232
233 $form['nid'] = array(
234 '#type' => 'value',
235 '#value' => $nid,
236 );
237
238 // Category.
239 $expert_categorize = variable_get('faq_ask_categorize', FALSE);
240 // Check if only experts can categorize the question.
241 // if (!$expert_categorize && user_access('answer question')) {
242 if (!$expert_categorize) {
243 $form['category'] = array(
244 '#title' => t('Category'),
245 '#type' => 'select',
246 '#options' => $cat_list,
247 '#default_value' => $default_tid,
248 '#required' => TRUE,
249 '#weight' => -3,
250 '#description' => t('Please select the correct category for your question.'),
251 );
252 }
253 else {
254 $form['category'] = array(
255 '#type' => 'value',
256 '#value' => -1,
257 );
258 }
259
260 if (isset($form_values)) {
261 if ($form_values['category'] == 0) {
262 $form['new_cat'] = array(
263 '#title' => t('Suggested Category'),
264 '#type' => 'textfield',
265 '#weight' => -1,
266 '#default_value' => NULL,
267 '#description' => t('Please enter your suggested category for the question.'),
268 );
269 }
270 }
271
272 // Question.
273 $question_length = variable_get('faq_question_length', 'short');
274 $cols = variable_get('faq_ask_title_len', 60);
275 // Long questions enabled.
276 $form['title'] = array(
277 '#type' => 'textfield',
278 '#title' => t('Question Summary'),
279 '#required' => TRUE,
280 '#default_value' => $title,
281 '#weight' => 0,
282 '#cols' => $cols,
283 '#rows' => 5,
284 '#maxlength' => 256,
285 '#weight' => -1,
286 '#description' => t('Summarize your question here. It will be answered as soon as possible.'),
287 );
288 $form['detailed_question'] = array(
289 '#type' => 'textarea',
290 '#title' => t('Details'),
291 '#required' => true,
292 '#default_value' => $detailed_question,
293 '#weight' => 0,
294 '#cols' => $cols,
295 '#rows' => 5,
296 '#description' => t('Enter your question here. It will be answered as soon as possible.'),
297 );
298
299 $form['send'] = array(
300 '#type' => 'submit',
301 '#value' => t('Send my question'),
302 '#weight' => 3,
303 );
304
305 return $form;
306 }
307
308 /**
309 * Implementation of hook_form_submit().
310 * This function gets the entered question and category and creates an unpublished FAQ node.
311 */
312 function faq_ask_form_submit($form_id, $form_values) {
313 global $user;
314
315 $category = $form_values['category'];
316 if ($category == 0 && empty($form_values['new_cat'])) {
317 // Can only be 0 if allowing category suggestion.
318 }
319 else {
320 if ($category == 0) {
321 // We have a new category to process.
322 $new_cat = $form_values['new_cat'];
323 $term = array('name' => $new_cat,
324 'description' => 'suggested by '. $user->name .' ('. $user->uid .') on '. date('F j, Y'),
325 'vid' => $form_values['faq_vid'],
326 'weight' => 0,
327 'parent' => 0,
328 'tid' => NULL,
329 );
330 taxonomy_save_term($term);
331 $term = (object)$term;
332 // Setting up the expert will be done in hook_taxonomy.
333 }
334 $term = taxonomy_get_term($category);
335
336 $node = array(
337 'type' => 'faq',
338 'body' => '', /* Empty string rather than NULL. */
339 'title' => $form_values['title'],
340 'taxonomy' => $category >= 0 ? array($category => $term) : NULL,
341 'created' => time(),
342 'uid' => $user->uid,
343 'name' => $user->uid ? $user->name : variable_get('anonymous', t('Anonymous')),
344 'status' => 0, /* Unpublished. */
345 'format' => 1, /* Default filter (filtered HTML) */
346 'comment' => variable_get('comment_faq', 0),
347 'detailed_question' => (isset($form_values['detailed_question']) ? $form_values['detailed_question'] : ''),
348 );
349 $node_options = variable_get('node_options_'. $node['type'], array('status', 'promote'));
350 foreach (array('promote', 'sticky', 'revision') as $key) {
351 $node[$key] = in_array($key, $node_options) ? 1 : 0;
352 }
353
354 if ($form_values['nid']) {
355 $node['nid'] = $form_values['nid'];
356 }
357
358 // Okay, let's get it done. Node_submit will prepare it and make it an object.
359 $node = node_submit($node);
360 node_save($node);
361
362 // Are we notifying the expert(s)?
363 if (variable_get('faq_ask_notify', FALSE)) {
364 // Find out who the experts are.
365 $result = db_query("SELECT u.mail FROM {faq_expert} e JOIN {users} u USING (uid) WHERE e.tid=%d", $category);
366 $to_addresses = array();
367 while ($expert = db_fetch_array($result)) {
368 $to_addresses[] = $expert['mail'];
369 }
370 $to = implode(', ', $to_addresses);
371
372 $from = variable_get('site_name', 'Drupal') .'<'. variable_get('site_mail', ini_get('sendmail_from')) .'>';
373 // Let's make this HTML mail because of the links we'll use.
374 $header = array(
375 'MIME-Version' => '1.0',
376 'Content-Type' => 'text/html; charset=UTF-8; format=flowed',
377 'Content-Transfer-Encoding' => '8Bit',
378 'X-Mailer' => 'Drupal'
379 );
380
381 if ($category == -1) {
382 $body = t('The following question has been posted.');
383 }
384 else {
385 $body = t('The following question has been posted in the "!cat" category.', array('!cat' => filter_xss($term->name)));
386 }
387 $body .= "\n<br/><br/>". $form_values['title'];
388 // The URLs in the following are contructed as absolute addresses.
389 $body .= "\n<br/><br/>". t('In order to answer it you will first need to <a href="!url">login</a> to the site.', array('!url' => url('user', NULL, NULL, TRUE)));
390 $body .= "\n<br/>". t('Once logged in, you may proceed <a href="!url">directly to the question</a> to answer it.', array('!url' => url('faq_ask/answer/'. $node->nid, NULL, NULL, TRUE)));
391
392 $mail_sent = drupal_mail('expert-notify', $to, t('You have a question waiting'), $body, $from, $header);
393 if ($mail_sent) {
394 watchdog('FAQ_Ask', t('Expert notification email sent.') .' '. $to, WATCHDOG_NOTICE);
395 }
396 else {
397 $msg = t('Expert notification email failed for the "!cat" category.', array('!cat' => filter_xss($term->name)));
398 watchdog('FAQ_Ask', $msg .' '. $to, WATCHDOG_ERROR);
399 drupal_set_message($msg, 'notice');
400 }
401 }
402
403 // Quick detour to see if the block is enabled.
404 // Slight flaw here: There may be more than one theme enabled, but this just gets the first one,
405 // which may not be the current theme.
406 if (user_access('answer question')) {
407 $region = db_result(db_query_range("SELECT region FROM {blocks} WHERE module='faq_ask' AND delta=0 AND status=1", 0, 1));
408 }
409 else {
410 $region = NULL;
411 }
412 if ($region) {
413 switch ($region) {
414 case 'left':
415 $region_name = t('left side');
416 break;
417 case 'right':
418 $region_name = t('right side');
419 break;
420 case 'header':
421 $region_name = t('top');
422 break;
423 case 'footer':
424 $region_name = t('bottom');
425 break;
426 default:
427 $region_name = '';
428 break;
429 }
430 if ($region_name) {
431 $msg = t('It should appear in the list at the @region of the screen momentarily.', array('@region' => $region_name));
432 }
433 else {
434 $msg = t('It should appear in the list momentarily.');
435 }
436 }
437 else {
438 $msg = NULL;
439 }
440 drupal_set_message(t('Your question has been submitted.') .' '. $msg, 'notice');
441
442 drupal_goto('faq/'. $category);
443 }
444 }
445
446 /**
447 * Implementation of hook_form().
448 * This form allows the users to select the expert roles and to which categories the users in those roles are assigned.
449 * Note, the expert/category table attempts to use the least horizontal space,
450 * so it can "flip" based on whether there are more categories or experts.
451 */
452 function faq_ask_settings_form($op = NULL, $aid = NULL) {
453 $form = array();
454 // Set a basic message that will be unset once we pass the error checking.
455 $form['error'] = array('#value' => t('Errors were found, please correct them before proceeding.'), '#weight' => -10);
456
457 if (!variable_get('faq_use_categories', FALSE)) {
458 drupal_set_message(t('The Faq_Ask module requires that FAQ "Categorize questions." Please go to the <a href="!url">settings page</a> to configure this module.', array('!url' => url('admin/settings/faq/categories'))), 'error');
459 return $form;
460 }
461
462 // Get the list of vocabularies that apply to FAQ s.
463 $vocabs = taxonomy_get_vocabularies('faq');
464 if (count($vocabs) == 0) {
465 drupal_set_message(t('The Faq_Ask module requires that at least one vocabulary apply to the "faq" content type.'), 'error');
466 return $form;
467 }
468
469 // Get the admin's name.
470 $admin = ucwords(db_result(db_query('SELECT name FROM {users} WHERE uid=1')));
471
472 $form['notification'] = array(
473 '#type' => 'fieldset',
474 '#title' => t('Notifications'),
475 '#collapsible' => TRUE,
476 '#collapsed' => FALSE,
477 );
478
479 $form['notification']['faq_ask_notify'] = array(
480 '#type' => 'checkbox',
481 '#title' => t('Notify experts'),
482 '#description' => t('If this box is checked, the expert(s) for the question will be notified via email that a question awaits them. If you do not choose this option, the "Unanswered Questions" block will be the only way they will know they have questions to answer.'),
483 '#default_value' => variable_get('faq_ask_notify', 0),
484 );
485
486 $form['options'] = array(
487 '#type' => 'fieldset',
488 '#title' => t('Options'),
489 '#collapsible' => TRUE,
490 '#collapsed' => FALSE,
491 );
492
493 $form['options']['faq_ask_categorize'] = array(
494 '#type' => 'checkbox',
495 '#title' => t('Only expert can categorize'),
496 '#description' => t('If this box is checked, only an expert answering a question can add a category.'),
497 '#default_value' => variable_get('faq_ask_categorize', FALSE),
498 );
499
500 $give_options = array(
501 0 => t('Asker retains ownerhsip'),
502 1 => t('Anonymous questions reassigned to expert'),
503 2 => t('All questions reassigned to expert'),
504 );
505 $form['options']['faq_ask_expert_own'] = array(
506 '#type' => 'radios',
507 '#options' => $give_options,
508 '#title' => t('Give ownership to the expert'),
509 '#description' => t('This determines if questions will be reassigned to the expert when answered.'),
510 '#default_value' => variable_get('faq_ask_expert_own', 0),
511 );
512
513 $form['options']['faq_ask_suggest'] = array(
514 '#type' => 'checkbox',
515 '#title' => t('Allow "Suggest a category"'),
516 '#description' => t('If this box is checked, the person asking a question will have an option to enter a new category. It will have the default expert assigned to it until you return here to correct the assignment.'),
517 '#default_value' => variable_get('faq_ask_suggest', FALSE),
518 );
519
520 $form['options']['advice'] = array(
521 '#type' => 'fieldset',
522 '#title' => t('Advice'),
523 '#description' => t('This text will be shown at the bottom of the "Unanswered questions" block.'),
524 '#collapsible' => TRUE,
525 '#collapsed' => FALSE,
526 );
527
528 $form['options']['advice']['faq_ask_expert_advice'] = array(
529 '#type' => 'textarea',
530 '#title' => t('Advice for the expert'),
531 '#cols' => 60,
532 '#rows' => 1,
533 '#default_value' => variable_get('faq_ask_expert_advice', _faq_ask_advice_default('expert')),
534 );
535
536 $form['options']['advice']['faq_ask_admin_advice'] = array(
537 '#type' => 'textarea',
538 '#title' => t('Advice for an administrator/editor'),
539 '#cols' => 60,
540 '#rows' => 1,
541 '#default_value' => variable_get('faq_ask_admin_advice', _faq_ask_advice_default('admin')),
542 );
543
544 $form['options']['advice']['faq_ask_asker_advice'] = array(
545 '#type' => 'textarea',
546 '#title' => t('Advice for an administrator/editor'),
547 '#cols' => 60,
548 '#rows' => 1,
549 '#default_value' => variable_get('faq_ask_asker_advice', _faq_ask_advice_default('asker')),
550 );
551
552 $help_default = variable_get('faq_ask_help_text', _faq_ask_help_default());
553 $form['options']['faq_ask_help_text'] = array(
554 '#type' => 'textarea',
555 '#title' => t('Help text for the asker'),
556 '#cols' => 60,
557 '#rows' => drupal_strlen($help_default) / 60,
558 '#description' => t('This text will be shown at the top of the "Ask a Question" page.'),
559 '#default_value' => $help_default,
560 );
561
562 $title_opts = drupal_map_assoc(array(20, 30, 40, 50, 60, 70, 80, 90, 100, 128));
563 $form['options']['faq_ask_title_len'] = array(
564 '#type' => 'select',
565 '#options' => $title_opts,
566 '#title' => t('Question Box Length'),
567 '#description' => t('This sets the length of the question box (not the question itself). This affects only the FAQ_Ask module, not the FAQ display. The recommendation is to set it as wide as your theme will allow it. Note: If resizable textareas are enabled, this setting will have no effect.'),
568 '#default_value' => variable_get('faq_ask_title_len', 60),
569 );
570
571 $form['experts'] = array(
572 '#type' => 'fieldset',
573 '#title' => t('Experts'),
574 '#collapsible' => TRUE,
575 '#collapsed' => FALSE,
576 );
577
578 // Use list of vocabularies gotten above.
579 if (count($vocabs) == 1) {
580 // Single vocabulary, don't bother with a selection box, just set it.
581 $vid = key($vocabs);
582 variable_set('faq_ask_vocabularies', array($vid => $vid));
583 $vobj = $vocabs[$vid];
584 $free = $vobj->tags;
585 }
586 else {
587 // Multiple vocabs available.
588 $voc_list = array();
589 $def_vid = 0;
590 $free = FALSE;
591 foreach ($vocabs as $vid => $vobj) {
592 $voc_list[$vid] = $vobj->name;
593 if ($vobj->name == 'FAQ') {
594 $def_vid = $vid;
595 }
596 $free = $free || ($vobj->tags);
597 }
598 $form['experts']['faq_ask_vocabularies'] = array(
599 '#type' => 'select',
600 '#options' => $voc_list,
601 '#title' => t('Use these vocabularies'),
602 '#multiple' => TRUE,
603 '#default_value' => variable_get('faq_ask_vocabularies', $def_vid),
604 '#description' => t('Only the terms from the selected vocabularies will be included in the list below.')
605 .' '. t("Simply adding the 'FAQ' content type to a vocabulary will not make it eligible for experts; you must return to here to add it.")
606 .'<br/><big>'. t('If you select different vocabularies, you must save the configuration BEFORE selecting users below.') .'</big>',
607 );
608 } // End multiple vocabs.
609
610 if ($free) {
611 $msg = t('Free tagging vocabularies are not allowed for Faq_Ask.');
612 drupal_set_message($msg, 'error');
613 $form['experts']['error'] = array(
614 '#type' => 'item',
615 '#value' => '<big><big>'. $msg .'</big></big>'
616 );
617 return $form;
618 }
619
620 // Changed query and loop because it failed if 'answer' was the first perm in list.
621 // This should be faster any way.
622 $role_list = db_result_array(db_query("SELECT r.rid, r.name FROM {role} r JOIN {permission} p USING (rid) WHERE p.perm LIKE '%%answer question%%'"));
623 if (empty($role_list)) {
624 drupal_set_message(t('No roles with "answer question" permission were found; only !admin is currently eligible to be an expert. You may want to go to the <a href="!access">Access Control page</a> to update your permissions.', array('!access' => url('admin/user/access'), '!admin' => $admin)), 'warning');
625 }
626
627 // Get all terms associated with FAQ.
628 //$vocabs = variable_get('faq_ask_vocabularies', $def_vid);
629 $vocabs_array = array();
630 foreach ($vocabs as $vocab) {
631 $vocabs_array[$vocab->vid] = $vocab->vid;
632 }
633
634 $result = db_query('SELECT td.tid, td.name, td.description FROM {term_data} td WHERE td.vid IN ('. db_placeholders($vocabs_array) .') ORDER BY td.weight ASC, td.name ASC', $vocabs_array);
635 // $result = db_query("SELECT td.tid, td.name, td.description FROM {term_data} td WHERE td.vid IN (". db_placeholders($vocabs) .") ORDER BY td.weight ASC, td.name ASC", $vocabs);
636
637 $faq_terms = array();
638 while ($term = db_fetch_array($result)) {
639 // Show term hierarchy?
640 $term_name = /* str_repeat('--', $term['depth']) . */ $term['name'];
641 if (substr($term['description'], 0, 9) == 'suggested') {
642 $faq_terms[$term['tid']] = $term_name .'<br/>--<small>'. strip_tags($term['description']) .'</small>';
643 }
644 else {
645 $faq_terms[$term['tid']] = $term_name;
646 }
647 }
648 if (count($faq_terms) == 0) {
649 drupal_set_message(t('No vocabularies or terms were found for the "faq" content type . Please go to the <a href="!access">Categories page</a> to update your vocabulary.', array('!access' => url('admin/content/taxonomy'))), 'warning');
650 }
651
652 // Get all users associated with the roles.
653 $faq_expert_names = array();
654 // User/1 typically is not assigned roles, but should be in the list.
655 $faq_expert_names[1] = $admin;
656
657 $rids = variable_get('faq_expert_role', array());
658 if (!empty($rids)) {
659 if (in_array(DRUPAL_AUTHENTICATED_RID, $rids)) {
660 // Authenticated users may be experts, so get all active users.
661 // No other roles matter.
662 $result = db_query("SELECT u.uid, u.name FROM {users} u WHERE status=1");
663 }
664 else {
665 // Only specific roles may be experts.
666 $result = db_query("SELECT DISTINCT(u.uid), u.name FROM {users_roles} ur JOIN {users} u USING (uid) WHERE ur.rid IN (". db_placeholders($rids) .")", $rids);
667 }
668 while ($user = db_fetch_array($result)) {
669 if ($user['uid'] != 1) {
670 $faq_expert_names[$user['uid']] = ucwords($user['name']);
671 }
672 }
673
674 // Put them in alphabetical order.
675 asort($faq_expert_names);
676 }
677
678 if (!empty($role_list)) {
679 $form['experts']['faq_expert_role'] = array(
680 '#type' => 'select',
681 '#title' => t('Expert Roles'),
682 '#options' => $role_list,
683 '#multiple' => TRUE,
684 '#default_value' => variable_get('faq_expert_role', '2'),
685 '#description' => t('User 1 (@admin) will always be in the list, regardless of roles.', array('@admin' => $admin)) .'<br/><big>'. t('If you select different roles, you must save the configuration BEFORE selecting users below.') .'</big>',
686 );
687 }
688
689 $more_experts_than_terms = count($faq_expert_names) > count($faq_terms);
690
691 // If there is only one eligible expert, we might as well preset all categories.
692 $expert_msg = NULL;
693 $only_one_expert = (count($faq_expert_names) == 1);
694
695 $count = 0;
696 if ($more_experts_than_terms) {
697 // Experts go down the left; terms go across the top.
698 $top = NULL;
699 if ($only_one_expert) {
700 $top .= '<p>'. t('Note: Even though the check boxes below are checked, you must still click the "Save configuration" button to save the expert settings.') .'</p>';
701 }
702 $top .= '<table id="faq_experts"><tr><th> </th><th>'. implode('</th><th>', $faq_terms) .'</th></tr>';
703 if ($only_one_expert) {
704 $top .= '<tr><td colspan="100">'. t('Note: Even though the check boxes below are checked, you must still click the "Save configuration" button to save the expert settings.') .'</td></tr>';
705 }
706 foreach ($faq_expert_names as $uid => $name) {
707 ++$count;
708 $class = $count & 1 ? 'odd' : 'even';
709 $left = '<tr class="'. $class .'"><td><strong>'. $name .'</strong></td>';
710 foreach ($faq_terms as $tid => $term_name) {
711 $box_name = 'expert_'. $uid .'_'. $tid;
712 $form['experts'][$box_name] = array(
713 '#type' => 'checkbox',
714 '#default_value' => $only_one_expert,
715 '#prefix' => $top . $left .'<td align="center">',
716 '#suffix' => '</td>',
717 );
718 $top = NULL;
719 $left = NULL;
720 }
721 $form['experts'][$box_name]['#suffix'] .= '</tr>';
722 }
723 $form['experts'][$box_name]['#suffix'] .= '</table>';
724 }
725 else {
726 // Experts go across the top; terms go down the left.
727 $top = NULL;
728 if ($only_one_expert) {
729 $top .= '<p>'. t('Note: Even though the check boxes below are checked, you must still click the "Save configuration" button to save the expert settings.') .'</p>';
730 }
731 $top .= '<table id="faq_experts"><tr><th> </th><th>'. implode('</th><th>', $faq_expert_names) .'</th></tr>';
732 foreach ($faq_terms as $tid => $term_name) {
733 ++$count;
734 $class = $count & 1 ? 'odd' : 'even';
735 $left = '<tr class="'. $class .'"><td><strong>'. $term_name .'</strong></td>';
736 foreach ($faq_expert_names as $uid => $name) {
737 $box_name = 'expert_'. $uid .'_'. $tid;
738 $form['experts'][$box_name] = array(
739 '#type' => 'checkbox',
740 '#default_value' => $only_one_expert,
741 '#prefix' => $top . $left .'<td align="center">',
742 '#suffix' => '</td>',
743 );
744 $top = NULL;
745 $left = NULL;
746 }
747 $form['experts'][$box_name]['#suffix'] .= '</tr>';
748 }
749 $form['experts'][$box_name]['#suffix'] .= '</table>';
750 }
751
752 $form['experts'][$box_name]['#suffix'] .= t('Those who will be answering questions will need both "answer question" and "edit faq" permissions.');
753
754 $result = db_query("SELECT * FROM {faq_expert}");
755 while ($expert = db_fetch_array($result)) {
756 $box_name = 'expert_'. $expert['uid'] .'_'. $expert['tid'];
757 if (isset($form['experts'][$box_name])) { // Might not be present any more.
758 $form['experts'][$box_name]['#default_value'] = TRUE;
759 }
760 else {
761 // Expert 0 means default expert; overlook it.
762 if ($expert['tid'] != 0) {
763 drupal_set_message(t("!name doesn't exist. If you have just changed your role selections this may be okay.", array('!name' => $box_name)), 'warning');
764 }
765 }
766 }
767
768 if ($only_one_expert) {
769 // Create a form value to set default expert to admin.
770 $form['experts']['faq_ask_default_expert'] = array(
771 '#type' => 'value',
772 '#value' => 1,
773 );
774 }
775 else {
776 $form['experts']['faq_ask_default_expert'] = array(
777 '#type' => 'select',
778 '#options' => $faq_expert_names,
779 '#multiple' => FALSE,
780 '#title' => t('Default expert'),
781 '#description' => t('The selected user will be assigned as the expert for all terms that are added to the selected vocabularies until you return to this page and update it.'),
782 '#default_value' => variable_get('faq_ask_default_expert', 1),
783 );
784 }
785
786 // Get rid of generic error message.
787 unset($form['error']);
788 $form['submit'] = array(
789 '#type' => 'submit',
790 '#value' => t('Save configuration'),
791 '#weight' => 5,
792 );
793
794 return $form;
795 }
796
797 /**
798 * Implementation of hook_form_submit.
799 * It saves the expert roles that were selected, then rebuilds the expert/category table.
800 */
801 function faq_ask_settings_form_submit($form_id, $form_values) {
802 // Save the simple stuff.
803 variable_set('faq_expert_role', $form_values['faq_expert_role']);
804 variable_set('faq_ask_vocabularies', $form_values['faq_ask_vocabularies']);
805 variable_set('faq_ask_title_len', $form_values['faq_ask_title_len']);
806 variable_set('faq_ask_categorize', $form_values['faq_ask_categorize']);
807 variable_set('faq_ask_expert_own', $form_values['faq_ask_expert_own']);
808 variable_set('faq_ask_suggest', $form_values['faq_ask_suggest']);
809 variable_set('faq_ask_notify', $form_values['faq_ask_notify']);
810 variable_set('faq_ask_default_expert', $form_values['faq_ask_default_expert']);
811 variable_set('faq_ask_expert_advice', $form_values['faq_ask_expert_advice']);
812 variable_set('faq_ask_help_text', $form_values['faq_ask_help_text']);
813 variable_set('faq_ask_admin_advice', $form_values['faq_ask_admin_advice']);
814 variable_set('faq_ask_asker_advice', $form_values['faq_ask_asker_advice']);
815
816 // Get all the selected expert/category options.
817 // First, we'll include the default expert for tid=0.
818 $values = array('('. $form_values['faq_ask_default_expert'] .', 0)');
819 foreach ($form_values as $name => $value) {
820 if (substr($name, 0, 7) == 'expert_') {
821 list($junk, $uid, $tid) = explode('_', $name);
822 if ($value) {
823 $values[] = '('. $uid .', '. $tid .')';
824 }
825 }
826 }
827 $vals = implode(', ', $values);
828
829 // Delete the current values and save the new ones.
830 if (!empty($values)) {
831 db_query('TRUNCATE {faq_expert}');
832 db_query("INSERT INTO {faq_expert} (uid, tid) VALUES %s", $vals);
833 }
834
835 drupal_set_message(t('Configuration has been updated.'), 'notice');
836 }
837
838 /**
839 * This function is called when an expert selects a question to answer.
840 * It changes the status option to "published" then goes to the regular FAQ edit function.
841 */
842 function faq_ask_answer($nid) {
843 $node = node_load(array('nid' => $nid));
844 $reassign_opt = variable_get('faq_ask_expert_own', 0);
845 // Check if we need to reassign to the expert.
846 switch ($reassign_opt) {
847 case 0: // Do not reassign.
848 break;
849
850 case 1: // Reassign if anonymous.
851 if ($node->uid == 0) {
852 faq_ask_reassign($node);
853 }
854 break;
855
856 case 2: // Always reassign.
857 faq_ask_reassign($node);
858 break;
859 }
860 // Change the status to published and the user id, if needed.
861 db_query("UPDATE {node} SET status=1, uid=%d WHERE nid=%d", $node->uid, $node->nid);
862
863 // Need to invoke node/##/edit.
864 drupal_goto('node/'. $nid .'/edit');
865 }
866
867 /**
868 * Reassign the node to the current user and display a message.
869 */
870 function faq_ask_reassign(&$node) {
871 global $user;
872 $node->uid = $user->uid;
873 drupal_set_message(t('This question is being assigned to !user.', array('!user' => theme('username', $user))));
874 }
875
876 /**
877 * Implementation of hook_taxonomy.
878 * @param:
879 * op: 'insert', 'update, 'delete'
880 * type: 'term', 'vocabulary'
881 * array: depends on other two.
882 */
883 function faq_ask_taxonomy($op, $type, $array = NULL) {
884 global $user;
885 $default_expert = variable_get('faq_ask_default_expert', 1);
886 $my_vocs = variable_get('faq_ask_vocabularies', array());
887 $vid = $array['vid'];
888 // See if it's one of our vocabularies.
889 $our_vocab = in_array($vid, $my_vocs);
890
891 switch ($op) {
892 case 'insert':
893 switch ($type) {
894 case 'term':
895 // term: set default expert.
896 if ($our_vocab) {
897 $insert = db_query("INSERT INTO {faq_expert} (uid, tid) VALUES (%d, %d)", $default_expert, $array['tid']);
898 if ($insert === FALSE) {
899 drupal_set_message(t('Attempt to assign expert failed.'), 'error');
900 }
901 else {
902 drupal_set_message(t('Assigned expert @expert to @name (@tid).', array('@expert' => $default_expert, '@name' => $array['name'], '@tid' => $array['tid'])), 'notice');
903 }
904 }
905 break;
906
907 case 'vocabulary':
908 // New vocabulary created. It will not show on the ask page until the user
909 // goes to the settings page, so we don't need to do anything.
910 break;
911 } // End insert switch type.
912 break;
913
914 case 'delete':
915 switch ($type) {
916 case 'term':
917 // Delete term: remove experts.
918 if ($our_vocab) {
919 _faq_ask_delete_expert($array['tid'], $array['name']);
920 }
921 break;
922
923 case 'vocabulary':
924 // Each term gets deleted first, so all we have to do is remove it from our vocab list.
925 if ($our_vocab) {
926 _faq_ask_delete_vocabulary($vid, $array, $my_vocs);
927 }
928 break;
929 } // End delete switch type.
930 break;
931
932 case 'update':
933 // Two cases for vocabulary:
934 // 1) FAQ is added to the vocab. -- see insert comment.
935 // 2) FAQ is removed from the vocab. -- need to delete all experts for all terms and remove from voc list?
936 // $array['nodes'] contains an array of content types for the vocab.
937 switch ($type) {
938 case 'term':
939 // Term update: nothing to do.
940 break;
941
942 case 'vocabulary':
943 if (in_array('faq', $array['nodes'])) {
944 // If it's there now, we're done.
945 break;
946 }
947 // Not there now, so we need to see if it was.
948 if ($our_vocab) {
949 $tree = taxonomy_get_tree($vid);
950 foreach ($tree as $term) {
951 $my_tid = $term->tid;
952 $my_tname = $term->name;
953 _faq_ask_delete_expert($my_tid, $my_tname);
954 } // End foreach tree.
955 _faq_ask_delete_vocabulary($vid, $array, $my_vocs);
956 }
957
958 break;
959 } // End update switch type.
960 break;
961
962 default:
963 drupal_set_message(t('Faq_ask_taxonomy: Unknown op (@op) encountered', array('@op' => $op)), 'warning');
964
965 } // End switch $op
966 }
967
968 /**
969 * Helper function to delete a vocabulary.
970 * @param.
971 * $vid - the taxonomy vocabulary id.
972 * $array - the array provided in hook_taxonomy.
973 * $my_vocs - the array of faq_ask vocabularies.
974 *
975 * @return: none.
976 */
977 function _faq_ask_delete_vocabulary($vid, $array, $my_vocs) {
978 global $user;
979 $name = $array['name'];
980 $uname = $user->name;
981 drupal_set_message("Vocabulary '$name' is being removed from the Faq_Ask list.", 'notice');
982 watchdog('Faq_Ask', "Vocabulary $name was deleted from Faq_Ask by $uname.", WATCHDOG-NOTICE);
983 unset($my_vocs[$vid]);
984 variable_set('faq_ask_vocabularies', $my_vocs);
985 }
986
987 /**
988 * Helper function to delete experts.
989 * @param.
990 * $tid - the taxonomy term id for the experts.
991 *
992 * @return: none.
993 */
994 function _faq_ask_delete_expert($tid, $name=NULL) {
995 $delete = db_query("DELETE FROM {faq_expert} WHERE tid=%d", $tid);
996 if ($delete === FALSE) {
997 drupal_set_message(t('Attempt to delete expert failed.'), 'error');
998 }
999 else {
1000 drupal_set_message(t("Deleted experts for '@name'.", array('@name' => $name)), 'notice');
1001 }
1002 }
1003
1004 /**
1005 * Special function to verify new module dependencies.
1006 */
1007 function _faq_ask_check_dependencies() {
1008 // TODO: Rip this code out after a few months.
1009 $is_it_here = module_exists('helpers_database');
1010 if (!$is_it_here) {
1011 drupal_set_message(t('The Helpers_database module is not present or not enabled. It is required for Faq_Ask. Faq_Ask has been disabled to prevent a site outage.'), 'error');
1012 module_disable(array('faq_ask'));
1013 }
1014 return $is_it_here;
1015 }
1016
1017 /**
1018 * Special function to get the advice text default.
1019 */
1020 function _faq_ask_advice_default($type = 'expert') {
1021 switch ($type) {
1022 case 'expert':
1023 return t('If you select a question, you must answer it.');
1024
1025 case 'admin':
1026 return t('You are allowed to edit unanswered questions.');
1027
1028 case 'asker':
1029 return t('You may edit your own questions until they are answered.');
1030 }
1031 }
1032
1033 /**
1034 * Special function to get the help text default.
1035 */
1036 function _faq_ask_help_default() {
1037 return t("Add a question for our expert to answer. After being answered, your question and the answer will be displayed in the FAQ pages. If the question will not fit in the box below, please try to rephrase it.");
1038 }
1039 /**
1040 * Implementation of hook_block().
1041 * This creates and populates the "unanswered questions" block.
1042 */
1043 function faq_ask_block($op = 'list', $delta = 0, $edit = array()) {
1044 global $user;
1045 switch ($op) {
1046 case 'list':
1047 $blocks[0]['info'] = t('Unanswered Questions');
1048 $blocks[1]['info'] = t('Ask a Question');
1049 return $blocks;
1050
1051 case 'view':
1052 switch ($delta) {
1053 case 0:
1054 // Unanswered Questions.
1055 // First, let's deal with a change in module dependencies.
1056 if (_faq_ask_check_dependencies()) {
1057 $block['content'] = _faq_ask_list_unanswered(variable_get('faq_unanswered_count', 3));
1058 }
1059 break;
1060
1061 case 1:
1062 // Ask a question block.
1063 $block['content'] = faq_ask_page(NULL);
1064 break;
1065
1066 } // end switch($delta).
1067 return $block;
1068
1069 case 'configure':
1070 switch ($delta) {
1071 case 0:
1072 // Unanswered Questions.
1073 $form['faq_unanswered_count'] = array(
1074 '#type' => 'select',
1075 '#title' => t('Number of questions to show'),
1076 '#description' => t("This controls the number of questions that appear in the 'Unanswered Questions' block."),
1077 '#options' => array(1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, 10 => 10, 15 => 15, 20 => 20, 25 => 25, 50 => 50, 100 => 100),
1078 '#default_value' => variable_get('faq_unanswered_count', 3),
1079 );
1080 break;
1081 } // end switch($delta)
1082
1083 return $form;
1084
1085 case 'save':
1086 switch ($delta) {
1087 case 0:
1088 variable_set('faq_unanswered_count', $edit['faq_unanswered_count']);
1089 break;
1090
1091 } // end switch($delta)
1092 return;
1093 } // end switch($op)
1094 }
1095
1096 /**
1097 * This is the code to select the Unanswered Questions for the block.
1098 * It is also used by the "more" page by setting a very high limit.
1099 */
1100 function _faq_ask_list_unanswered($limit) {
1101 global $user;
1102 // Bounce anonymous users.
1103 if ($user->uid == 0) {
1104 return NULL;
1105 }
1106
1107 // What permissions does this user have?
1108 $can_edit = user_access('administer faq') || user_access('administer nodes');
1109 $is_expert = user_access('answer question');
1110 // $only_own = user_access('edit own faq');
1111
1112 $mode = 'edit';
1113 $output = $extra_msg = NULL;
1114 // A high limit means we are doing the "more" page.
1115