/[drupal]/contributions/sandbox/deekayen/modules/dpistudy/dpistudy.module
ViewVC logotype

Contents of /contributions/sandbox/deekayen/modules/dpistudy/dpistudy.module

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


Revision 1.12 - (show annotations) (download) (as text)
Mon Apr 28 05:36:13 2008 UTC (19 months ago) by deekayen
Branch: MAIN
CVS Tags: HEAD
Changes since 1.11: +1 -1 lines
File MIME type: text/x-php
the report pager was showing extra pages that weren't needed because it was counting all the keys in the DB instead of the keys that were actually used when people hit the site
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Distribution method for the Digital Propensity Index Questionanire
7 * and the instructional intervention for David Kent Norman's dissertation
8 *
9 * @author David Kent Norman
10 * @link http://deekayen.net
11 * @link http://dpistudy.com
12 * @copyright Copyright 2008 David Kent Norman
13 */
14
15 define('DPISTUDY_ALT_IRB_URL', 'http://education.ucf.edu/insttech/');
16 define('DPISTUDY_NUM_QUESTIONS', 6);
17
18 /**
19 * Implementation of hook_perm().
20 *
21 * @return array
22 */
23 function dpistudy_perm() {
24 return array('access dpistudy');
25 }
26
27 /**
28 * Implementation of hook_init().
29 *
30 * Adds the timer javascript to the output and start the timer.
31 */
32 function dpistudy_init() {
33 if (arg(0) != 'admin') {
34 _dpistudy_js_timer();
35 _dpistudy_js_timer_start();
36 }
37 }
38
39 /**
40 * Implementation of hook_menu().
41 *
42 * @return array
43 */
44 function dpistudy_menu() {
45 $items = array();
46
47 $items['dpistudy'] = array(
48 'page callback' => 'dpistudy_hub',
49 'page arguments' => array(),
50 'access callback' => 'user_access',
51 'access arguments' => array('access dpistudy'),
52 'type' => MENU_CALLBACK,
53 'title' => 'Get your Digital Propensity Index score!',
54 'title callback' => 't',
55 'title arguments' => array()
56 );
57 $items['dpistudy/dpi'] = array(
58 'page callback' => 'drupal_get_form',
59 'page arguments' => array('dpistudy_dpi_questionnaire_form'),
60 'access callback' => 'user_access',
61 'access arguments' => array('access dpistudy'),
62 'title' => 'Digital Propensity Index Questionnaire',
63 'title callback' => 't',
64 'title arguments' => array(),
65 'type' => MENU_CALLBACK
66 );
67 $items['dpistudy/instruction'] = array(
68 'page callback' => 'drupal_get_form',
69 'page arguments' => array('dpistudy_instruction_form'),
70 'access callback' => 'user_access',
71 'access arguments' => array('access dpistudy'),
72 'title' => 'Just @num more!', // not ideal, but this or arguments have to be passed for the callback to be executed
73 'title callback' => 'dpistudy_instruction_title',
74 'type' => MENU_CALLBACK
75 );
76 $items['dpistudy/score'] = array(
77 'page callback' => 'dpistudy_score',
78 'page arguments' => array(),
79 'access callback' => 'user_access',
80 'access arguments' => array('access dpistudy'),
81 'title' => 'Your Digital Propensity Index score!',
82 'title callback' => 't',
83 'title arguments' => array(),
84 'type' => MENU_CALLBACK
85 );
86 $items['admin/settings/dpistudy'] = array(
87 'page callback' => 'drupal_get_form',
88 'page arguments' => array('dpistudy_admin_settings'),
89 'access callback' => 'user_access',
90 'access arguments' => array('administer site configuration'),
91 'description' => 'Toggle features of the DPI questionnaire.',
92 'title' => 'DPI Study',
93 'title callback' => 't',
94 'title arguments' => array(),
95 'type' => MENU_NORMAL_ITEM
96 );
97 $items['admin/settings/dpistudy/settings'] = array(
98 'page callback' => 'drupal_get_form',
99 'page arguments' => array('dpistudy_admin_settings'),
100 'access callback' => 'user_access',
101 'access arguments' => array('administer site configuration'),
102 'title' => 'Settings',
103 'title callback' => 't',
104 'title arguments' => array(),
105 'type' => MENU_DEFAULT_LOCAL_TASK,
106 'weight' => 0
107 );
108 $items['admin/settings/dpistudy/keys'] = array(
109 'page callback' => 'dpistudy_admin_settings_invite_keys',
110 'page arguments' => array(),
111 'access callback' => 'user_access',
112 'access arguments' => array('administer site configuration'),
113 'title' => 'Invite Keys',
114 'title callback' => 't',
115 'title arguments' => array(),
116 'type' => MENU_LOCAL_TASK,
117 'weight' => 3
118 );
119 $items['admin/settings/dpistudy/keys/delete'] = array(
120 'page callback' => 'dpistudy_admin_settings_invite_keys_delete',
121 'page arguments' => array(5),
122 'access callback' => 'user_access',
123 'access arguments' => array('administer site configuration'),
124 'title' => 'Delete a key',
125 'title callback' => 't',
126 'title arguments' => array(),
127 'type' => MENU_LOCAL_TASK,
128 'weight' => 3
129 );
130 $items['admin/settings/dpistudy/questions'] = array(
131 'page callback' => 'dpistudy_admin_settings_list_questions',
132 'page arguments' => array(),
133 'access callback' => 'user_access',
134 'access arguments' => array('administer site configuration'),
135 'description' => 'Manage questions on the questionnaire.',
136 'title' => 'Questionnaire',
137 'title callback' => 't',
138 'title arguments' => array(),
139 'type' => MENU_LOCAL_TASK,
140 'weight' => 5
141 );
142 $items['admin/settings/dpistudy/questions/list'] = array(
143 'page callback' => 'dpistudy_admin_settings_list_questions',
144 'page arguments' => array(),
145 'access callback' => 'user_access',
146 'access arguments' => array('administer site configuration'),
147 'description' => 'Manage questions on the questionnaire.',
148 'title' => 'List questions',
149 'title callback' => 't',
150 'title arguments' => array(),
151 'type' => MENU_DEFAULT_LOCAL_TASK,
152 'weight' => 0
153 );
154 $items['admin/settings/dpistudy/questions/add'] = array(
155 'page callback' => 'drupal_get_form',
156 'page arguments' => array('dpistudy_admin_settings_add_question_form'),
157 'access callback' => 'user_access',
158 'access arguments' => array('administer site configuration'),
159 'description' => 'Adds a question to the questionnaire.',
160 'title' => 'Add question',
161 'title callback' => 't',
162 'title arguments' => array(),
163 'type' => MENU_LOCAL_TASK,
164 'weight' => 5
165 );
166 $items['admin/reports/dpistudy'] = array(
167 'page callback' => 'dpistudy_report',
168 'page arguments' => array(),
169 'access callback' => 'user_access',
170 'access arguments' => array('administer site configuration'),
171 'description' => 'View the breakdown of responses for each invite key and copy quick links for keyed visitors.',
172 'title' => 'DPI Study',
173 'title callback' => 't',
174 'title arguments' => array(),
175 'type' => MENU_NORMAL_ITEM
176 );
177 $items['admin/reports/dpistudy/visits'] = array(
178 'page callback' => 'dpistudy_report',
179 'page arguments' => array(),
180 'access callback' => 'user_access',
181 'access arguments' => array('administer site configuration'),
182 'title' => 'Visits',
183 'title callback' => 't',
184 'title arguments' => array(),
185 'type' => MENU_DEFAULT_LOCAL_TASK,
186 'weight' => 0
187 );
188 $items['admin/reports/dpistudy/export'] = array(
189 'page callback' => 'dpistudy_csv_export',
190 'page arguments' => array(),
191 'access callback' => 'user_access',
192 'access arguments' => array('administer site configuration'),
193 'description' => 'Export survey results to a spreadsheet.',
194 'title' => 'Export',
195 'title callback' => 't',
196 'title arguments' => array(),
197 'type' => MENU_LOCAL_TASK,
198 'weight' => 10
199 );
200 return $items;
201 }
202
203 /**
204 * Enter description here...
205 *
206 * @param string $title
207 */
208 function dpistudy_instruction_title($title) {
209 if ($_SESSION['instruction_number'] == 6) {
210 return t('Last one!');
211 }
212 else {
213 return t($title, array('@num' => (empty($_SESSION['instruction_number']) ? DPISTUDY_NUM_QUESTIONS - 1 : DPISTUDY_NUM_QUESTIONS - $_SESSION['instruction_number'])));
214 }
215 }
216
217 /**
218 * @return array
219 */
220 function dpistudy_admin_settings() {
221 $form = array();
222 $form['dpistudy_add_form_num_questions'] = array(
223 '#type' => 'textfield',
224 '#title' => t('Number of answers on add question form'),
225 '#size' => 4,
226 '#default_value' => variable_get('dpistudy_add_form_num_questions', 5)
227 );
228 $form['consent'] = array(
229 '#type' => 'fieldset',
230 '#title' => t('Consent form'),
231 '#collapsible' => true,
232 '#collapsed' => true
233 );
234 $form['consent']['dpistudy_irb_consent_active'] = array(
235 '#type' => 'radios',
236 '#options' => array('disabled', 'enabled'),
237 '#title' => t('Toggle IRB consent screen'),
238 '#default_value' => variable_get('dpistudy_irb_consent_active', 0),
239 '#description' => t('Enabling the IRB form will require people to view .')
240 );
241 $form['consent']['dpistudy_irb_text'] = array(
242 '#type' => 'textarea',
243 '#rows' => 25,
244 '#title' => t('Text to output on the IRB consent form.'),
245 '#default_value' => variable_get('dpistudy_irb_text', ''),
246 '#description' => t('HTML is allowed.')
247 );
248 $form['dpistudy_score_screen'] = array(
249 '#type' => 'textarea',
250 '#rows' => 10,
251 '#title' => t('Score screen text'),
252 '#description' => t('Text to output on the final screen where the DPI score is presented. Use @score as the placeholder for the numerical score. HTML is allowed.'),
253 '#default_value' => variable_get('dpistudy_score_screen', ''),
254 );
255 $form['dpistudy_alt_irb_url'] = array(
256 '#type' => 'textfield',
257 '#title' => t('Alternate IRB URL'),
258 '#default_value' => variable_get('dpistudy_alt_irb_url', DPISTUDY_ALT_IRB_URL),
259 '#description' => t('Alternate URL to link users to under the "I Consent" button on the IRB consent form.')
260 );
261 return system_settings_form($form);
262 }
263
264 /**
265 * Handles new key adding and displays current invite keys
266 *
267 * @return string
268 */
269 function dpistudy_admin_settings_invite_keys() {
270 $headers = array(
271 array('data' => t('Invite Key'), 'field' => 'invite_key', 'sort' => 'asc'),
272 array('data' => t('Notes'), 'field' => 'notes'),
273 array('data' => t('Action'))
274 );
275
276 $sql = 'SELECT invite_key, notes FROM {dpistudy_keys}';
277 $sql .= tablesort_sql($headers);
278
279 $count = 'SELECT COUNT(*) FROM {dpistudy_keys}';
280 $result = pager_query($sql, 15, 0, $count);
281
282 while ($key = db_fetch_object($result)) {
283 $rows[] = array($key->invite_key, $key->notes, l(t('delete'), 'admin/settings/dpistudy/keys/delete/' . $key->invite_key));
284 }
285
286 $pager = theme('pager', NULL, 15);
287
288 if (!empty($pager)) {
289 $rows[] = array(array('data' => $pager, 'colspan' => 3));
290 }
291
292 $output = theme('table', $headers, $rows);
293 return $output . drupal_get_form('dpistudy_admin_settings_invite_keys_form');
294 }
295
296 /**
297 * Handles new key removing current invite keys
298 *
299 * @return string
300 */
301 function dpistudy_admin_settings_invite_keys_delete($key) {
302 db_query("DELETE FROM {dpistudy_keys} WHERE invite_key = '%s'", $key);
303 drupal_set_message(t('!key deleted.', array('!key' => $key)), 'status');
304 drupal_goto('admin/settings/dpistudy/keys');
305 }
306
307 /**
308 * @return array
309 */
310 function dpistudy_admin_settings_invite_keys_form() {
311 $form = array();
312 $form['add_key'] = array(
313 '#type' => 'fieldset',
314 '#title' => t('Add new invite key'),
315 '#collapsible' => true,
316 '#collapsed' => true
317 );
318 $form['add_key']['dpistudy_new_invite_key'] = array(
319 '#type' => 'textfield',
320 '#title' => t('New invite key'),
321 '#size' => 4,
322 '#default_value' => '',
323 '#required' => true,
324 '#maxlength' => 50,
325 '#description' => t('Entry can be alphanumeric.')
326 );
327 $form['add_key']['dpistudy_new_invite_key_notes'] = array(
328 '#type' => 'textfield',
329 '#title' => t('Key notes'),
330 '#description' => t('Notes on the audience to be associated with the key.'),
331 '#maxlength' => 255,
332 '#default_value' => ''
333 );
334 $form['add_key']['submit'] = array(
335 '#type' => 'submit',
336 '#value' => t('Add key')
337 );
338 return $form;
339 }
340
341 /**
342 * Validate the form submission from dpistudy_admin_settings_invite_keys_form()
343 *
344 * @param array $form
345 * @param array $form_state
346 */
347 function dpistudy_admin_settings_invite_keys_form_validate($form, &$form_state) {
348 if ($form_state['values']['dpistudy_new_invite_key'] == '') {
349 form_set_error('dpistudy_new_invite_key', t('You must add some text to the key value.'));
350 }
351 $existing = db_result(db_query("SELECT COUNT(*) FROM {dpistudy_keys} WHERE invite_key = '%s'", $form_state['values']['dpistudy_new_invite_key']));
352 if ($existing > 0) {
353 form_set_error('dpistudy_new_invite_key', t('"!key" is already a key! You must delete the old one first.', array('!key' => $form_state['values']['dpistudy_new_invite_key'])));
354 }
355 }
356
357 /**
358 * Process the form submission from dpistudy_admin_settings_invite_keys_form()
359 *
360 * @param array $form
361 * @param array $form_state
362 */
363 function dpistudy_admin_settings_invite_keys_form_submit($form, &$form_state) {
364 db_query("INSERT INTO {dpistudy_keys} (invite_key, notes) VALUES ('%s', '%s')", $form_state['values']['dpistudy_new_invite_key'], $form_state['values']['dpistudy_new_invite_key_notes']);
365 drupal_set_message(t('Invite key "!key" added', array('!key' => $form_state['values']['dpistudy_new_invite_key'])));
366 cache_clear_all('dpistudy-', 'cache', TRUE);
367 }
368
369 /**
370 * Lists questions on the questionnaire and allows weight adjustment
371 * active/deactivate
372 *
373 * @return string
374 */
375 function dpistudy_admin_settings_list_questions() {
376 $headers = array(
377 array('data' => t('Weight'), 'field' => 'weight'),
378 array('data' => t('Title'), 'field' => 'title')
379 );
380
381 $sql = 'SELECT pkid, weight, title FROM {dpistudy_questions} GROUP BY weight, title';
382 $sql .= tablesort_sql($headers);
383
384 $count = 'SELECT COUNT(*) FROM {dpistudy_questions}';
385 $result = pager_query($sql, 50, 0, $count);
386
387 while ($question = db_fetch_object($result)) {
388 $weight = $question->weight;
389 // $weight .= drupal_render(array(['my_elements'][$delta]['weight']['#attributes']['class'] = 'my-elements-weight'));
390
391 $title = l($question->title, "admin/settings/dpistudy/question/view/{$question->pkid}", array('attributes' => array('title' => t('View detailed question information'))));
392 $rows[] = array(
393 'data' => array($weight, $title),
394 'class' => 'draggable'
395 );
396 }
397
398 $pager = theme('pager', NULL, 50);
399
400 if (!empty($pager)) {
401 $rows[] = array(array('data' => $pager, 'colspan' => 2));
402 }
403
404 $output = theme('table', $headers, $rows, array('id' => 'dpistudy-questions'));
405 return $output;
406 }
407
408 /**
409 * Form for modifying the content of the questionnaire
410 *
411 * @param integer $pkid
412 * @param integer $weight
413 * @return array
414 */
415 function dpistudy_admin_settings_list_questions_weight($pkid, $weight = 0) {
416 $form = array();
417 $form["question_{$pkid}_weight"] = array(
418 '#type' => 'select',
419 '#title' => '',
420 '#default_value' => $weight,
421 '#options' => drupal_map_assoc(range(-50, 50))
422 );
423 return $form;
424 }
425
426 /**
427 * Form for modifying the content of the questionnaire
428 *
429 * @return array
430 */
431 function dpistudy_admin_settings_list_questions_form() {
432 $form = array();
433 $form['submit'] = array(
434 '#type' => 'submit',
435 '#value' => t('Update questions')
436 );
437 return $form;
438 }
439
440 /**
441 * Form to add a question to the questionnaire.
442 * Defaults to 10 question answers.
443 *
444 * @todo de-hardcode the question type
445 * @return array
446 */
447 function dpistudy_admin_settings_add_question_form() {
448 $form = array();
449 $form['question_type'] = array(
450 '#type' => 'radios',
451 '#options' => array('Radio buttons', 'Checkbox'),
452 '#title' => t('Question type'),
453 '#description' => t('Select radio buttons for a single response per question or checkbox for multiple answers to the question. Currently, only radio buttons are supported.'),
454 '#weight' => 0,
455 // currently only radio is supported
456 '#default_value' => 0,
457 '#disabled' => true
458 );
459 $form['question_title'] = array(
460 '#type' => 'textfield',
461 '#title' => t('Question title'),
462 '#description' => t('Note: A colon is automatically appended to the question text.'),
463 '#weight' => 1,
464 '#maxlength' => 250
465 );
466 $form['answers'] = array(
467 '#type' => 'fieldset',
468 '#title' => t('Answers'),
469 '#collapsible' => true,
470 '#collapsed' => false,
471 '#weight' => 2
472 );
473 for ($i=1, $j = variable_get('dpistudy_add_form_num_questions', 5); $i <= $j; $i++) {
474 $form['answers']['answer'][$i] = array(
475 '#type' => 'fieldset',
476 '#collapsible' => false,
477 '#collapsed' => false
478 );
479 $form['answers']['answer'][$i][$i] = array(
480 '#type' => 'textfield',
481 '#title' => t('Answer %i', array('%i' => $i)),
482 '#weight' => $i,
483 '#maxlength' => 250
484 );
485 $form['answers']['answer'][$i]["{$i}_weight"] = array(
486 '#type' => 'select',
487 '#title' => t('Weight'),
488 '#default_value' => $i,
489 '#options' => drupal_map_assoc(range(-15, 15))
490 );
491 }
492 $form['question_weight'] = array(
493 '#type' => 'textfield',
494 '#size' => 4,
495 '#title' => t('Question weight'),
496 '#default_value' => '0', // text value will convert to integer in database
497 '#description' => t('Integer to dictate the order of the questions in the questionnaire. Questions with higher weight will be lower in the question order. Questions with the same weight will order alphabetically. For example, question number 20 would likely have a weight of 20.'),
498 '#weight' => $i
499 );
500 $form['submit'] = array(
501 '#type' => 'submit',
502 '#value' => t('Add question'),
503 '#weight' => $i + 1
504 );
505 return $form;
506 }
507
508 /**
509 * Process the form submission from dpistudy_admin_settings_add_question_form()
510 *
511 * @param array $form
512 * @param array $form_status
513 */
514 function dpistudy_admin_settings_add_question_form_submit($form, &$form_status) {
515 $answers = array();
516 while (list($key, $value) = each($form_status['values'])) {
517 if (is_integer($key)) {
518 $answers[$key] = $value;
519 }
520 }
521 ksort($answers);
522 db_query("INSERT INTO {dpistudy_questions} (weight, title) VALUES (%d, '%s')", $form_status['values']['question_weight'], $form_status['values']['question_title']);
523 $id = db_last_insert_id('dpistudy_questions', 'pkid');
524 while (list($key, $value) = each($answers)) {
525 // todo - format like this:
526 // INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);
527 db_query("INSERT INTO {dpistudy_answers} (parent, weight, answer) VALUES (%d, %d, '%s')", $id, $key . '_weight', $value);
528 }
529 drupal_set_message(t('Question "%title" added', array('%title' => $form_status['values']['question_title'])));
530 cache_clear_all('dpistudy-', 'cache', TRUE);
531 }
532
533 /**
534 * Sends the survey results in CSV format
535 * Requires PHP5
536 *
537 */
538 function dpistudy_csv_export() {
539 // http://support.microsoft.com/kb/318756 says not to add charset
540 // but http://api.drupal.org/api/5/function/drupal_set_header says the charset prevents XSS
541 drupal_set_header('Content-type: application/vnd.ms-excel; charset=utf-8');
542 drupal_set_header('Content-Disposition: attachment; filename=dpistudy_' . date('YmdHis') . '.csv');
543 drupal_set_header('Expires: 0');
544 drupal_set_header('Cache-Control: must-revalidate, post-check=0,pre-check=0');
545 drupal_set_header('Pragma: public');
546
547 $out = fopen('php://output', 'w');
548
549 // set column headings for the CSV file
550 $headers = array();
551 $headers[] = 'sessid';
552 $headers[] = 'invite_key';
553 $headers[] = 'key_notes';
554 $headers[] = 'agent';
555 $headers[] = 'referer';
556 $headers[] = 'irb_php';
557 $headers[] = 'irb_http';
558 $headers[] = 'irb_js';
559 for ($i=1; $i<47; $i++) {
560 $headers[] = "q$i";
561 }
562 $headers[] = 'dpi_php';
563 $headers[] = 'dpi_http';
564 $headers[] = 'dpi_js';
565 for ($i=100; $i<106; $i++) {
566 $headers[] = "q$i";
567 $headers[] = "php_q$i";
568 $headers[] = "http_q$i";
569 $headers[] = "js_q$i";
570 }
571 fputcsv($out, $headers);
572 unset($headers);
573
574 // pre-populate default participant as non-response
575 // for every question
576 $populated_answer = array();
577 $populated_answer['invite_key'] = '';
578 $populated_answer['key_notes'] = '';
579 $populated_answer['agent'] = '';
580 $populated_answer['referer'] = '';
581 $populated_answer['irb_php'] = '';
582 $populated_answer['irb_http'] = '';
583 $populated_answer['irb_js'] = '';
584 for ($i=1; $i<47; $i++) {
585 // 9 will be set as a missing value in SPSS
586 $populated_answer["q$i"] = 9;
587 }
588 $populated_answer['dpi_php'] = 0;
589 $populated_answer['dpi_http'] = 0;
590 $populated_answer['dpi_js'] = 0;
591 for ($i=100; $i<106; $i++) {
592 $populated_answer["q$i"] = 6;
593 $populated_answer["php_q$i"] = 0;
594 $populated_answer["http_q$i"] = 0;
595 $populated_answer["js_q$i"] = 0;
596 }
597 unset($i);
598
599 /**
600 * Replace non-response values with actual responses.
601 * Non-response values will stay. Using this method
602 * because the database doesn't store non-responses,
603 * just actual values. If the participant never
604 * submits their responses, there won't be a session
605 * id value to fire the fputcsv() that would represent
606 * a participant.
607 */
608
609 $answer = array();
610
611 // just DPI questionnaire results
612 $result = db_query('SELECT r.sessid, l.invite_key, k.notes, l.agent, l.referer, l.irb_php_timer AS irb_php, l.irb_apache_timer AS irb_http, l.irb_js_timer AS irb_js, r.question, r.answer, r.php_timer, r.apache_timer, r.js_timer FROM {dpistudy_responses} AS r LEFT JOIN {dpistudy_login} AS l ON r.sessid = l.sessid LEFT JOIN {dpistudy_keys} AS k ON l.invite_key = k.invite_key WHERE r.question < 100 ORDER BY l.invite_key, r.sessid, r.question');
613 while ($r = db_fetch_object($result)) {
614 // storing to an array like this could kill available
615 // RAM, so hopefully it won't get out of control
616 if (!isset($answer[$r->sessid])) {
617 $answer[$r->sessid] = $populated_answer;
618 }
619 $answer[$r->sessid]['invite_key'] = $r->invite_key;
620 $answer[$r->sessid]['key_notes'] = $r->notes;
621 $answer[$r->sessid]['agent'] = $r->agent;
622 $answer[$r->sessid]['referer'] = $r->referer;
623 $answer[$r->sessid]['irb_php'] = $r->irb_php;
624 $answer[$r->sessid]['irb_http'] = $r->irb_http;
625 $answer[$r->sessid]['irb_js'] = $r->irb_js;
626 $answer[$r->sessid]['dpi_php'] = $r->php_timer;
627 $answer[$r->sessid]['dpi_http'] = $r->apache_timer;
628 $answer[$r->sessid]['dpi_js'] = $r->js_timer;
629 $answer[$r->sessid]["q{$r->question}"] = $r->answer;
630 }
631
632 // add on the instructional responses
633 $result = db_query('SELECT r.sessid, l.invite_key, k.notes, l.agent, l.referer, l.irb_php_timer AS irb_php, l.irb_apache_timer AS irb_http, l.irb_js_timer AS irb_js, r.question, r.answer, r.php_timer, r.apache_timer, r.js_timer FROM {dpistudy_responses} AS r LEFT JOIN {dpistudy_login} AS l ON r.sessid = l.sessid LEFT JOIN {dpistudy_keys} AS k ON l.invite_key = k.invite_key WHERE r.question > 99 ORDER BY l.invite_key, r.sessid, r.question');
634 while ($r = db_fetch_object($result)) {
635 // storing to an array like this could kill available
636 // RAM, so hopefully it won't get out of control
637 if (!isset($answer[$r->sessid])) {
638 $answer[$r->sessid] = $populated_answer;
639 }
640 $answer[$r->sessid]['invite_key'] = $r->invite_key;
641 $answer[$r->sessid]['key_notes'] = $r->notes;
642 $answer[$r->sessid]['agent'] = $r->agent;
643 $answer[$r->sessid]['referer'] = $r->referer;
644 $answer[$r->sessid]['irb_php'] = $r->irb_php;
645 $answer[$r->sessid]['irb_http'] = $r->irb_http;
646 $answer[$r->sessid]['irb_js'] = $r->irb_js;
647 $answer[$r->sessid]["q{$r->question}"] = $r->answer;
648 $answer[$r->sessid]["php_q{$r->question}"] = $r->php_timer;
649 $answer[$r->sessid]["http_q{$r->question}"] = $r->apache_timer;
650 $answer[$r->sessid]["js_q{$r->question}"] = $r->js_timer;
651 }
652 while (list($sessid, $response) = each($answer)) {
653 fputcsv($out, array_merge(array($sessid), $response));
654 unset($answer[$sessid]);
655 }
656 fclose($out);
657 }
658
659 /**
660 * Breakout of how many people responded in each invite key.
661 */
662 function dpistudy_report() {
663 $headers = array(
664 array('data' => t('Invite Key'), 'field' => 'invite_key', 'sort' => 'asc'),
665 array('data' => t('Visits')),
666 array('data' => t('Invite link'))
667 );
668
669 $sql = 'SELECT invite_key, COUNT(sessid) AS responses FROM {dpistudy_login} GROUP BY invite_key';
670 $sql .= tablesort_sql($headers);
671
672 $count = 'SELECT COUNT(DISTINCT(invite_key)) FROM {dpistudy_login}';
673 $result = pager_query($sql, 15, 0, $count);
674
675 while ($response = db_fetch_object($result)) {
676 // str_replace()ing # is kind of hacky, but passing '?key=' to url() translates
677 // the ? and = through drupal_urlencode() and no longer shows ? and =
678 // in the output, rather the translated urlencode equivalents
679 $rows[] = array($response->invite_key, $response->responses, l(str_replace('#', '', url('<front>', array('absolute' => true, 'fragment' => '?k=' . $response->invite_key))), '?k=' . $response->invite_key, array('absolute' => true)));
680 }
681
682 $pager = theme('pager', NULL, 15);
683
684 if (!empty($pager)) {
685 $rows[] = array(array('data' => $pager, 'colspan' => 3));
686 }
687
688 $output = theme('table', $headers, $rows);
689 return $output;
690 }
691
692 /**
693 * Gateway function to control what is displayed.
694 * 1. IRB
695 * 2. DPI
696 * 3. Instruction
697 *
698 * This function uses $_SESSION as a checklist of things
699 * the user has completed. If nothing is set for the dpistudy
700 * array element, nothing has been completed. When IRB is set, it
701 * means the user has accepted the risks involved with the project
702 * and is ready to move on to the questionnaire.
703 * When $_SESSION['questionnaire'] is DPI, the user has submitted
704 * their DPI questionnaire and is ready for instruction, and so on.
705 *
706 * @return string
707 */
708 function dpistudy_hub() {
709 if (empty($_SESSION['logged_in'])) {
710 if (isset($_REQUEST['k'])) {
711 db_query("INSERT INTO {dpistudy_login} (sessid, invite_key, agent, referer) VALUES ('%s', '%s', '%s', '%s')", session_id(), $_REQUEST['k'], $_SERVER['HTTP_USER_AGENT'], $_SERVER['HTTP_REFERER']);
712 }
713 else {
714 db_query("INSERT INTO {dpistudy_login} (sessid, agent, referer) VALUES ('%s', '%s', '%s')", session_id(), $_SERVER['HTTP_USER_AGENT'], $_SERVER['HTTP_REFERER']);
715 }
716 }
717 $_SESSION['logged_in'] = TRUE;
718
719 if (variable_get('dpistudy_irb_consent_active', 0) && !isset($_SESSION['dpistudy']['irb_consent'])) {
720 return drupal_get_form('dpistudy_irb_form');
721 }
722 else {
723 drupal_goto('dpistudy/dpi');
724 }
725 }
726
727 /**
728 * @return array
729 */
730 function dpistudy_irb_form() {
731 $form = array();
732 $form['irb_text'] = array(
733 '#value' => variable_get('dpistudy_irb_text', '')
734 );
735 $form['microtime'] = array(
736 '#type' => 'hidden',
737 '#value' => microtime(true)
738 );
739 $form['request_time'] = array(
740 '#type' => 'hidden',
741 '#value' => $_SERVER['REQUEST_TIME']
742 );
743 $form['js_timer'] = array(
744 '#type' => 'hidden'
745 );
746 $form['submit'] = array(
747 '#type' => 'submit',
748 '#value' => t('I consent'),
749 '#weight' => 1,
750 '#suffix' => '<p class="alt_consent">' . l(t("I'm not sure. Take me somewhere else."), variable_get('disseration_alt_irb_url', DPISTUDY_ALT_IRB_URL)) . '</p>'
751 );
752 $form['#attributes'] = array('name' => 'dpistudy');
753 $form['#redirect'] = 'dpistudy/dpi';
754 return $form;
755 }
756
757 function dpistudy_irb_form_submit($form, &$form_status) {
758 $_SESSION['dpistudy']['irb_consent'] = true;
759
760 $times = _dpistudy_calculate_times(
761 $form_status['clicked_button']['#post']['microtime'],
762 $form_status['clicked_button']['#post']['request_time'],
763 $form_status['values']['js_timer']
764 );
765
766 db_query("UPDATE {dpistudy_login} SET irb_php_timer = '%f', irb_apache_timer = %d, irb_js_timer = %d WHERE sessid = '%s'", $times['microtime'], $times['http_header'], $times['javascript'], session_id());
767 }
768
769 /**
770 * Displays the DPI questionnaire. When the responses are submitted, it
771 * commits them to the database and forwards on to the instructional unit.
772 *
773 * @return array
774 */
775 function dpistudy_dpi_questionnaire_form() {
776 $i = 1;
777 $form = array();
778 $form['open_table'] = array(
779 '#type' => 'markup',
780 '#value' => '<table border="0" cellpadding="2" cellspacing="2" summary="">'
781 );
782
783 $questions = cache_get('dpistudy-questions');
784 $questions = $questions->data;
785 if (empty($questions)) {
786 $questions = array();
787 $result = db_query('SELECT pkid, title FROM {dpistudy_questions} ORDER BY weight, title');
788
789 while ($question = db_fetch_object($result)) {
790 $answers = array();
791 $result2 = db_query('SELECT weight, answer FROM {dpistudy_answers} WHERE parent = %d ORDER BY weight', $question->pkid);
792 while ($answer = db_fetch_object($result2)) {
793 $answers[$answer->weight] = $answer->answer;
794 }
795 ksort($answers); // shouldn't be necessary, but just making sure
796 $questions['question_' . $i] = array(
797 '#type' => 'radios',
798 '#options' => $answers,
799 '#title' => $question->title,
800 '#prefix' => "<tr><td style=\"vertical-align: top; font-weight: bold\"><div class=\"form-item\">$i.</div></td><td>",
801 '#suffix' => '</td></tr>'
802 );
803 $i++;
804 }
805 cache_set('dpistudy-questions', $questions);
806 }
807
808 $form = array_merge($form, $questions);
809 unset($questions);
810
811 $form['close_table'] = array(
812 '#type' => 'markup',
813 '#value' => '</table>'
814 );
815 $form['request_time'] = array(
816 '#type' => 'hidden',
817 '#value' => $_SERVER['REQUEST_TIME']
818 );
819 $form['js_timer'] = array(
820 '#type' => 'hidden'
821 );
822 $form['#redirect'] = 'dpistudy/instruction';
823 $form['next'] = array(
824 '#value' => '<p>' . t('After clicking submit, you will be presented with 6 timed questions.') . '</p>'
825 );
826 return array_merge($form, dpistudy_submit_form());
827 }
828
829 function dpistudy_dpi_questionnaire_form_submit($form, &$form_status) {
830 $times = _dpistudy_calculate_times(
831 $form_status['clicked_button']['#post']['microtime'],
832 $form_status['clicked_button']['#post']['request_time'],
833 $form_status['values']['js_timer']
834 );
835
836 $num_questions = db_result(db_query('SELECT COUNT(*) FROM {dpistudy_questions}'));
837 for ($i=1; $i<=$num_questions; $i++) {
838 if (isset($form_status['values']["question_$i"])) {
839 db_query("INSERT INTO {dpistudy_responses} (sessid, question, answer, php_timer, apache_timer, js_timer) VALUES ('%s', %d, %d, '%f', %d, %d)", session_id(), $i, $form_status['values']["question_$i"], $times['microtime'], $times['http_header'], $times['javascript']);
840 }
841 }
842 }
843
844 /**
845 * Cycle through questions for the instructional intervention
846 * Multiple choice questions should have one of the following
847 * sample behaviors according to Hirumi's verbal alignment:
848 *
849 * choose, discriminate, identify, recognize, select, solve, locate
850 *
851 * Verbs to match those behaviors at a verbal, knowledge level are:
852 *
853 * arrange, acquire, define, distinguish, duplicate,
854 * identify, label, list, match, memorize, name,
855 * order, recognize, recall, repeat, reproduce
856 *
857 * @return string
858 */
859 function dpistudy_instruction_form() {
860
861 $module_path = url(drupal_get_path('module', 'dpistudy'), array('absolute' => true));
862 if (!isset($_SESSION['instruction_number'])) {
863 $_SESSION['instruction_number'] = 1;
864 }
865
866 if (!isset($_SESSION['instructional_material_order'])) {
867 // counter balancing with randomization
868 $text = array(1, 2, 3);
869 $graphics = array(4, 5, 6);
870 shuffle($text);
871 shuffle($graphics);
872 if (mt_rand(0, 1)) {
873 $_SESSION['instructional_material_order'] = array_merge($text, $graphics);
874 }
875 else {
876 $_SESSION['instructional_material_order'] = array_merge($graphics, $text);
877 }
878 }
879
880 $form = dpistudy_time_form();
881 $form['question'] = array(
882 '#type' => 'value',
883 '#value' => $_SESSION['instructional_material_order'][$_SESSION['instruction_number']-1] // the key of the 1-6 questions, which is actually 0-5
884 );
885
886 switch ($form['question']['#value']) {
887 case 6:
888 $form['instruct_q6'] = array(
889 '#type' => 'radios',
890 '#prefix' => '<p><img src="' . $module_path . '/images/Graph_TravelTimes.gif" height="520" width="589" alt="Travel Times Southbound Departing St. Canards Station" title="Travel Times Southbound Departing St. Canards Station" longdesc="Travel Times Southbound Departing St. Canards Station at 6:00 am. Travel time in minutes. 14 stops are listed in a horizontal bar chart. Springfield 10. Toontown 17. Glenoak 23. Faerie 27. Emerald City 36. Whoville 42. Nottinghamshire 47. Fantasia 49. Florin 51. Hogwarts 61. Fraggle Rock 67. Bedrock 73. Southpark 78. Hotel Denouement 84." /></p>',
891 '#title' => t('Based on the above chart, the travel time to Nottinghamshire is'),
892 '#options' => array(
893 t('<img src="' . $module_path . '/images/bar_42.gif" height="18" width="197" alt="blue bar" title="blue bar" longdesc="Horizontal blue bar matching the length of Whoville." />'),
894 t('<img src="' . $module_path . '/images/bar_51.gif" height="18" width="238" alt="blue bar" title="blue bar" longdesc="Horizontal blue bar matching the length of Florin." />'),
895 t('<img src="' . $module_path . '/images/bar_17.gif" height="18" width="79" alt="blue bar" title="blue bar" longdesc="Horizontal blue bar matching the length of Toontown." />'),
896 t('<img src="' . $module_path . '/images/bar_47.gif" height="18" width="214" alt="blue bar" title="blue bar" longdesc="Horizontal blue bar matching the length of Nottinghamshire." />')
897 ),
898 '#attributes' => array('class' => 'instruction-question')
899 );
900 break;
901 case 5:
902 $form['instruct_q5_markup'] = array(
903 '#value' => '<p style="background-color:yellow; font-weight:bold;">Click on the Regional Intermodal Center for the I-Drive Circulator on Toll 417.</p>'
904 . '<p><map name="transit_routes_im" id="transit_routes_im">'
905 . '<area shape="circle" coords="380,507,8" href="javascript:dpistudy_q5(0);" alt="I-Drive Circulator" title="I-Drive Circulator" longdesc="Black dot with description saying I-Drive Circulator" />'
906 . '<area shape="circle" coords="385,554,11" href="javascript:dpistudy_q5(0);" alt="I-Drive Regional Intermodal Center" title="I-Drive Regional Intermodal Center" longdesc="White circle, with a red border, and a blue 5-point star in the center. I-Drive Regional Intermodal Center" />'
907 . '<area shape="circle" coords="471,436,11" href="javascript:dpistudy_q5(1);" alt="Orlando Regional Intermodal Center" title="Orlando Regional Intermodal Center" longdesc="White circle, with a red border, and a blue 5-point star in the center. Orlando Regional Intermodal Center" />'
908 . '<area shape="circle" coords="426,707,11" href="javascript:dpistudy_q5(2);" alt="Kissimmee Regional Intermodal Center" title="Kissimmee Regional Intermodal Center" longdesc="White circle, with a red border, and a blue 5-point star in the center. Kissimmee Regional Intermodal Center" />'
909 . '<area shape="circle" coords="619,163,11" href="javascript:dpistudy_q5(3);" alt="Sanford Regional Intermodal Center" title="Sanford Regional Intermodal Center" longdesc="White circle, with a red border, and a blue 5-point star in the center. Sanford Regional Intermodal Center" />'
910 . '<area shape="circle" coords="537,578,11" href="javascript:dpistudy_q5(4);" alt="OIA Regional Intermodal Center" title="OIA Regional Intermodal Center" longdesc="White circle, with a red border, and a blue 5-point star in the center. OIA Regional Intermodal Center" />'
911 . '</map><img src="' . $module_path . '/images/transit_routes.jpg" height="734" width="750" alt="Central Florida Commuter Rail Transit Routes map" title="Central Florida Commuter Rail Transit Routes map" longdesc="Central Florida Commuter Rail Transit Routes map has a key in the top left corner." usemap="#transit_routes_im" /></p>'
912 . '<p style="background-color:yellow; font-weight:bold;">Click on the Regional Intermodal Center for the I-Drive Circulator on Toll 417.</p>'
913 );
914 $form['instruct_q5'] = array(
915 '#type' => 'hidden',
916 '#value' => ''
917 );
918 break;
919 case 4:
920 $form['instruct_q4'] = array(
921 '#type' => 'radios',
922 '#prefix' => '<p><img src="' . $module_path . '/images/Graph_CapitalFundingDistribution.gif" height="314" width="302" alt="capital funding distribution" title="capital funding distribution" longdesc="Capital funding distribution." /><img src="' . $module_path . '/images/Graph_TypicalOMFundingSources.gif" height="314" width="300" alt="" title="" longdesc="" /></p>',
923 '#title' => t('Select the pie chart representation of "Equipment 18%"'),
924 '#options' => array(
925 t('<img src="' . $module_path . '/images/capital_funding_equipment.gif" height="121" width="150" alt="51" title="51" longdesc="Horizontal blue bar with the number 51 on the right side in white text." />'),
926 t('<img src="' . $module_path . '/images/funding_sources_subsidy.gif" height="141" width="150" alt="42" title="42" longdesc="Horizontal blue bar with the number 42 on the right side in white text." />'),
927 t('<img src="' . $module_path . '/images/capital_funding_station_costs.gif" height="96" width="189" alt="17" title="17" longdesc="Horizontal blue bar with the number 17 on the right side in white text." />'),
928 t('<img src="' . $module_path . '/images/funding_sources_fare_box_recovery.gif" height="107" width="151" alt="47" title="47" longdesc="Horizontal blue bar with the number 47 on the right side in white text." />')
929 ),
930 '#attributes' => array('class' => 'instruction-question')
931 );
932 break;
933 case 3:
934 $form['instruct_q3'] = array(
935 '#type' => 'radios',
936 '#prefix' => '<ul><strong>DeLand/Orange City/DeBary/Volusia County</strong>'
937 . '<li style="margin-left: 2.5em">Nearly 50,000 people live in Orange City, DeBary and DeLand, one of the fastest growing areas of Volusia County</li>'
938 . '<li style="margin-left: 2.5em">Nearly a quarter of the workforce commutes to jobs outside the county, primarily to Seminole and Orange counties</li></ul>'
939
940 . '<ul><strong>Sanford/Lake Mary/Longwood/Altamonte Springs</strong>'
941
942 . '<li style="margin-left: 2.5em">Home to two major retail malls</li>'
943 . '<li style="margin-left: 2.5em">Growing business clusters along the I-4 corridor and individual communities</li>'
944 . '<li style="margin-left: 2.5em">County government located in Sanford</li>'
945 . '<li style="margin-left: 2.5em">Nearly 400,000 live in Seminole County</li>'
946 . '<li style="margin-left: 2.5em">More than 40 percent of workforce commutes to jobs in Orange County</li>'
947 . '<li style="margin-left: 2.5em">Passenger counts at Orlando-Sanford Airport nearly doubled between 2000 and 2004 Winter Park/Orlando/Orange County</li></ul>'
948
949 . '<ul><strong>Winter Park/Orlando/Orange County</strong>'
950
951 . '<li style="margin-left: 2.5em">Economic and cultural hub of Central Florida</li>'
952 . '<li style="margin-left: 2.5em">Home to NBA\'s Orlando Magic</li>'
953 . '<li style="margin-left: 2.5em">Intermodal transfers at Lynx Central Station and the Sand Lake area</li>'
954 . '<li style="margin-left: 2.5em">Federal/state/local government and educational activity centers</li>'
955 . '<li style="margin-left: 2.5em">Major renovations to the Citrus Bowl, the downtown arena and a new performing arts center planned</li>'
956 . '<li style="margin-left: 2.5em">Station stops at Florida Hospital Orlando and Orlando Regional Medical Center, two of the region\'s largest employers</li>'
957 . '<li style="margin-left: 2.5em">Ready access to retail, dining and cultural activities in Winter Park and downtown Orlando</li>'
958 . '<li style="margin-left: 2.5em">Amtrak transfer stations</li></ul>'
959
960 . '<ul><strong>Kissimmee/Osceola County</strong>'
961
962 . '<li style="margin-left: 2.5em">Line terminates at the 1,200-acre Poinciana Industrial Park, which now employs more than 1,600 workers with major expansions planned</li>'
963 . '<li style="margin-left: 2.5em">Nearly 56,000 residents live within the city limits of Kissimmee, one of the fastest growing counties in Central Florida</li>'
964 . '<li style="margin-left: 2.5em">Almost three-quarters of Kissimmee residents commute to jobs outside the city</li>'
965 . '<li style="margin-left: 2.5em">More than a third of residents work in the tourism or services industry</li></ul>',
966 '#title' => t('Question: In which of these cities do &frac34; of residents commute to jobs outside the city'),
967 '#options' => array(
968 t('Lake Mary'),
969 t('DeLand and Orlando'),
970 t('Kissimmee'),
971 t('Longwood')
972 ),
973 '#attributes' => array('class' => 'instruction-question')
974 );
975 break;
976 case 2:
977 $form['instruct_q2'] = array(
978 '#type' => 'radios',
979 '#prefix' => '<p>The clock\'s winding down on Orange County commissioners doing what the people elected them to do, and that\'s providing leadership.</p>'
980 . '<p>The commissioners say they support commuter rail.</p>'
981 . '<p>They say it will help relieve gridlock by speeding commuters aboard sleek Internet-fitted cars beginning in 2009.</p>'
982 . '<p>But trains don\'t run on peanuts -- and that\'s all the commissioners so far say they\'re willing to give commuter rail to make it go. Unlike their counterparts in Volusia, Seminole and Osceola counties, most commissioners in Orange County say the county shouldn\'t have to assume the costs of its stations beginning in 2017. That\'s the date when localities must take on rail\'s operations and maintenance costs.</p>'
983 . '<p>What they say they will do is pay just one-third of the operations and maintenance expenses of Winter Park and Maitland, two cities north of Orlando considering hosting stations. That\'s what passes for leadership? Passing the buck?</p>'
984 . '<p>Orange County commissioners will meet Tuesday, one week before Winter Park residents vote on paying for and hosting a station. Residents there in a survey have said they want commuter rail. But many fear the cost.</p>'
985 . '<p>County commissioners can relieve those fears by agreeing to fully fund Winter Park\'s share. They also should do the same for Maitland, which is wary of the expense of maintaining a station in 2017.</p>',
986 '#title' => t('Question: According to the above editorial exerpt, trains don\'t run on'),
987 '#options' => array(
988 t('gasoline.'),
989 t('diesel.'),
990 t('peanuts.'),
991 t('good intentions.')
992 ),
993 '#attributes' => array('class' => 'instruction-question')
994 );
995 break;
996 case 1:
997 default:
998 $form['instruct_q1'] = array(
999 '#type' => 'radios',
1000 '#prefix' => '<p>Traffic congestion is a growing concern for those who live, work and visit Central Florida. As our region continues to grow at a staggering pace, that congestion will only get worse. Though there is no one magic bullet to solve our traffic woes, several different modes of transportation options working together - known as "intermodal" in transportation-speak - is a proven way to ease the gridlock.</p>'
1001 . '<p>That\'s why the Florida Department of Transportation (FDOT), in cooperation with local government officials in Orange, Seminole, Volusia and Osceola counties and the federal government, is looking at a commuter rail transit project to run along a 61-mile stretch of existing rail freight tracks in the four-county area.</p>'
1002 . '<p>The 31-mile Phase 1 segment would serve 10 stations, linking DeBary to Orlando. Service could begin as soon as 2009 - just as FDOT starts a major I-4 reconstruction project through the heart of Central Florida, from State Road 434 in Longwood to Kirkman Road in southwest Orange County.</p>',
1003 '#title' => t('Question: Referencing the paragraphs above, the definition of "intermodal" is'),
1004 '#options' => array(
1005 t('multiple transportation modes working together.'),
1006 t('riding a railroad.'),
1007 t('the average of two populations.'),
1008 t('having a single mode of transportation.')
1009 ),
1010 '#attributes' => array('class' => 'instruction-question')
1011 );
1012 break;
1013 }
1014 $form['request_time'] = array(
1015 '#type' => 'hidden',
1016 '#value' => $_SERVER['REQUEST_TIME']
1017 );
1018 if ($_SESSION['instruction_number'] == 6) {
1019 $form['#redirect'] = 'dpistudy/score';
1020 }
1021 return array_merge($form, dpistudy_submit_form());
1022 }
1023
1024 function dpistudy_instruction_form_submit($form, &$form_status) {
1025 $times = _dpistudy_calculate_times(
1026 $form_status['clicked_button']['#post']['microtime'],
1027 $form_status['clicked_button']['#post']['request_time'],
1028 $form_status['values']['js_timer']
1029 );
1030
1031 if (isset($form_status['values']["instruct_q{$form_status['values']['question']}"])) {
1032 db_query("INSERT INTO {dpistudy_responses} (sessid, question, answer, php_timer, apache_timer, js_timer) VALUES ('%s', '%s', '%s', '%f', %d, %d)", session_id(), $form_status['values']['question']+99, $form_status['values']["instruct_q{$form_status['values']['question']}"]+1, $times['microtime'], $times['http_header'], $times['javascript']);
1033 }
1034
1035 $_SESSION['instruction_number']++;
1036 }
1037
1038 function dpistudy_score() {
1039 $num_responses = 0;
1040 $dpi_score = 0;
1041 $incomplete = false;
1042
1043 $result = db_query("SELECT r.answer FROM {dpistudy_responses} AS r LEFT JOIN {dpistudy_questions} AS q ON r.question = q.pkid WHERE r.question <= 34 AND r.answer <> 0 AND r.sessid = '%s'", session_id());
1044
1045 while ($response = db_fetch_object($result)) {
1046 $num_responses++;
1047 $dpi_score += $response->answer;
1048 }
1049 for ($i=34; $i>$num_responses; $i--) {
1050 $incomplete = true;
1051 $dpi_score += 3;
1052 }
1053 if ($incomplete == true) {
1054 drupal_set_message(t('Since you did not answer every question regarding Digital Propensity, your score is estimated.'), 'status', false);
1055 }
1056
1057 dpistudy_protect_anonymity();
1058
1059 return t(variable_get('dpistudy_score_screen', ''), array('@score' => $dpi_score));
1060 }
1061
1062 /**
1063 * Destroy the session data and cookie.
1064 *
1065 * Calling user_logout() or sess_regenerate() would be similar
1066 * but since this is more messy, it makes sure we don't keep a
1067 * link to who the user is since it sets the existing cookie to
1068 * expire, then sets a totally different id that we never logged
1069 * in the database.
1070 */
1071 function dpistudy_protect_anonymity() {
1072 $_SESSION = array();
1073 sess_destroy_sid(session_id());
1074 if (isset($_COOKIE[session_name()])) {
1075 setcookie(session_name(), '', time() - 42000, '/');
1076 }
1077 session_regenerate_id(TRUE);
1078 }
1079
1080 /**
1081 *
1082 *
1083 * @return array
1084 */
1085 function dpistudy_time_form() {
1086 $form = array();
1087 $form['js_timer'] = array(
1088 '#type' => 'textfield',
1089 '#title' => t('This question is timed'),
1090 '#size' => 7,
1091 '#maxlength' => 9,
1092 '#default_value' => ''
1093 );
1094 return $form;
1095 }
1096
1097 /**
1098 * Contains the form elements that make the PHP-based and