Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
form_section.inc
1 <?php
17 require_once SQ_INCLUDE_PATH.'/asset.inc';
18 require_once SQ_CORE_PACKAGE_PATH.'/interfaces/bridge/bridge.inc';
19 
31 class Form_Section extends Asset implements Bridge
32 {
33 
38  var $question_index = 0;
39 
44  var $question_col_width = 0;
45 
50  var $answer_col_width = 0;
51 
56  var $current_answers = Array();
57 
58 
63  var $extra_data = Array();
64 
65 
70  var $submission_errors = Array();
71 
72 
79  function Form_Section($assetid=0)
80  {
81  $this->_ser_attrs = TRUE;
82  $this->Asset($assetid);
83 
84  }//end constructor
85 
86 
87 //-- GENERIC ASSET FUNCTIONS --//
88 
89 
104  function _updated($update_parents=TRUE)
105  {
106  if (!parent::_updated($update_parents)) return FALSE;
107  if (SQ_IN_BACKEND) return $this->fileRegeneration();
108  return TRUE;
109 
110  }//end _updated()
111 
112 
120  function _getAllowedLinks()
121  {
122  return Array(
123  SQ_LINK_TYPE_2 => Array(
124  'form_section' => Array(
125  'card' => 'M',
126  'exclusive' => FALSE,
127  ),
128  'bodycopy' => Array(
129  'card' => '1',
130  'exclusive' => FALSE,
131  ),
132  ),
133  SQ_LINK_TYPE_3 => Array(
134  'bodycopy' => Array(
135  'card' => '1',
136  'exclusive' => FALSE,
137  ),
138  ),
139  );
140 
141  }//end _getAllowedLinks()
142 
143 
144 
165  public function prepareLink(Asset $asset, $side_of_link, &$link_type, &$value, &$sort_order, &$dependant, &$exclusive)
166  {
167  // if this is a bodycopy container then we need to make it a dependant link
168  if ($side_of_link == 'major' && ($asset instanceof Form_Section) && $dependant != '1') {
169  $dependant = '1';
170  $link_type = SQ_LINK_TYPE_2;
171  return TRUE;
172  }
173 
174  return FALSE;
175 
176  }//end prepareLink()
177 
178 
179 
180  /* Clones certain specified components of the asset
181  *
182  * @param object &$clone the clone shell
183  * @param array $components the wanted components to clone eg.
184  * <pre>
185  * Array(
186  * 'attributes',
187  * 'metadata_schemas',
188  * 'metadata',
189  * 'workflow',
190  * 'permissions',
191  * 'data',
192  * 'content_tags',
193  * 'roles',
194  * );
195  * or alternately
196  * Array('all');
197  * </pre>
198  * @param boolean $override whether or not to override the existing permission, metadata schemas, workflow schemas with the new ones.
199  *
200  * @return boolean
201  * @access public
202  * @see asset::cloneComponents()
203  */
204  function cloneComponents(&$clone, $components, $override=FALSE)
205  {
206  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
207  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
208 
209  if (!parent::cloneComponents($clone, $components, $override)) {
210  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
211  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
212  return FALSE;
213  }
214 
215  if (in_array('attributes', $components) || in_array('all', $components)) {
216  $sort_order = $this->attr('sort_order');
217  $sort_order = str_replace($this->id.':q', $clone->id.':q', $sort_order);
218 
219  // save the information
220  $clone->setAttrValue('sort_order', $sort_order);
221  $clone->saveAttributes();
222 
223  // Now remap any parent forms, but only if we are being cloned along with a form clone:
224  // remap to the cloned form. If we are being clone within a form, we will not remap.
225  $form_parents = $GLOBALS['SQ_SYSTEM']->am->getDependantParents($this->id, 'form_email', FALSE);
226  $clone_form_parents = $GLOBALS['SQ_SYSTEM']->am->getDependantParents($clone->id, 'form_email', FALSE);
227 
228  $clone_form_parents = array_diff($clone_form_parents, $form_parents);
229 
230  foreach ($clone_form_parents as $parent_assetid) {
231  $form = $GLOBALS['SQ_SYSTEM']->am->getAsset($parent_assetid);
232 
233  $recip = unserialize($form->attr('recipient_emails_format'));
234  $recip = str_replace('%response_'.$this->id.'_', '%response_'.$clone->id.'_', $recip);
235  $rec = unserialize($form->attr('receipt_emails_format'));
236  $rec = str_replace('%response_'.$this->id.'_', '%response_'.$clone->id.'_', $rec);
237  $staf = unserialize($form->attr('staf_format'));
238  $staf = str_replace('%response_'.$this->id.'_', '%response_'.$clone->id.'_', $staf);
239  $sel = $form->attr('selective_emails');
240  foreach ($sel as $key => $null) {
241  $sel[$key]['address'] = str_replace('%response_'.$this->id.'_', '%response_'.$clone->id.'_', $sel[$key]['address']);
242  $sel[$key]['assetid'] = str_replace($this->id.':q', $clone->id.':q', $sel[$key]['assetid']);
243  }
244 
245  // field selections for STAF and receipt
246  $staf_field = str_replace($this->id.':q', $clone->id.':q', $form->attr('staf_field'));
247  $receipt_field = str_replace($this->id.':q', $clone->id.':q', $form->attr('receipt_field'));
248 
249  // save the information
250  $form->setAttrValue('recipient_emails_format', serialize($recip));
251  $form->setAttrValue('receipt_emails_format', serialize($rec));
252  $form->setAttrValue('staf_format', serialize($staf));
253  $form->setAttrValue('selective_emails', $sel);
254 
255  $form->setAttrValue('staf_field', $staf_field);
256  $form->setAttrValue('receipt_field', $receipt_field);
257 
258  $form->saveAttributes();
259  }
260  }//end if
261 
262  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
263  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
264  return TRUE;
265 
266  }//end cloneComponents()
267 
268 
279  function _getName($short_name=FALSE, $contextid=NULL)
280  {
281  // No context specified, using the current context
282  if ($contextid === NULL) {
283  $contextid = $GLOBALS['SQ_SYSTEM']->getContextId();
284  }//end if
285 
286  // Obtain the attribute value for Name from the specified Context
287  $values = $GLOBALS['SQ_SYSTEM']->am->getAttributeValuesByName('name', $this->type(), Array($this->id), $contextid);
288  if (empty($values) === TRUE) {
289  return parent::_getName($short_name, $contextid);
290  } else {
291  return $values[$this->id];
292  }
293 
294  }//end _getName()
295 
296 
297 //-- BRIDGE FUNCTIONS --//
298 
299 
314  function getAsset($shadowid, $type_code='', $mute_errors=FALSE)
315  {
316  $asset = NULL;
317  $id_parts = explode(':', $shadowid);
318 
319  if (isset($id_parts[1])) {
320  $shadowid = $id_parts[1];
321  } else {
322  return $asset;
323  }
324 
325  // Questions follow the format '<assetid>:q<shadowid>'
326  if ($shadowid{0} == 'q') {
327 
328  // pick up the question in question (!)
329  $questions = $this->attr('questions');
330  $questionid = substr($shadowid, 1, strlen($shadowid));
331  if (empty($questions[$questionid])) return $asset;
332 
333  $q_type_code = $questions[$questionid]['question_type_code'];
334 
335  // not fussed about type code?
336  if (empty($type_code)) $type_code = $q_type_code;
337 
338  // only give the asset back if of the right type code
339  if (in_array($q_type_code, $GLOBALS['SQ_SYSTEM']->am->getTypeDescendants($type_code, TRUE))) {
340  $questions[$questionid]['questionid'] = $questionid;
341  $GLOBALS['SQ_SYSTEM']->am->includeAsset($q_type_code);
342 
343  // this eval returns the necessary object for the question type
344  $asset = new $q_type_code($this->id, $questions[$questionid]);
345  }
346  }
347 
348  return $asset;
349 
350  }//end getAsset()
351 
352 
364  function getLinkById($linkid, $assetid=0, $side_of_link='major')
365  {
366  if (!is_numeric($assetid)) {
367  if ($side_of_link == 'minor') {
368  return Array(
369  'linkid' => 0,
370  'majorid' => $this->id,
371  'minorid' => $this->id.':'.$assetid,
372  'major_type_code' => $this->type(),
373  'value' => '',
374  'link_type' => SQ_LINK_TYPE_2,
375  'is_dependant' => TRUE,
376  'is_exclusive' => FALSE,
377  'sort_order' => 0,
378  'locked' => 0,
379  );
380  }
381  return Array();
382  }
383 
384  $id_parts = explode(':', $linkid);
385  if (isset($id_parts[1]) && isset($id_parts[2])) {
386  $looking_for = $id_parts[1].':'.$id_parts[2];
387  $tmp_question_nb = str_replace('q', '', $id_parts[2]);
388  $questions = $this->attr('questions');
389  if (isset($questions[$tmp_question_nb])) {
390  return Array(
391  'linkid' => $linkid,
392  'majorid' => $this->id,
393  'minorid' => $this->id.':q'.$tmp_question_nb,
394  'minor_type_code' => $questions[$tmp_question_nb]['question_type_code'],
395  'value' => '',
396  'link_type' => SQ_LINK_TYPE_2,
397  'is_dependant' => TRUE,
398  'is_exclusive' => FALSE,
399  'sort_order' => 0,
400  'locked' => 0,
401  );
402  }
403  }
404 
405  return Array();
406 
407  }//end getLinkById()
408 
409 
431  function countLinks($assetid, $side_of_link='major', $link_types=0, $type_code='', $strict_type_code=TRUE, $ignore_linkid=0)
432  {
433  $tmp_questions = $this->attr('questions');
434  if (isset($tmp_questions)) {
435  return count($tmp_questions);
436  }
437  return 0;
438 
439  }//end countLinks()
440 
441 
451  function deleteAssetLink($linkid, $moving=FALSE)
452  {
453  $id_parts = explode(':', $linkid);
454  if (isset($id_parts[1]) && isset($id_parts[2])) {
455  $question_to_delete = $id_parts[1].':'.$id_parts[2];
456  $asset_to_delete = $this->getAsset($question_to_delete);
457  if ($asset_to_delete != NULL) {
458  return $this->deleteQuestion($asset_to_delete);
459  }
460  }
461 
462  return FALSE;
463 
464  }//end deleteAssetLink()
465 
466 
487  function getLinks($assetid, $link_types, $type_code='', $strict_type_code=TRUE, $side_of_link='major', $sort_by=NULL)
488  {
489  // there are no links away from the shadow asset (ie:questions)
490  $links = Array();
491 
492  if (!is_numeric($assetid)) {
493  if ($side_of_link == 'minor') {
494  $return_link = FALSE;
495 
496  if ($type_code == '') {
497  // not fussed what we're getting, so return me
498  $return_link = TRUE;
499  } else if ($strict_type_code) {
500  // strict type code check
501  $return_link = ($type_code == $this->type());
502  } else {
503  $return_link = (in_array($this->type(), $GLOBALS['SQ_SYSTEM']->am->getTypeDescendants($type_code, TRUE)));
504  }
505 
506  if ($return_link) {
507  $links[] = Array(
508  'linkid' => 0,
509  'majorid' => $this->id,
510  'minorid' => $this->id.':'.$assetid,
511  'major_type_code' => $this->type(),
512  'value' => '',
513  'link_type' => SQ_LINK_TYPE_2,
514  'is_dependant' => TRUE,
515  'is_exclusive' => FALSE,
516  'sort_order' => 0,
517  'locked' => 0,
518  );
519  }
520  }
521 
522  return $links;
523  }
524 
525  $new_sort_order = 0;
526 
527  // There are no real links made at a section level
528  // but we also need question shadow asset links
529  if ($link_types & SQ_LINK_TYPE_2) {
530  $questions = $this->attr('questions');
531 
532  // keep a cache of asset type codes if we are checking on a non-strict
533  // type code, to stop us calling getTypeDescendants() all the time
534  // (not needed if strict type check or if not fussed)
535  $get_question_types = Array();
536 
537  foreach ($questions as $questionid => $data) {
538  if ($type_code == '') {
539  // not fussed what we're getting, so return me
540  $get_question = TRUE;
541  } else if ($strict_type_code) {
542  // strict type code check
543  $get_question = ($type_code == $data['question_type_code']);
544  } else {
545  // if we've already cached whether this question type is to be
546  // returned, then we don't need to look it up again
547  if (isset($get_question_types[$data['question_type_code']])) {
548  $get_question = $get_question_types[$data['question_type_code']];
549  $get_question_types[$data['question_type_code']] = $get_question;
550  } else {
551  $get_question = (in_array($data['question_type_code'], $GLOBALS['SQ_SYSTEM']->am->getTypeDescendants($type_code, TRUE)));
552  }
553  }
554 
555  if ($get_question) {
556  $links[] = Array(
557  'linkid' => 0,
558  'majorid' => $this->id,
559  'minorid' => $this->id.':q'.$questionid,
560  'minor_type_code' => $data['question_type_code'],
561  'value' => '',
562  'link_type' => SQ_LINK_TYPE_2,
563  'is_dependant' => TRUE,
564  'is_exclusive' => FALSE,
565  'sort_order' => $new_sort_order,
566  'locked' => 0,
567  );
568  }
569  $new_sort_order++;
570  }//end foreach
571  }//end if
572 
573  if (is_null($sort_by)) $sort_by = 'sort_order';
574 
575  uasort($links, create_function('$a,$b','return $a["'.$sort_by.'"] > $b["'.$sort_by.'"];'));
576 
577  return $links;
578 
579  }//end getLinks()
580 
581 
588  function getAssetMapLinks()
589  {
590  $new_sort_order = 0;
591  $old_links = $this->getLinks($this->id, SQ_SC_LINK_BACKEND_NAV);
592  $links = Array();
593  while (!empty($old_links)) {
594  array_push($links, array_shift($old_links));
595  }
596 
597  $questions = $this->attr('questions');
598 
599  foreach (array_keys($links) as $i) {
600 
601  $link =& $links[$i];
602 
603  // we would remove real links at this point, but Form Section doesn't
604  // have any real links underneath it
605 
606  // mould it all to the asset map's liking
607  $link['url'] = '';
608  $link['path'] = '';
609  $link['num_kids'] = 0;
610  $link['accessible'] = 1;
611 
612  $link['assetid'] = $link['minorid'];
613  $link['type_code'] = $link['minor_type_code'];
614  $link['linkid'] = $link['majorid'].':'.$link['minorid'];
615 
616  $questionid = str_replace($this->id.':q', '', $link['assetid']);
617 
618  // make name and short name the same
619  $link['name'] = $questions[$questionid]['attributes']['name'];
620  $link['short_name'] = $link['name'];
621 
622  // make the status the same as the form's one
623  $link['status'] = $this->status;
624 
625  unset($link['minor_type_code']);
626  unset($link['majorid']);
627  unset($link['minorid']);
628  unset($link['value']);
629  unset($link['is_exclusive']);
630 
631  }//end foreach
632 
633  return $links;
634 
635  }//end getAssetMapLinks()
636 
637 
652  function getParents($shadowid, $type_code='', $strict_type_code=TRUE)
653  {
654  // basically get the parents of the section, and add itself
655  $ret_val = $GLOBALS['SQ_SYSTEM']->am->generateGetParentsQuery($this->id, $type_code, $strict_type_code);
656  if (empty($ret_val)) return Array();
657 
658  $query = MatrixDAL::preparePdoQuery(implode(' ', $ret_val['sql_array']));
659  foreach ($ret_val['bind_vars'] as $bind_var => $bind_value) {
660  MatrixDAL::bindValueToPdo($query, $bind_var, $bind_value);
661  }
662 
663  $queried_parents = MatrixDAL::executePdoAssoc($query);
664  $parents = Array();
665 
666  foreach ($queried_parents as $queried_parent) {
667  $parents[$queried_parent['majorid']] = $queried_parent['type_code'];
668  }
669 
670  // TODO: add this asset only if it meets type code requirements
671  $parents[$this->id] = $this->type();
672 
673  return $parents;
674 
675  }//end getParents()
676 
677 
700  function getChildren($assetid, $type_code='', $strict_type_code=TRUE, $dependant=NULL, $sort_by=NULL)
701  {
702  // no shadow assets have children in this asset
703  if (!is_numeric($assetid)) return Array();
704 
705  if (!is_array($type_code)) {
706  if (empty($type_code)) {
707  $type_code = Array();
708  } else {
709  $type_code = Array($type_code);
710  }
711  }
712 
713  $children = Array();
714 
715  // we don't have to get any real links, sections only have question children
716 
717  // now questions
718  $entries = $this->attr('questions');
719  if (empty($type_code)) {
720  foreach ($entries as $questionid => $data) {
721  $children[$this->id.':q'.$questionid] = Array(Array('type_code' => $data['question_type_code']));
722  }
723  } else {
724  foreach ($type_code as $this_type_code) {
725  foreach ($entries as $questionid => $data) {
726  if ($strict_type_code) {
727  if ($data['question_type_code'] == $this_type_code) {
728  $children[$this->id.':q'.$questionid] = $data['question_type_code'];
729  }
730  } else {
731  $type_desc = $GLOBALS['SQ_SYSTEM']->am->getTypeDescendants($this_type_code) + Array($this_type_code);
732  if (in_array($data['question_type_code'], $type_desc)) {
733  $children[$this->id.':q'.$questionid] = Array(Array('type_code' => $data['question_type_code']));
734  }
735  }
736  }
737  }
738  }
739 
740  if (!is_null($sort_by)) {
741  uasort($children, create_function('$a,$b','return $a["'.$sort_by.'"] > $b["'.$sort_by.'"]'));
742  }
743 
744  return $children;
745 
746  }//end getChildren()
747 
748 
759  function getLineageFromURL($assetid, $protocol, $url)
760  {
761  return Array();
762 
763  }//end getLineageFromURL()
764 
765 
774  function getAssetMapAssetInfo($assetid)
775  {
776  return Array();
777 
778  }//end getAssetMapAssetInfo()
779 
780 
781 //-- CONTENT FILE GENERATION --//
782 
783 
792  function fileRegeneration($form_regen=TRUE)
793  {
794  // we want to regen this section
795  $edit = $this->getEditFns();
796  $edit->generateContentFile($this);
797 
798  // sometimes, we might not want to regen the form (eg. if the form is calling this function!)
799  if ($form_regen) {
800  // then, get all the links of this guy's form parents
801  $links = $GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, SQ_LINK_TYPE_2, 'form', FALSE, 'minor');
802 
803  foreach ($links as $l) {
804  $form = $GLOBALS['SQ_SYSTEM']->am->getAsset($l['majorid'], $l['major_type_code']);
805  if (is_null($form)) continue;
806  if (!$form->_updated()) {
807  unset($form);
808  return FALSE;
809  }
810  unset($form);
811  }
812  }//end if
813 
814  return TRUE;
815 
816  }//end fileRegeneration()
817 
818 
819 
820 
830  function generateJSCode()
831  {
832  $questions = $this->getQuestions();
833  $code = ''; // blank sheet of code to start with
834 
835  foreach ($questions as $q_id => $question) {
836  $q_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->id.':q'.$q_id);
837  $q_code = $q_asset->generateJSCode();
838 
839  $code .= $q_code.(empty($q_code) ? '' : "\n");
840  }
841 
842  return $code;
843 
844  }//end generateJSCode()
845 
846 
847 //-- QUESTION MANAGEMENT --//
848 
849 
858  function getQuestions($force=FALSE)
859  {
860  return $this->attr('questions');
861 
862  }//end getQuestions()
863 
864 
873  function getQuestionAssets($type_code=NULL)
874  {
875  $am = $GLOBALS['SQ_SYSTEM']->am;
876  $result = Array();
877 
878  $questions = $this->attr('questions');
879  if (!empty($questions)) {
880  foreach ($questions as $q_id => $question) {
881  if (empty($type_code) || ($question['question_type_code'] === $type_code)) {
882  $id = $this->id.':q'.$q_id;
883  $result[$id] = $am->getAsset($id);
884  }
885  }
886  }
887 
888  return $result;
889 
890  }//end getQuestionAssets()
891 
892 
901  function getAllQuestionAssets($type_code=NULL)
902  {
903  $questions = $this->getQuestionAssets($type_code);
904 
905  // Get questions within nested sections
906  $nested_sections = $this->getSectionLinks();
907  foreach ($nested_sections as $section_link) {
908  $section = $GLOBALS['SQ_SYSTEM']->am->getAsset($section_link['minorid']);
909  $questions += $section->getAllQuestionAssets($type_code);
910  }
911 
912  return $questions;
913 
914  }//end getQuestionAssets()
915 
924  function getQuestionByID($questionid)
925  {
926  $questions = $this->getQuestions();
927  return (isset($questions[$questionid])) ? $questions[$questionid] : Array();
928 
929  }//end getQuestionByID()
930 
931 
940  function &getQuestionByOrder($orderid)
941  {
942  $sort_order = $this->attr('sort_order');
943  $question = $GLOBALS['SQ_SYSTEM']->am->getAsset($sort_order[$orderid]);
944  return $question;
945 
946  }//end getQuestionByOrder()
947 
948 
959  function attachQuestion($type_code)
960  {
961  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
962  $db = MatrixDAL::getDb();
963 
964  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
965 
966  $questions = $this->getQuestions();
967  if ($this->attr('next_questionid') > 0) {
968  $new_key = $this->attr('next_questionid');
969  } else if (empty($questions)) {
970  $new_key = 1;
971  } else {
972  $new_key = max(array_keys($questions)) + 1;
973  }
974 
975  $questions[$new_key] = Array(
976  'question_type_code' => $type_code,
977  'attributes' => Array(
978  'name' => 'Question '.(count($questions)+1),
979  ),
980  );
981 
982  $this->setAttrValue('questions', $questions);
983  $this->setAttrValue('next_questionid', $new_key + 1);
984 
985  $sort_order = $this->attr('sort_order');
986 
987  if (empty($sort_order)) {
988  $new_index = 0;
989  } else {
990  $new_index = max(array_keys($sort_order))+1;
991  }
992  $sort_order[$new_index] = $this->id.':q'.$new_key;
993  $this->setAttrValue('sort_order', $sort_order);
994 
995  if (!$this->saveAttributes()) {
996  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
997  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
998  return FALSE;
999  }
1000  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
1001  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1002 
1003  return TRUE;
1004 
1005  }//end attachQuestion()
1006 
1007 
1018  function deleteQuestion(&$question)
1019  {
1020  if ($question->_questionid == 0) return FALSE;
1021 
1022  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
1023  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
1024  $questions = $this->getQuestions();
1025 
1026  unset($questions[$question->_questionid]);
1027 
1028  $this->setAttrValue('questions', $questions);
1029 
1030  $sort_order = $this->attr('sort_order');
1031 
1032  // remove it from the sort order - it does not matter that there are gaps
1033  $old_index = array_search($question->id, $sort_order);
1034  unset($sort_order[$old_index]);
1035 
1036  // unset the question
1037  unset($question);
1038 
1039  $this->setAttrValue('sort_order', $sort_order);
1040 
1041  if (!$this->saveAttributes()) {
1042  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
1043  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1044  return FALSE;
1045  }
1046  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
1047  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1048 
1049  return TRUE;
1050 
1051  }//end deleteQuestion()
1052 
1053 
1060  function getQuestionCount()
1061  {
1062  $questions = $this->getQuestions();
1063  return count($questions);
1064 
1065  }//end getQuestionCount()
1066 
1067 
1068 //-- SECTION MANAGEMENT --//
1069 
1070 
1077  function getSectionLinks()
1078  {
1079  // This is what one would find in Asset_Manager::&getAsset() except we can't call that,
1080  // lest we spin off into an infinite loop involving OUR &getAsset().
1081  $value = Array(
1082  'link_value' => Array(''),
1083  'equal' => TRUE,
1084  );
1085  $sql_query = $GLOBALS['SQ_SYSTEM']->am->generateGetLinksQuery($this->id, SQ_LINK_TYPE_2, 'form_section', TRUE, 'major', $value, '1', '0');
1086  if (empty($sql_query)) return Array();
1087  $sql_query['sql_array']['select'] .= ', l.majorid'; // we need the major id too
1088  $db = MatrixDAL::getDb();
1089 
1090  try {
1091  $sql = implode(' ', $sql_query['sql_array']);
1092  $query = MatrixDAL::preparePdoQuery($sql);
1093  foreach ($sql_query['bind_vars'] as $bind_var => $bind_value) {
1094  MatrixDAL::bindValueToPdo($query, $bind_var, $bind_value);
1095  }
1096  $result = MatrixDAL::executePdoAssoc($query);
1097  } catch (Exception $e) {
1098  throw new Exception('Unable to get section links attached to form_section: '.$this->id.' due to database error: '.$e->getMessage());
1099  }
1100  return $result;
1101 
1102  }//end getSectionLinks()
1103 
1104 
1105 //-- PAINTING FUNCTIONS --//
1106 
1107 
1122  function printBody(Form $form, $submit_buttons=FALSE)
1123  {
1124  // if format bodycopy enabled, print with that, otherwise print default
1125  if ($this->isFormatBodycopyEnabled()) {
1126  $this->printCustomFormat($form, $submit_buttons);
1127  } else {
1128  $this->printDefaultFormat($form, $submit_buttons);
1129  }
1130 
1131  }//end printBody()
1132 
1133 
1143  function printDefaultFormat(Form $form, $submit_buttons=FALSE)
1144  {
1145  // Default format definitions to keep the section looking in line with
1146  // its current parent form
1147  $form_width = $form->attr('form_width');
1148  $question_width = $form->attr('question_col_width');
1149  $response_width = $form->attr('answer_col_width');
1150 
1151  include $this->data_path.'/content_file.php';
1152 
1153  if ($submit_buttons) {
1154  ob_start();
1155  ?>
1156  <table width="<?php echo $form_width; ?>">
1157  <tr>
1158  <td width="<?php echo $question_width; ?>">&nbsp;</td>
1159  <td width="<?php echo $response_width; ?>">
1160  %submit_button%
1161  %reset_button%
1162  </td>
1163  </tr>
1164  </table>
1165  <?php
1166  $content = ob_get_clean();
1167 
1168  require_once SQ_FUDGE_PATH.'/general/text.inc';
1169 
1170  $keyword_list = extract_keywords($content);
1171  if (!empty($keyword_list)) {
1172  foreach ($keyword_list as $keyword) {
1173  if (isset($replacements[$keyword])) continue;
1174  $replacements[$keyword] = $form->getKeywordReplacement($keyword);
1175  }
1176  replace_keywords($content, $replacements);
1177  }
1178  echo $content;
1179  }
1180 
1181  }//end printCustomFormat()
1182 
1183 
1193  function printCustomFormat(Form $form, $submit_buttons=FALSE)
1194  {
1195  // return custom bodycopy but ONLY if enabled
1196  $format_bodycopy = $this->getFormatBodycopy(TRUE);
1197  if (!$format_bodycopy) {
1198  // Halt the display if the custom format is being asked for, but does
1199  // not exist
1200  trigger_error('Form Section asked for a custom format when it has no format bodycopy enabled. This should not happen.', E_USER_ERROR);
1201  }
1202 
1203  $keywords = $format_bodycopy->getKeywords();
1204  $replacements = Array();
1205 
1206  foreach ($keywords as $keyword) {
1207  // Try to get a specific display keyword replacement, to handle
1208  // %question_*% and nested %section_*% keywords
1209  $replacement = $form->getDisplayKeywordReplacement($keyword);
1210 
1211  if (is_null($replacement)) {
1212  // If we couldn't get a keyword from there, use the generic
1213  // keyword replacement form - this will pick up CAPTCHA, errors,
1214  // submit/reset replacements
1215  $replacement = $form->getKeywordReplacement($keyword);
1216  }
1217 
1218  if (!is_null($replacement)) {
1219  $replacements[$keyword] = $replacement;
1220  }
1221 
1222  }//end foreach
1223 
1224  $format_bodycopy->setKeywordReplacements($replacements);
1225  $format_bodycopy->printBody();
1226 
1227  }//end printCustomFormat()
1228 
1229 
1230  function printSemanticSummary()
1231  {
1232  if ($this->getQuestionCount() > 0) {
1233  echo '<dl>'."\n";
1234  foreach ($this->attr('sort_order') as $sort_order => $question_assetid) {
1235 
1236  // Make sure that we own this question
1237  if ($this->id == strtok($question_assetid, ':')) {
1238  $question = $GLOBALS['SQ_SYSTEM']->am->getAsset($question_assetid);
1239  echo '<dt>'.$question->attr('name').'</dt>'."\n";
1240  echo '<dd>'.htmlentities($question->getSummary(), ENT_NOQUOTES).'</dd>'."\n";
1241  }
1242 
1243  }
1244  echo '</dl>'."\n";
1245  }
1246 
1247  // now nested sections - get them to print itself out, recursively
1248  $section_links = $this->getSectionLinks();
1249  if (count($section_links) > 0) {
1250  echo '<ul>'."\n";
1251  }
1252 
1253  foreach ($section_links as $section_link) {
1254  echo '<li>'."\n";
1255  $section = $GLOBALS['SQ_SYSTEM']->am->getAsset($section_link['minorid']);
1256  echo '<strong>'.htmlentities($section->attr('name'), ENT_NOQUOTES).'</strong>'."\n";
1257  $section->printSemanticSummary();
1258  echo '</li>'."\n";
1259  }
1260 
1261  if (count($section_links) > 0) {
1262  echo '</ul>'."\n";
1263  }
1264 
1265  }//end printSemanticSummary()
1266 
1267 
1268 //-- CUSTOM FORMAT BODYCOPY FUNCTIONS --//
1269 
1270 
1283  function createFormatBodycopy($enable_on_create=TRUE)
1284  {
1285  $_bodycopy_link = $GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, SQ_LINK_TYPE_2 | SQ_LINK_TYPE_3, 'bodycopy', 'format');
1286  $bodycopy_link = reset($_bodycopy_link);
1287 
1288  // we already have a bodycopy link?!
1289  if ($bodycopy_link) {
1290  return FALSE;
1291  } else {
1292  $GLOBALS['SQ_SYSTEM']->am->includeAsset('bodycopy');
1293 
1294  $link_type = ($enable_on_create ? SQ_LINK_TYPE_2 : SQ_LINK_TYPE_3);
1295 
1296  $asset = new Bodycopy();
1297  $copy_link = Array(
1298  'asset' => &$this,
1299  'value' => 'format',
1300  'link_type' => $link_type,
1301  'is_dependant' => 1,
1302  'is_exclusive' => 1,
1303  );
1304 
1305  $asset->setAttrValue('name', 'Format Bodycopy');
1306  if (!$asset->create($copy_link)) return FALSE;
1307 
1308  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
1309  unset($asset);
1310  }
1311 
1312  return TRUE;
1313 
1314  }//end createFormatBodycopy()
1315 
1316 
1327  function &getFormatBodycopy($only_if_enabled=TRUE)
1328  {
1329  $asset = NULL;
1330 
1331  if ($only_if_enabled) {
1332  $link_types = SQ_LINK_TYPE_2;
1333  } else {
1334  $link_types = SQ_LINK_TYPE_2 | SQ_LINK_TYPE_3;
1335  }
1336 
1337  $tmp_bodycopy = $GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, $link_types, 'bodycopy', 'format');
1338  $bodycopy_link = reset($tmp_bodycopy);
1339 
1340  if ($bodycopy_link) {
1341  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($bodycopy_link['minorid'], 'bodycopy');
1342  }
1343 
1344  return $asset;
1345 
1346  }//end getFormatBodycopy()
1347 
1348 
1356  {
1357  $link_types = SQ_LINK_TYPE_2 | SQ_LINK_TYPE_3;
1358 
1359  $_bodycopy_link = $GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, $link_types, 'bodycopy', 'format');
1360  $bodycopy_link = reset($_bodycopy_link);
1361 
1362  if ($bodycopy_link) {
1363  return ($bodycopy_link['link_type'] == SQ_LINK_TYPE_2);
1364  } else {
1365  return FALSE;
1366  }
1367 
1368 
1369  }//end isFormatBodycopyEnabled()
1370 
1371 
1381  function setUseFormatBodycopy($enabled)
1382  {
1383  $tmp_bodycopy = $GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, SQ_LINK_TYPE_2 | SQ_LINK_TYPE_3, 'bodycopy', 'format');
1384  $bodycopy_link = reset($tmp_bodycopy);
1385 
1386  if (!$bodycopy_link) {
1387  // no bodycopy yet? If we're trying to set to disabled, then we don't
1388  // need to do anything - if not then we need to create it
1389  if ($enabled) {
1390  if (!$this->createFormatBodycopy()) return FALSE;
1391  }
1392  } else {
1393  // set link type to either TYPE_2 if enabled or TYPE_3 if disabled
1394  $new_link_type = ($enabled) ? SQ_LINK_TYPE_2 : SQ_LINK_TYPE_3;
1395  if ($bodycopy_link['link_type'] != $new_link_type) {
1396  $GLOBALS['SQ_SYSTEM']->am->updateLink($bodycopy_link['linkid'], $new_link_type);
1397  $this->fileRegeneration();
1398  }
1399  }
1400 
1401  return TRUE;
1402 
1403  }//end setUseFormatBodycopy()
1404 
1405 
1406 //-- KEYWORD PROVISION --//
1407 
1408 
1419  function onRequestKeywords(&$broadcaster, $vars=Array())
1420  {
1421  $vars['keywords'] = isset($vars['keywords']) ? $vars['keywords'] : Array();
1422  $keywords = Array();
1423 
1424  if ($this->isFormatBodycopyEnabled()) {
1425  $keywords = $this->getDisplayKeywords();
1426  }
1427 
1428  $vars['keywords'] = array_merge($vars['keywords'], $keywords);
1429 
1430  }//end onRequestKeywords()
1431 
1432 
1446  function getDisplayKeywords($include_submit_keywords=TRUE)
1447  {
1448  $questions = $this->attr('questions');
1449  $keywords = Array();
1450 
1451  foreach ($questions as $shadowid => $question) {
1452  $keywords['question_field_'.$this->id.'_q'.$shadowid] = 'Question Field: '.ellipsisize($question['attributes']['name'], 30).' ('.$this->id.':q'.$shadowid.')';
1453  $keywords['question_name_'.$this->id.'_q'.$shadowid] = 'Question Name: '.ellipsisize($question['attributes']['name'], 30).' ('.$this->id.':q'.$shadowid.')';
1454  $keywords['question_id_'.$this->id.'_q'.$shadowid] = 'Question ID: '.ellipsisize($question['attributes']['name'], 30).' ('.$this->id.':q'.$shadowid.')';
1455  $keywords['question_note_'.$this->id.'_q'.$shadowid] = 'Question Note: '.ellipsisize($question['attributes']['name'], 30).' ('.$this->id.':q'.$shadowid.')';
1456  $keywords['question_label_'.$this->id.'_q'.$shadowid] = 'Question Label: '.ellipsisize($question['attributes']['name'], 30).' ('.$this->id.':q'.$shadowid.')';
1457  }
1458 
1459  foreach ($GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, SQ_LINK_TYPE_2, 'form_section', FALSE) as $link) {
1460  // get the section
1461  $sectionid = $link['minorid'];
1462  $s = $GLOBALS['SQ_SYSTEM']->am->getAsset($sectionid);
1463 
1464  $keywords += $s->getDisplayKeywords();
1465  $keywords['section_title_'.$s->id] = 'Nested Section Name: '.$s->name.' ('.$s->id.')';
1466  $keywords['section_contents_'.$s->id] = 'Nested Section Contents: '.$s->name.' ('.$s->id.')';
1467 
1468  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($s);
1469  unset($s);
1470  }
1471 
1472  if ($include_submit_keywords) {
1473  $keywords['submit_button'] = 'Submit Button';
1474  $keywords['reset_button'] = 'Reset Button';
1475  $keywords['form_errors'] = 'Form Errors';
1476  }
1477 
1478  return $keywords;
1479 
1480  }//end getDisplayKeywords()
1481 
1482 
1492  {
1493  $questions = $this->attr('questions');
1494  $keywords = Array();
1495 
1496  foreach ($questions as $shadowid => $question) {
1497  $keywords['response_'.$this->id.'_q'.$shadowid] = translate('cms_form_section_response', $this->name, ellipsisize($question['attributes']['name'], 30)).' ('.$this->id.':q'.$shadowid.')';
1498  $keywords['question_name_'.$this->id.'_q'.$shadowid] = translate('cms_form_section_q_name', $this->name, ellipsisize($question['attributes']['name'], 30)).' ('.$this->id.':q'.$shadowid.')';
1499  $keywords['question_note_'.$this->id.'_q'.$shadowid] = translate('cms_form_section_q_note', $this->name, ellipsisize($question['attributes']['name'], 30)).' ('.$this->id.':q'.$shadowid.')';
1500  }
1501 
1502  foreach ($GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, SQ_LINK_TYPE_2, 'form_section', FALSE) as $link) {
1503  // get the nested form
1504  $sectionid = $link['minorid'];
1505  $s = $GLOBALS['SQ_SYSTEM']->am->getAsset($sectionid);
1506 
1507  $keywords += $s->getResponseKeywords();
1508  $keywords['section_title_'.$s->id] = 'Nested Section Name: '.$s->name.' ('.$s->id.')';
1509 
1510  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($s);
1511  unset($s);
1512  }
1513 
1514  return $keywords;
1515 
1516  }//end getResponseKeywords()
1517 
1518 
1519 //-- KEYWORD REPLACEMENTS --//
1520 
1521 
1530  function getDisplayKeywordReplacements($generating=FALSE, $include_submit_keywords=TRUE, $form=NULL)
1531  {
1532  if (!is_null($form)) {
1533  assert_is_a($form, 'Form');
1534  }
1535 
1536  $questions = $this->attr('questions');
1537  $keywords = Array();
1538 
1539  foreach ($questions as $shadowid => $question) {
1540  // get the question
1541  $assetid = $this->id.':q'.$shadowid;
1542  $q = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
1543  if ($generating) {
1544  $keywords['question_field_'.$this->id.'_q'.$shadowid] = '<'.'?php echo $GLOBALS[\'SQ_SYSTEM\']->am->getAsset(\''.$assetid.'\')->getHtmlField(); ?'.'>';
1545  $keywords['question_note_'.$this->id.'_q'.$shadowid] = '<'.'?php echo $GLOBALS[\'SQ_SYSTEM\']->am->getAsset(\''.$assetid.'\')->attr(\'note\'); ?'.'>';
1546  $keywords['question_name_'.$this->id.'_q'.$shadowid] = '<'.'?php echo $GLOBALS[\'SQ_SYSTEM\']->am->getAsset(\''.$assetid.'\')->attr(\'name\'); ?'.'>';
1547  $keywords['question_id_'.$this->id.'_q'.$shadowid] = $assetid;
1548  $keywords['question_label_'.$this->id.'_q'.$shadowid] = '<'.'?php echo $GLOBALS[\'SQ_SYSTEM\']->am->getAsset(\''.$assetid.'\')->getHtmlLabel(); ?'.'>';
1549  } else {
1550  $keywords['question_field_'.$this->id.'_q'.$shadowid] = $q->getHtmlField();
1551  $keywords['question_note_'.$this->id.'_q'.$shadowid] = $q->attr('note');
1552  $keywords['question_name_'.$this->id.'_q'.$shadowid] = $q->attr('name');
1553  $keywords['question_id_'.$this->id.'_q'.$shadowid] = $assetid;
1554  $keywords['question_label_'.$this->id.'_q'.$shadowid] = $q->getHtmlLabel();
1555  }
1556  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($q);
1557  unset($q);
1558  }
1559 
1560  foreach ($GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, SQ_LINK_TYPE_2, 'form_section', FALSE) as $link) {
1561  // get the nested form section
1562  $sectionid = $link['minorid'];
1563  $s = $GLOBALS['SQ_SYSTEM']->am->getAsset($sectionid);
1564  $keywords += $s->getDisplayKeywordReplacements($generating, FALSE);
1565 
1566  if ($generating) {
1567  $keywords['section_title_'.$s->id] = '<'.'?php echo $GLOBALS[\'SQ_SYSTEM\']->am->getAsset(\''.$sectionid.'\')->name; ?'.'>';;
1568 
1569  $keywords['section_contents_'.$s->id] = '<'.'?php include_once $GLOBALS[\'SQ_SYSTEM\']->am->getAsset(\''.$sectionid.'\')->data_path.\'/content_file.php\'; ?'.'>';;
1570  } else {
1571  $keywords['section_title_'.$s->id] = $s->name;
1572  ob_start();
1573  $s->printBody($form);
1574  $keywords['section_contents_'.$s->id] = ob_get_clean();
1575  }
1576 
1577  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($s);
1578  unset($s);
1579  }
1580 
1581  if ($include_submit_keywords) {
1582  // Submit Button, Reset Button, Form Errors
1583  // TODO:
1584  ob_start();
1585  //submit_button('submit_button', );
1586  $submit_button = ob_get_contents();
1587  ob_end_clean();
1588 
1589  ob_start();
1590  //reset_button('reset_button', 'Reset');
1591  $reset_button = ob_get_contents();
1592  ob_end_clean();
1593 
1594  $keywords['submit_button'] = $submit_button;
1595  $keywords['reset_button'] = $reset_button;
1596 
1597  // TODO:
1598  $keywords['form_errors'] = implode('\n', $this->submission_errors);
1599  } else {
1600  // suppress them
1601  $keywords['submit_button'] = '';
1602  $keywords['reset_button'] = '';
1603  $keywords['form_errors'] = '';
1604  }
1605 
1606  return $keywords;
1607 
1608  }//end getDisplayKeywordReplacements()
1609 
1610 
1611 }//end class
1612 ?>