CMS  Version 3.9
questionnaire_form.inc
Go to the documentation of this file.
1 <?php
7 /**************************************************************
8 
9 Copyright (c) 2007,2008 Sonjara, Inc
10 
11 Permission is hereby granted, free of charge, to any person
12 obtaining a copy of this software and associated documentation
13 files (the "Software"), to deal in the Software without
14 restriction, including without limitation the rights to use,
15 copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the
17 Software is furnished to do so, subject to the following
18 conditions:
19 
20 The above copyright notice and this permission notice shall be
21 included in all copies or substantial portions of the Software.
22 
23 Except as contained in this notice, the name(s) of the above
24 copyright holders shall not be used in advertising or otherwise
25 to promote the sale, use or other dealings in this Software
26 without prior written authorization.
27 
28 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
30 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
32 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
33 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
34 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
35 OTHER DEALINGS IN THE SOFTWARE.
36 
37 *****************************************************************/
38 
39 /*
40  * Title: questionnaire_form.inc
41  *
42  * Description: Class for building custom surveys, questionaires,
43  * quizzes and response interface.
44  *
45  * The form formats the question display based on the
46  * type of question.
47  *
48  * The specifics of the different classes that can render
49  * questions are handled by a manger class which provides
50  * information such as the name of the question_id field,
51  * gets the set of Questions and Answers through its relations,
52  * and saves to its specific type of data structure.
53  *
54  * author: Janice Gallant for Sonjara, Inc.
55  *
56  * date: 11/4/09
57  *
58  */
59 
60 Fakoli::using("questionnaire");
61 
62 /*
63  * QuestionnaireForm
64  *
65  * @param: $mgr - an object of class QuestionnnaireManager, SurveyManager,
66  * or other which provides specific data and values to this form as
67  * requested.
68  * @param: $readonlyForm - defaults to false - whether questionnaire is
69  * being reviewed or answered. The form may be read only if the responses
70  * are being validated or reviewed. The readonly setting is checked by Question Renderer
71  * objects.
72  */
73 
75 {
76  var $mgr; // the object that links to the questions and answers: questionnaire, survey, quiz, etc.
78  var $answers;
79  var $submitLabel = "Save";
80  var $validators = array();
81  var $requiredText = "<b>You must answer all required questions.</b><br/>";
82  var $msg; // validation msg
83  var $onSaveComplete = null;
85  var $id;
86 
87  // AutoForm defaults
88  var $labelCSS = "";
89  var $valueCSS = "";
90  var $buttonCSS = "button";
91  var $inputCSS = "";
92  var $checkboxCSS = "";
93  var $markRequiredFields = false;
94  var $onFormatLabel = null;
95  var $buttons_at_top = false;
96  var $requiredFieldsText = "* indicates required question";
97  var $subordinate = false;
98  var $buttons = array();
99  var $allowIncompleteSave = true;
100 
102  {
103  global $auto_form_defaults;
104 
105  $this->mgr = $mgr;
106  $this->readOnlyForm = $readOnlyForm;
107 
108  foreach($auto_form_defaults as $field => $value)
109  {
110  $this->$field = $value;
111  }
112  $this->requiredFieldsText = preg_replace("/field/i", "question", $this->requiredFieldsText);
113 
114  $questions = $this->mgr->getQuestions();
115 
116  if(count($questions) > 0)
117  {
118  $this->answers = $this->getIndexedAnswers();
119 
120  foreach($questions as $question)
121  {
122  $this->questions[] = QuestionField::create($this, $question);
123  }
124 
125  $num = 1;
126 
127  foreach($this->questions as $question)
128  {
129  if (!$question->skipNumbering)
130  {
131  $question->question->question_number = $num++;
132  }
133 
134  if($question->question->required)
135  {
136  array_push($this->validators, $question->getRequiredValidator());
137  }
138  }
139  }
140  }
141 
142  function makeSubordinate($subordinate = true)
143  {
144  $this->subordinate = $subordinate;
145  }
146 
148  {
149  return $this;
150  }
151 
161  function button($text, $url, $confirm = null, $isScript = false)
162  {
163  $this->buttons[] = array('text' => $text, 'url' => $url, 'confirm' => $confirm, 'isScript' => $isScript);
164  }
165 
170  function drawButtons()
171  {
172  foreach($this->buttons as $button)
173  {
174  $url = ($button['isScript']) ? $button['url'] : "go('{$button['url']}');";
175 
176  if ($button['confirm'])
177  {
178  $link = "if (confirm('".jsSafe($button['confirm'])."')) $url; return false;";
179  }
180  else
181  {
182  $link = "$url; return false;";
183  }
184 
185  echo "&nbsp;&nbsp;&nbsp;&nbsp;<input type='button' class='{$this->buttonCSS}' onclick=\"$link\" value=\"{$button['text']}\"/>";
186  }
187 
188  }
190  {
191  $qPk = $this->mgr->getQuestionKey();
192  $answer = $this->answers[$question->$qPk];
193  return ($answer) ? $answer->value : "";
194  }
195 
196  function writeScript()
197  {
198  $script = "";
199 
200  if(count($this->questions) == 0)
201  return;
202 
203  foreach($this->questions as $question)
204  {
205  $script .= $question->writeScript();
206  }
207 
208 
209  $script .= "function validate_Questionnaire_form(form)\n";
210  $script .= "{";
211 
212  foreach($this->validators as $validator)
213  {
214  $script .= $validator->writeClient();
215  }
216 
217  $script .= "\n\n return true;\n}";
218  $script .= "\n\n";
219  $script .= "function onSubmitQuestionnaire_form(form)\n{\n";
220 
221  if ($this->allowIncompleteSave)
222  {
223  // return true because we need to save their answers, even if they
224  // didn't answer all questions (invalid form)
225  $script .= "\t validate_Questionnaire_form(form); return true\n}";
226  }
227  else
228  {
229  $script .= "\t return validate_Questionnaire_form(form);\n}";
230  }
231  if ($script) $script = "<script type='text/javascript'>\n$script\n</script>";
232  return $script;
233  }
234 
235  function writeHTML()
236  {
237  if(count($this->questions) == 0)
238  {
239  echo "There are no questions.<br/>";
240  return;
241  }
242 
243  if($this->requiredText)
244  {
245  if ($this->msg)
246  {
247  echo "<tr>\n <td colspan='2'><span class='error'>{$this->msg}</span</td></tr>\n";
248  $this->msg = "";
249  echo "</span>\n";
250  }
251  echo $this->requiredText;
252  }
253 
254  echo "<b>{$this->requiredFieldsText}</b><br/><br/>";
255 
256  echo "<dl>\n";
257 
258  foreach($this->questions as $question)
259  {
260  $this->writeOneQuestion($question);
261  }
262 
263  echo "</dl>\n";
264  }
265 
267  {
268  $required = $question->getRequired();
269  $qPk = $question->getID();
270  $num = ($question->question->question_number) ? $question->question->question_number : $this->mgr->getQuestionNumber();
271 
272  echo " <dt><label for='question_{$qPk}'>";
273 
274  if (!$question->skipNumbering)
275  {
276  echo "<b>{$num}.</b> ";
277  }
278 
279  echo "{$question->question->question}{$required}</label></dt>\n";
280 
281  echo " <dd>";
282 
283  $question->writeHTML();
284 
285  echo " <br/></dd>";
286  }
287 
294  function fromPOST()
295  {
296  global $_POST;
297 
298  $pk = $this->mgr->item->getPrimaryKey();
299  $qPk = $this->mgr->getQuestionKey();
300  $aPk = $this->mgr->getAnswerKey();
301  $answerClass = $this->mgr->getAnswerClass();
302 
303  foreach($_POST as $name => $value)
304  {
305  if (!strncmp("question_", $name, 9))
306  {
307  // convert array of checks to comma delimated string
308  $valueAnswer = (is_array($value)) ? implode(",", array_values($value)) : $value;
309 
310  $question_id = substr($name, 9);
311 
312  if(array_key_exists($question_id, $this->answers))
313  {
314  $this->answers[$question_id]->value = $valueAnswer;
315  }
316  else
317  {
318  $answer = new $answerClass();
319  // e.g., response_id
320  $answer->$pk = $this->mgr->item->$pk;
321  $answer->$qPk = $question_id;
322  $answer->value = $valueAnswer;
323  $this->answers[$question_id] = $answer;
324  }
325  }
326  }
327 
328  // Need to handle deleted checkbox answers which don't appear in $_POST
329  $questions = $this->mgr->getQuestions();
330 
331  if(count($questions) > 0)
332  {
333  $indexed = regroupList($questions, "question_type_id");
334  $checkListQuestions = array();
335  // CheckListView
336  if(array_key_exists(5, $indexed))
337  {
338  $checkListQuestions = $indexed[5];
339  if(!is_array($checkListQuestions))
340  {
341  $checkListQuestions = array($checkListQuestions);
342  }
343  }
344 
345  if(count($checkListQuestions) > 0)
346  {
347  foreach($checkListQuestions as $question)
348  {
349  $answer = $this->answers[$question->$qPk];
350  if($answer && $answer->value)
351  {
352  $postAnswer = $_POST["question_{$question->$qPk}"];
353 
354  if(!$postAnswer && !is_array($postAnswer))
355  {
356  $answer->value = "";
357  }
358  }
359  }
360  }
361  }
362 
363  }
364 
365  function drawForm()
366  {
367  if ($this->readOnlyForm) return $this->drawReadOnly();
368 
369  echo "<div class='questionnaire_form'>";
370 
372 
373  $id = ($this->id) ? $this->id : "Questionnaire_form";
374 
375  if (!$this->subordinate)
376  {
377  echo "<form id=\"{$id}\" method=\"POST\" action=\"\" ";
378  echo "enctype='multipart/form-data' onsubmit='return onSubmitQuestionnaire_form(this);'>";
379  }
380 
381  if ($this->buttons_at_top && !$this->subordinate)
382  {
383  $this->drawSubmitButtons($submitLabel);
384  $this->drawButtons();
385  echo "<br/><br/>";
386  }
387 
388  $this->writeHTML();
389 
390  if (!$this->subordinate)
391  {
392  echo "<br/>";
393  $this->drawSubmitButtons($submitLabel);
394  $this->drawButtons();
395  echo "<br/>";
396  echo "</form>";
397  }
398 
399  echo "</div>";
400  }
401 
403  {
404  echo "<input type=\"submit\" style=\"float: left\" name=\"save\" class=\"{$this->buttonCSS}\" value=\"&nbsp;&nbsp;{$submitLabel}&nbsp;&nbsp;\"/>";
405  }
406 
407  function drawReadOnly()
408  {
409  $this->writeHTML();
410  }
411 
412  // display or preview view
413  function drawView()
414  {
415  $this->writeHTML();
416  }
417 
418  function getIndexedAnswers()
419  {
420  $answers = $this->mgr->getAnswers();
421 
422  if(count($answers) > 0)
423  $answers = reindexList($answers, $this->mgr->getQuestionKey());
424 
425  return $answers;
426  }
427 
428 
429  function validate()
430  {
431  foreach($this->validators as $validator)
432  {
433  $msg = $validator->validate();
434  if ($msg)
435  {
436  $result .= $msg."<br>";
437  }
438  }
439  $this->msg = $msg;
440  return $result;
441  }
442 
443 
444  function save()
445  {
446  $this->fromPOST();
447  $aPk = $this->mgr->getAnswerKey();
448  $qPk = $this->mgr->getQuestionKey();
449 
450  foreach($this->answers as $question_id => $answer)
451  {
452  // If saved answer, just update value
453  if($answer->$aPk)
454  {
455  $answer->filter = new InclusionFilter("value");
456  }
457  // new answer - save all fields
458  $answer->save();
459  }
460 
461  // Even if invalid, we still want to save their answers
462  $this->msg = $this->validate();
463  if ($this->msg != "")
464  {
465  return false;
466  }
467 
468  // onComplete event is fired once all processing has been completed
469  if ($this->onSaveComplete)
470  {
471  $onComplete = $this->onSaveComplete;
472  $onComplete($this);
473  }
474 
475  return true;
476 
477  }
478 
479 
480  // change from starting at 0 to starting at 1
481  static function incrementArray($array)
482  {
483  if(count($array) > 0)
484  {
485  $idx = 0;
486  while($idx < (count($array)))
487  {
488  $newArray[$idx+1] = $array[$idx];
489  $idx++;
490  }
491  }
492  return $newArray;
493  }
494 }
495 
496 
497 
498 /*
499  * For questionnaire creator to view the layout of the questionnaire
500  * from the questionnaire data entry interface.
501  */
503 {
504  var $mgr;
505 
507  {
508  $this->mgr = $mgr;
509  $this->readOnlyForm = true;
510 
511  $questions = $this->mgr->getQuestions();
512 
513  if(count($questions) == 0) return;
514 
515  foreach($questions as $question)
516  {
517  $this->questions[] = QuestionField::create($this, $question);
518  }
519  }
520 
522  {
523  $required = $question->getRequired();
524  $qPk = $question->getID();
525  $num = ($question->question->question_number) ? $question->question->question_number : $this->mgr->getQuestionNumber();
526 
527  echo " <dt><label for='question_{$qPk}'>";
528 
529  if (!$question->skipNumbering)
530  {
531  echo "<b>{$num}.</b> ";
532  }
533 
534  echo "{$question->question->question}{$required}</label></dt>\n";
535 
536  echo " <dd>";
537 
538  $question->writeHTML();
539 
540  echo " <br/></dd>";
541  }
542 
543 
544  function writeScript()
545  {
546  return "";
547  }
548 }
549 
550 
551 /*
552  * QuestionnaireResponseView
553  *
554  * @param: $mgr - an object of class QuestionnnaireManager, SurveyManager,
555  * or other which provides specific data and values to this form as
556  * requested.
557  *
558  * This view is similar to calling QuestionnaireForm as readonly
559  * except that responses are displayed in summary or text only form,
560  * not as disabled fields readonly fields.
561  *
562  * Calling page should call function drawView rather than
563  * drawForm for display only view.
564  *
565  * e.g.,
566  *
567  * $mgr = new MyQuestionnaireResponseManger($myObj);
568  * $view = new QuestionnaireResponseView($mgr);
569  *
570  * $view->drawView();
571  */
572 
574 {
576  {
577  parent::QuestionnaireForm($mgr, true);
578  }
579 
581  {
582  $required = $question->getRequired();
583  $num = ($question->question->question_number) ? $question->question->question_number : $this->mgr->getQuestionNumber();
584 
585  echo " <dt>\n";
586  if (!$question->skipNumbering)
587  {
588  echo "<b>{$num}.</b> ";
589  }
590 
591  echo "{$question->question->question}{$required}</dt>\n";
592  echo " <dd>\n";
593  $question->drawSummaryView();
594  echo " </dd><br/>";
595  echo "</dl>";
596  }
597 }?>
$_POST["owner_id"]
Definition: blog_form.inc:54
$name
Definition: upload.inc:54
static using()
Import the datamodels, views and manifest for the specified component(s).
Definition: core.inc:116
static create($parent, $question)
$allowIncompleteSave
Allows users to save partially completed forms.
$buttonCSS
CSS Class to use for buttons.
makeSubordinate($subordinate=true)
$markRequiredFields
Set to true to add an asterisk after required field labels.
fromPOST()
Put any updated or new answers in the $_POST array into the array of answers that are indexed by ques...
$onFormatLabel
Label Processing hook.
$checkboxCSS
CSS Class to use for checkbox fields.
static incrementArray($array)
$buttons
The custom buttons collection.
drawButtons()
Draws any additional buttons specified in the calling script.
$readOnlyForm
Specifies whether the entire form is read-only.
QuestionnaireForm($mgr, $readOnlyForm=false)
$valueCSS
CSS Class to use for field/value cells.
drawSubmitButtons($submitLabel)
$labelCSS
CSS Class to use for label cells.
button($text, $url, $confirm=null, $isScript=false)
Adds a custom button to the form.
$inputCSS
CSS Class to use for input fields.
$onSaveComplete
Callback event handler that is fired after the AutoForm has finished saving data to the database.
$validator
$form readOnlyForm
$form onSaveComplete
$result
$question_id
$question
if(! $blog->published||! $blog->enable_rss_feed||!checkRole($blog->allow_read)) $url
Definition: rss.inc:58
$button
Definition: show.inc:6