Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
form_question.inc
1 <?php
18 require_once SQ_INCLUDE_PATH.'/asset.inc';
19 require_once SQ_LIB_PATH.'/html_form/html_form.inc';
20 
21 
35 class Form_Question extends Asset
36 {
37 
42  var $_formid = 0;
43 
48  var $_questionid = 0;
49 
54  var $section_name = '';
55 
60  var $section_id = 0;
61 
62 
67  var $value = NULL;
68 
69 
75  var $extra_data = Array();
76 
77 
82  var $submission_errors = NULL;
83 
84 
89  var $active_form = NULL;
90 
91 
96  var $failed_rules = Array();
97 
98 
106  function Form_Question($formid=0, $data=Array())
107  {
108  if (!$formid || empty($data)) {
109  return;
110  }
111  $form = $GLOBALS['SQ_SYSTEM']->am->getAsset($formid);
112 
113  if (is_null($form)) return;
114  $this->_formid = $formid;
115  $this->_questionid = $data['questionid'];
116 
117  // set the data paths
118  $this->_loadDataPaths();
119 
120  $asset_attributes = $GLOBALS['SQ_SYSTEM']->am->getAssetTypeAttributes(get_class_lower($this), Array('name', 'type','default_val','attrid'));
121 
122  foreach ($asset_attributes as $attribute_name => $asset_attribute) {
123  $this->vars[$attribute_name]['type'] = $asset_attribute['type'];
124  $this->vars[$attribute_name]['attrid'] = $asset_attribute['attrid'];
125  if (isset($data['attributes'][$attribute_name])) {
126  $this->vars[$attribute_name]['value'] = (($asset_attribute['type'] == 'serialise') ? unserialize($data['attributes'][$attribute_name]) : $data['attributes'][$attribute_name]);
127  } else {
128  $this->vars[$attribute_name]['value'] = (($asset_attribute['type'] == 'serialise') ? unserialize($asset_attribute['default_val']) : $asset_attribute['default_val']);
129  }
130  }
131 
132  // set general object properties, now we have the name of the thing
133  $this->id = $formid.':q'.$this->_questionid;
134  $this->name = $this->vars['name']['value'];
135  $this->short_name = $this->name;
136  $this->status = $form->status;
137  $this->version = '0.1';
138 
139  // set variable values
140  $this->vars['question_type_code']['value'] = get_class_lower($this);
141  $this->vars['question_type_code']['type'] = 'text';
142 
143  }//end constructor
144 
145 
159  function setAttrValue($name, $value)
160  {
161  // do not allow empty question titles
162  if ($name == 'name' && empty($value)) {
163  return FALSE;
164  }
165 
166  return parent::setAttrValue($name, $value);
167 
168  }//end setAttrValue()
169 
170 
184  function saveAttributes($dont_run_updated=FALSE)
185  {
186  if (!$this->id) return TRUE;
187 // if (empty($this->_tmp['vars_set'])) return TRUE;
188 
189  $form = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->_formid);
190 
191  if (!$GLOBALS['SQ_REVERT_TO_SYSTEM_VERSION'] && !$form->writeAccess('attributes')) {
192  trigger_localised_error('CORE0121', E_USER_WARNING, $this->name, $this->id);
193  return FALSE;
194  }
195 
196  $save_vars = Array();
197  $attr_ids = Array();
198 
199  // prepare for the save
200  $saved_vars = $this->vars;
201 
202  unset($saved_vars['question_type_code']);
203 
204  // open the transaction
205  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
206  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
207 
208  // find any previous entries
209  $sql = 'SELECT name, default_val FROM '.SQ_TABLE_RUNNING_PREFIX.'ast_attr ';
210  $where = 'type_code = :type_code';
211  $where = $GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause($where);
212 
213  try {
214  $query = MatrixDAL::preparePdoQuery($sql.$where);
215  MatrixDAL::bindValueToPdo($query, 'type_code', $this->vars['question_type_code']['value']);
216  $existing = MatrixDAL::executePdoGroupedAssoc($query);
217  } catch (Exception $e) {
218  throw new Exception('Can not get default val and name base on type code due to the following database error '.$e->getMessage());
219  }//end try catch
220 
221  // check against default values; we don't need to save that attribute if it is
222  foreach ($existing as $name => $default_val) {
223  if ($saved_vars[$name]['value'] == $default_val['0']['default_val']) {
224  unset($saved_vars[$name]);
225  }
226  }//end foreach
227 
228  // only save the values
229  foreach ($saved_vars as $attr_id => $saved_var) {
230  $saved_vars_ser[$attr_id] = (($saved_vars[$attr_id]['type'] == 'serialise') ? serialize($saved_vars[$attr_id]['value']) : $saved_vars[$attr_id]['value']);
231  }
232 
233 
234  $questions = $form->attr('questions');
235  $questions[$this->_questionid]['attributes'] = $saved_vars_ser;
236 
237  $form->setAttrValue('questions', $questions);
238 
239  if (!$form->saveAttributes()) {
240  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
241  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
242  return FALSE;
243  }
244 
245  // tell, the asset it has updated
246  if (!$dont_run_updated && !$this->_updated()) {
247  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
248  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
249  return FALSE;
250  }
251 
252  unset($this->_tmp['vars_set']);
253 
254  // If we get this far, then it's all OK
255  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
256  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
257 
258  $GLOBALS['SQ_SYSTEM']->am->updateLock($this->_formid, 'attributes');
259 
260  return TRUE;
261 
262  }//end saveAttributes()
263 
264 
279  function _updated($update_parents=TRUE)
280  {
281  // We should not be running our _updated() because we don't have anything in the asset table
282  // to update... but we should run our form/section's update, if only to increment the version
283  if (SQ_IN_BACKEND) {
284  $form = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->_formid);
285  return $form->_updated($update_parents);
286  }
287 
288  return TRUE;
289 
290  }//end _updated()
291 
292 
299  function canClone()
300  {
301  return FALSE;
302 
303  }//end canClone()
304 
305 
314  function _getName($short_name=FALSE)
315  {
316  return $this->attr('name');
317 
318  }//end _getName()
319 
320 
336  function getVal($field='')
337  {
338  switch (strtolower($field)) {
339  case 'title' :
340  return $this->attr('name');
341  case 'answer' :
342  return $this->getHtmlField();
343  break;
344  case 'note' :
345  return $this->attr('note');
346  case 'value' :
347  return $this->getValue();
348  default:
349  return '';
350  }
351 
352  }//end getVal()
353 
354 
366  function getAnswerByOffset($offset=0)
367  {
368  $options = $this->getOptions();
369  if (is_array($offset)) {
370  $answers = Array();
371  foreach ($offset as $o) {
372  if (isset($options[$o])) $answers[] = $options[$o];
373  }
374  return $answers;
375  }
376  if (isset($options[$offset])) return $options[$offset];
377 
378  return FALSE;
379 
380  }//end getAnswerByOffset()
381 
382 
391  function getOffsetByAnswer($answer='')
392  {
393  if (!$answer) return FALSE;
394 
395  $options = $this->getOptions();
396  if (in_array($answer, $options)) {
397  return array_search($answer, $options);
398  }
399 
400  }//end getOffsetByAnswer()
401 
402 
409  function isSelection()
410  {
411  foreach ($this->getSelectionTypeQuestions() as $types) {
412  if ($this instanceof $types) return TRUE;
413  }
414  return FALSE;
415 
416  }//end isSelection()
417 
418 
428  {
429  $questions = Array(
430  'form_question_type_select',
431  'form_question_type_tickbox_list',
432  'form_question_type_option_list',
433  'form_question_type_country',
434  );
435 
436  return $questions;
437 
438  }//end getSelectionTypeQuestions()
439 
440 
447  function &getMySection()
448  {
449  $asset = NULL;
450  if (empty($this->_formid)) return $asset;
451 
452  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->_formid);
453  return $asset;
454 
455  }//end getMySection()
456 
457 
464  function getMySectionName()
465  {
466  if (!$this->section_name) {
467  $section = $this->getMySection();
468  $this->section_name = $section->name;
469  }
470  return $this->section_name;
471 
472  }//end getMySectionName()
473 
474 
484  function generateJSCode()
485  {
486  $rules = $this->getRules();
487  $code = ''; // blank sheet of code to start with
488 
489  foreach ($rules as $k => $rule) {
490  $type_code = 'form_question_rule_type_'.$rule['rule_code'];
491 
492  $GLOBALS['SQ_SYSTEM']->am->includeAsset($type_code);
493  $rule_asset = new $type_code();
494  $code .= $rule_asset->generateJSCode($this, $rule);
495  $code .= "\n";
496  }
497 
498  return $code;
499 
500  }//end generateJSCode()
501 
502 
514  function getAllowedRules()
515  {
516  return Array();
517 
518  }//end getAllowedRules()
519 
520 
531  function &addRule($rule_code, $operator, $value)
532  {
533  $rules = $this->attr('rules');
534  $rules[] = Array('rule_code' => $rule_code, 'operator' => $operator, 'value' => $value);
535  $this->setAttrValue('rules', $rules);
536  $this->saveAttributes();
537  return $rules;
538 
539  }//end addRule()
540 
541 
550  function &removeRule(&$deleted_rule)
551  {
552  $rules = $this->attr('rules');
553  foreach (array_keys($rules) as $rule_index) {
554  if ($rules[$rule_index] == $deleted_rule) {
555  unset($rules[$rule_index]);
556  break;
557  }
558  }
559  $this->setAttrValue('rules', $rules);
560  $this->saveAttributes();
561  return $rules;
562 
563  }//end removeRule()
564 
565 
572  function getRules()
573  {
574  return $this->attr('rules');
575 
576  }//end getRules()
577 
578 
585  function getRuleCount()
586  {
587  return count($this->getRules());
588 
589  }//end getRuleCount()
590 
591 
598  function isEditable()
599  {
600  return TRUE;
601 
602  }//end isEditable()
603 
604 
611  function getHtmlField()
612  {
613  return TRUE;
614 
615  }//end getHtmlField()
616 
617 
629  function populate(Asset $parent=NULL)
630  {
631  if (isset($_POST['q'.$this->id])) {
632  // check if we are allowing the keywords to be replaced in answer
633  if(isset($parent) && $parent->attr('disable_keyword_replacements')) {
634  $value = $_POST['q'.$this->id];
635  // sanitise it by removing '%' from keywords so that
636  // they dont go ahead and try to fetch it replacement
637  if (is_array($value)) {
638  foreach ($value as $idx => $val) {
639  $keywords = extract_keywords($val);
640  foreach ($keywords as $keyword) {
641  $pattern = '/(%*)'.$keyword.'(%*)/';
642  $value[$idx] = preg_replace($pattern, $keyword, $val);
643  }
644  }
645  } else {
646  $keywords = extract_keywords($value);
647  foreach ($keywords as $keyword) {
648  $pattern = '/(%*)'.$keyword.'(%*)/';
649  $value = preg_replace($pattern, $keyword, $value);
650  }
651  }
652  $this->setValue($value);
653  } else {
654  $this->setValue($_POST['q'.$this->id]);
655  }
656  }
657  return TRUE;
658 
659  }//end populate()
660 
661 
670  function setValue($value)
671  {
672  $this->value = $value;
673 
674  }//end setValue()
675 
676 
685  function setExtraData(Array $data=NULL)
686  {
687  $this->extra_data = $data;
688 
689  }//end setExtraData()
690 
691 
707  function hasValidValue($answer=NULL, $mute_errors=FALSE)
708  {
709  if (is_null($answer)) $answer = $this->getValue();
710  if ($mute_errors === FALSE) {
711  $this->failed_rules = Array();
712  }
713 
714  $rules = $this->getRules();
715  $ok = TRUE; // We start as being all OK
716 
717  if (!$this->attr('is_required') && $answer === '') {
718  return TRUE;
719  }
720 
721  foreach ($rules as $k=>$rule) {
722  $type_code = 'form_question_rule_type_'.$rule['rule_code'];
723 
724  $GLOBALS['SQ_SYSTEM']->am->includeAsset($type_code);
725  $rule_asset = new $type_code();
726  $rule_ok = $rule_asset->evaluate($answer, $rule, $this);
727 
728  if (!$rule_ok) {
729  $ok = FALSE;
730  if (!$mute_errors) {
731  $custom_text = array_get_index($rule, 'custom_text', '');
732  if (!empty($custom_text)) {
733  $this->failed_rules[] = $rule['custom_text'];
734  } else {
735  $this->failed_rules[] = $rule_asset->defaultError($this, $rule);
736  }
737  } else {
738  // If we are muting errors, we don't need to evaluate any
739  // more rules, so we can just short-circuit and return now
740  return FALSE;
741  }
742  }
743 
744  }
745 
746  return $ok;
747 
748  }//end hasValidValue()
749 
750 
759  function saveValue()
760  {
761  return;
762 
763  }//end saveValue()
764 
765 
772  function getValue()
773  {
774  require_once SQ_FUDGE_PATH.'/general/text.inc';
775 
776  // First see if there is a sticky value for this question - if we're
777  // allowed to use it
778  if (is_null($this->value) && $this->attr('sticky')) {
779  $this->value = $this->getStickyValue();
780  }
781 
782  // Still nothing? Go to the default value then
783  if (is_null($this->value)) {
784 
785  $default_value = $this->attr('default');
786  // If the default value is NULL on some configuration, make it empty string to prevent infinite loop.
787  if (is_null($default_value)) {
788  $default_value = '';
789  }//end if
790 
791  if (!is_array($default_value)) {
792  $keywords = retrieve_keywords_replacements($default_value);
793  $replacements = Array();
794 
795  foreach ($keywords as $keyword) {
796  if (preg_match('|^current_user_|', $keyword)) {
797  $user_keyword = preg_replace('|^current_user_|', 'asset_', $keyword);
798  $user = $GLOBALS['SQ_SYSTEM']->user;
799 
800  // If we are the public user, this should be empty
801  if ($GLOBALS['SQ_SYSTEM']->userPublic()) {
802  $replacements[$keyword] = '';
803  } else {
804  $replacements[$keyword] = $user->getKeywordReplacement($user_keyword);
805  }
806  } else {
807  $replacements[$keyword] = $this->getKeywordReplacement($keyword);
808  }
809  }
810 
811  replace_keywords($default_value, $replacements);
812  replace_global_keywords($default_value);
813 
814  // If the default value is a dud, blank it
815  if (!$this->hasValidValue($default_value, TRUE)) {
816  $default_value = '';
817  }
818  }
819 
820  return $default_value;
821  }//end if (is_null($this->value))
822 
823  if (is_array($this->value)) return $this->value;
824 
825  //return htmlspecialchars($this->value, ENT_NOQUOTES);
826  return $this->value;
827 
828  }//end getValue()
829 
830 
837  function getSummary()
838  {
839  return $this->getValue();
840 
841  }//end getSummary()
842 
843 
850  function getExtraData()
851  {
852  return $this->extra_data;
853 
854  }//end getSummary()
855 
856 
863  function getXML()
864  {
865  ob_start();
866 
867  echo '<text_q id="'.addslashes($this->id).'" name="'.htmlspecialchars($this->attr('name')).'">';
868  echo htmlspecialchars($this->getSummary());
869  echo '</text_q>';
870 
871  $contents = ob_get_contents();
872  ob_end_clean();
873 
874  return $contents;
875 
876  }//end getXML()
877 
878 
885  function getErrors()
886  {
887  return $this->failed_rules;
888 
889  }//end getErrors()
890 
891 
892 //-- PERMISSIONS/ACCESS (DEFERS TO FORM/FORM SECTION) --//
893 
894 
904  function readAccess($assetids=Array())
905  {
906  $form = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->_formid);
907  if (is_null($form)) return FALSE;
908 
909  return $form->readAccess($assetids);
910 
911  }//end readAccess()
912 
913 
926  function writeAccess($lock_type='', $assetids=Array())
927  {
928  $form = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->_formid);
929  if (is_null($form)) return FALSE;
930 
931  return $form->writeAccess($lock_type, $assetids);
932 
933  }//end writeAccess()
934 
935 
948  function adminAccess($lock_type='', $assetids=Array())
949  {
950  $form = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->_formid);
951  if (is_null($form)) return FALSE;
952 
953  return $form->adminAccess($lock_type, $assetids);
954 
955  }//end adminAccess()
956 
957 
971  function liveEditAccess($lock_type)
972  {
973  if (empty($lock_type) || $this->canLiveEdit($lock_type)) {
974  return $this->checkAccess(SQ_PERMISSION_WRITE, $lock_type);
975  }
976  return FALSE;
977 
978  }//end liveEditAccess()
979 
980 
996  function checkAccess($perm, $lock_type, $assetids=Array())
997  {
998  $form = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->_formid);
999  if (is_null($form)) return FALSE;
1000 
1001  return $form->checkAccess($perm, $lock_type, $assetids);
1002 
1003  }//end checkAccess()
1004 
1005 
1017  function _checkPermissionAccess($perm, $assetids=Array())
1018  {
1019  $form = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->_formid);
1020  if (is_null($form)) return FALSE;
1021 
1022  return $form->_checkPermissionAccess($perm, $assetids);
1023 
1024  }//end _checkPermissionAccess()
1025 
1026 
1035  function stickValue($value)
1036  {
1037  $cookie_name = $this->_getCookieName();
1038  $_SESSION['SQ_FORM_STICKY'][$GLOBALS['SQ_SYSTEM']->user->id][$cookie_name] = $value;
1039 
1040  }//end stickValue()
1041 
1042 
1049  function unstickValue()
1050  {
1051  $cookie_name = $this->_getCookieName();
1052  unset($_SESSION['SQ_FORM_STICKY'][$GLOBALS['SQ_SYSTEM']->user->id][$cookie_name]);
1053 
1054  }//end unstickValue()
1055 
1056 
1063  function getStickyValue()
1064  {
1065  $cookie_name = $this->_getCookieName();
1066 
1067  if (!isset($_SESSION['SQ_FORM_STICKY'][$GLOBALS['SQ_SYSTEM']->user->id][$cookie_name])) {
1068  return NULL;
1069  }
1070 
1071  $value = $_SESSION['SQ_FORM_STICKY'][$GLOBALS['SQ_SYSTEM']->user->id][$cookie_name];
1072 
1073  // If the sticky value is a dud, blank it
1074  if (!$this->hasValidValue($value, TRUE)) {
1075  $value = NULL;
1076  }
1077 
1078  return $value;
1079 
1080  }//end getStickyValue()
1081 
1082 
1089  function _getCookieName()
1090  {
1091  // We use make_valid_web_paths() to make the name more safe to use
1092  list($question_name) = make_valid_web_paths(Array($this->name));
1093  return $question_name;
1094 
1095  }//end _getCookieName()
1096 
1097 
1109  function processStatusChange($new_status, $update_parent=TRUE)
1110  {
1111  return TRUE;
1112 
1113  }//end processStatusChange()
1114 
1115 
1122  function getHtmlLabel()
1123  {
1124  ob_start();
1125  label($this->attr('name'), 'q'.str_replace(':', '_', $this->id));
1126  $label = ob_get_contents();
1127  ob_end_clean();
1128  return $label;
1129 
1130  }//end getHtmlLabel()
1131 
1132 
1133 }//end class
1134 ?>