Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
form.inc
1 <?php
17 require_once SQ_INCLUDE_PATH.'/asset.inc';
18 require_once SQ_LIB_PATH.'/html_form/html_form.inc';
19 require_once SQ_FUDGE_PATH.'/general/datetime.inc';
20 require_once SQ_FUDGE_PATH.'/general/general.inc';
21 require_once SQ_FUDGE_PATH.'/general/www.inc';
22 require_once SQ_CORE_PACKAGE_PATH.'/interfaces/bridge/bridge.inc';
23 require_once SQ_FUDGE_PATH.'/mollom/mollom.inc';
24 
25 
37 class Form extends Asset implements Bridge
38 {
39 
40 
45  var $current_answers = Array();
46 
47 
56  var $extra_data = Array();
57 
58 
63  var $active_section = NULL;
64 
65 
70  var $submission_errors = NULL;
71 
72 
77  var $submission_asset = NULL;
78 
79 
80 
89  function Form($assetid=0)
90  {
91  $this->_ser_attrs = TRUE;
92  $this->Asset($assetid);
93 
94  }//end constructor
95 
96 
97 
98 //-- GENERIC ASSET FUNCTIONS --//
99 
100 
109  function create(&$link)
110  {
111  $this->setAttrValue('submission_errors_msg', translate('cms_form_error_msg_default'));
112 
113  return parent::create($link);
114 
115  }//end create()
116 
117 
138  function _createAdditional(&$link)
139  {
140  if (!$this->createSubmissionsFolder(translate('submissions'), 'submissions_folder')) {
141  return FALSE;
142  }
143 
144  return parent::_createAdditional($link);
145 
146  }//end _createAdditional()
147 
148 
159  protected function _getName($short_name=FALSE, $contextid=NULL)
160  {
161  // No context specified, using the current context
162  if ($contextid === NULL) {
163  $contextid = $GLOBALS['SQ_SYSTEM']->getContextId();
164  }//end if
165 
166  // Obtain the attribute value for Name from the specified Context
167  $values = $GLOBALS['SQ_SYSTEM']->am->getAttributeValuesByName('name', $this->type(), Array($this->id), $contextid);
168  if (empty($values) === TRUE) {
169  return parent::_getName($short_name, $contextid);
170  } else {
171  return $values[$this->id];
172  }
173 
174  }//end _getName()
175 
176 
184  function _getAllowedLinks()
185  {
186  $page_links = Array(
187  SQ_LINK_TYPE_2 => Array(
188  'form_section' => Array(
189  'card' => 'M',
190  'exclusive' => FALSE,
191  ),
192  'folder' => Array(
193  'card' => '3',
194  'exclusive' => TRUE,
195  ),
196  ),
197  );
198  return $page_links;
199 
200  }//end _getAllowedLinks()
201 
202 
212  function linksUpdated()
213  {
214  $this->fileRegeneration();
215 
216  }//end linksUpdated()
217 
218 
219 
245  function cloneComponents(&$clone, $components, $override=FALSE)
246  {
247  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
248  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
249 
250  if (!parent::cloneComponents($clone, $components, $override)) {
251  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
252  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
253  return FALSE;
254  }
255 
256  if (in_array('attributes', $components) || in_array('all', $components)) {
257  $sort_order = $this->attr('sort_order');
258  $sort_order = str_replace($this->id.':q', $clone->id.':q', $sort_order);
259 
260  // save the information
261  $clone->setAttrValue('sort_order', $sort_order);
262  $clone->saveAttributes();
263 
264  // Change the content of the content type to replace question id.
265 
266  }
267 
268  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
269  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
270  return TRUE;
271 
272  }//end cloneComponents()
273 
274 
298  function cloneComponentsAdditional(&$clone, $components)
299  {
300 
301  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
302  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
303 
304  if (in_array('attributes', $components) || in_array('all', $components)) {
305  $GLOBALS['SQ_SYSTEM']->setRunLevel($GLOBALS['SQ_SYSTEM']->getRunLevel() & SQ_RUN_LEVEL_FORCED);
306 
307 
308  $section_links = $this->getSectionLinks();
309  $clone_section_links = $clone->getSectionLinks();
310 
311  $originals_clones = Array();
312  foreach ($section_links as $link) {
313  foreach ($clone_section_links as $cloned_link) {
314  if ($cloned_link['sort_order'] == $link['sort_order']) {
315  // Create a new array mapping original id and cloned id of sections.
316  $originals_clones[] = Array (
317  'original' => $link['minorid'],
318  'clone' => $cloned_link['minorid'],
319  );
320  }
321  }
322  }
323 
324  // Might as well add this form and its clone to the list to do.
325  $originals_clones[] = Array (
326  'original' => $this->id,
327  'clone' => $clone->id,
328  );
329 
330  // Now get all the bodycopies, and get their content types
331  $bodycopies = Array (
332  'use_bodycopy_main' => 'Page Contents',
333  'use_bodycopy_thank_you' => 'Thank You',
334  );
335  if ((isset($clone->_bodycopies) && !empty($clone->_bodycopies)) ) {
336  $bodycopies = array_merge($bodycopies, Array ('use_bodycopy_receipt' => 'Receipt'));
337  }
338 
339  $replace_keywords = Array ( 'question_name', 'response', 'question_field', 'question_id', 'question_note', 'question_label', 'section_title');
340  foreach ($bodycopies as $attribute => $bodycopy_name) {
341  $bodycopy = NULL;
342  if ((isset($clone->_bodycopies) && !empty($clone->_bodycopies)) ) {
343  $bodycopy = $clone->getBodycopy($bodycopy_name);
344  } else {
345  $attr_value = $clone->attr($attribute);
346  if (!empty($attr_value)) $bodycopy = $clone->getBodycopy($bodycopy_name);
347  }
348 
349  if (!empty($bodycopy)) {
350  $containers = $bodycopy->getContainers();
351  foreach ($containers as $container) {
352  $content_type = $container->getContentType();
353  if ($container->type() == 'bodycopy_table') {
354  if (is_array($content_type)) {
355  foreach ($content_type as $cell_type) {
356  if (!$this->_cloneDIVContents($cell_type, $originals_clones, $replace_keywords)) {
357  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
358  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
359  return FALSE;
360  }//end if
361  }//end foreach
362  } else {
363  if (!$this->_cloneDIVContents($content_type, $originals_clones, $replace_keywords)) {
364  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
365  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
366  return FALSE;
367  }//end if
368  }//end if
369  } else {
370  if (!$this->_cloneDIVContents($content_type, $originals_clones, $replace_keywords)) {
371  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
372  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
373  return FALSE;
374  }//end if
375  }//end if
376  }//end foreach
377  }//end if
378  }//end foreach
379 
380  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
381 
382  }//end if components
383 
384  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
385  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
386  return TRUE;
387 
388  }//end cloneComponentsAdditional()
389 
390 
401  function _cloneDIVContents($content_type, $original_clones=Array(), $replace_keywords=Array())
402  {
403  $old_html = $content_type->attr('html');
404  if (!empty($old_html) && !empty($original_clones)) {
405  $new_html = $content_type->attr('html');
406  foreach ($original_clones as $original_clone) {
407  // Replace the old question id with the new question id
408  foreach ($replace_keywords as $keyword) {
409  if ($keyword == 'section_title') {
410  $new_html = str_replace($keyword.'_'.$original_clone['original'], $keyword.'_'.$original_clone['clone'], $new_html);
411  } else {
412  $new_html = str_replace($keyword.'_'.$original_clone['original'].'_q', $keyword.'_'.$original_clone['clone'].'_q', $new_html);
413  }
414  }//end foreach
415  }//end foreach
416 
417  // Save and check for errors, the rollback occurs above
418  if (!$content_type->setAttrValue('html', $new_html)) {
419  return FALSE;
420  }//end if
421 
422  if (!$content_type->saveAttributes()) return FALSE;
423 
424  }//end if
425 
426  return TRUE;
427 
428  }//end _cloneDIVContents()
429 
430 
431 //-- BRIDGE FUNCTIONS --//
432 
433 
448  public function getAsset($assetid, $type_code='', $mute_errors=FALSE)
449  {
450  $asset = NULL;
451  $id_parts = explode(':', $assetid);
452 
453  if (isset($id_parts[1])) {
454  $shadowid = $id_parts[1];
455  } else {
456  return $asset;
457  }
458 
459  // Questions follow the format '<assetid>:q<shadowid>'
460  if ($shadowid{0} == 'q') {
461 
462  // pick up the question in question (!)
463  $questions = $this->attr('questions');
464  $questionid = substr($shadowid, 1, strlen($shadowid));
465  if (empty($questions[$questionid])) return $asset;
466 
467  $q_type_code = $questions[$questionid]['question_type_code'];
468 
469  // not fussed about type code?
470  if (empty($type_code)) $type_code = $q_type_code;
471 
472  // only give the asset back if of the right type code
473  if (in_array($q_type_code, $GLOBALS['SQ_SYSTEM']->am->getTypeDescendants($type_code, TRUE))) {
474  $questions[$questionid]['questionid'] = $questionid;
475  $GLOBALS['SQ_SYSTEM']->am->includeAsset($q_type_code);
476 
477  // this eval returns the necessary object for the question type
478  $asset = new $q_type_code($this->id, $questions[$questionid]);
479  }
480  }
481 
482  return $asset;
483 
484  }//end getAsset()
485 
486 
507  function getLinks($assetid, $link_types, $type_code='', $strict_type_code=TRUE, $side_of_link='major', $sort_by=NULL)
508  {
509  $id_parts = explode(':', $assetid);
510  if (isset($id_parts[1])) $assetid = $id_parts[1];
511 
512  $links = Array();
513  $new_sort_order = 0;
514 
515  // shadow assets have no children, but they have one minor link the other way
516  if (!is_numeric($assetid)) {
517  if ($side_of_link == 'minor') {
518  $return_link = FALSE;
519 
520  if ($type_code == '') {
521  // not fussed what we're getting, so return me
522  $return_link = TRUE;
523  } else if ($strict_type_code) {
524  // strict type code check
525  $return_link = ($type_code == $this->type());
526  } else {
527  $return_link = (in_array($this->type(), $GLOBALS['SQ_SYSTEM']->am->getTypeDescendants($type_code, TRUE)));
528  }
529 
530  if ($return_link) {
531  $links[] = Array(
532  'linkid' => 0,
533  'majorid' => $this->id,
534  'minorid' => $this->id.':'.$assetid,
535  'major_type_code' => $this->type(),
536  'value' => '',
537  'link_type' => SQ_LINK_TYPE_2,
538  'is_dependant' => TRUE,
539  'is_exclusive' => FALSE,
540  'sort_order' => 0,
541  'locked' => 0,
542  );
543  }
544  }
545 
546  return $links;
547  }
548 
549  // are we getting our bodycopy folder and/or sections (if they all exist)?
550  $real_type_codes = Array(
551  'folder' => SQ_LINK_TYPE_2 | SQ_LINK_TYPE_3,
552  'form_section' => SQ_LINK_TYPE_2,
553  );
554 
555  // asset_manager will handle all the real links for us, but we need to determine sort order;
556  // get all the links, but don't add them to the links array
557  foreach ($real_type_codes as $real_type_code => $allowed_link_types) {
558  if ($type_code == '') {
559  // not fussed what we're getting, so return me
560  $get_type = TRUE;
561  } else if ($strict_type_code) {
562  $get_type = ($type_code == $real_type_code);
563  } else {
564  // specific type code set, non-strict check
565  $get_type = (in_array($real_type_code, $GLOBALS['SQ_SYSTEM']->am->getTypeDescendants($type_code, TRUE)));
566  }
567 
568  // we are getting this type?
569  if ($get_type) {
570  $query = $GLOBALS['SQ_SYSTEM']->am->generateGetLinksQuery($assetid, $link_types & $allowed_link_types, $real_type_code, FALSE);
571  if (empty($query)) return Array();
572 
573  $db = MatrixDAL::getDb();
574  try {
575  $stmt = MatrixDAL::preparePdoQuery(implode(' ', $query['sql_array']));
576  foreach ($query['bind_vars'] as $bind_var => $bind_value) {
577  MatrixDAL::bindValueToPdo($stmt, $bind_var, $bind_value);
578  }
579  $type_links = MatrixDAL::executePdoGroupedAssoc($stmt);
580  } catch (Exception $e) {
581  throw new Exception('Unable to get links for asset: '.$assetid.' due to database error: '.$e->getMessage());
582  }
583 
584  foreach (array_keys($type_links) as $key) {
585  $new_sort_order++;
586  }
587  }
588  }
589 
590  // but we also need question shadow asset links
591  if ($link_types & SQ_LINK_TYPE_2) {
592  $questions = $this->attr('questions');
593 
594  // keep a cache of asset type codes if we are checking on a non-strict
595  // type code, to stop us calling getTypeDescendants() all the time
596  // (not needed if strict type check or if not fussed)
597  $get_question_types = Array();
598 
599  foreach ($questions as $questionid => $data) {
600  if ($type_code == '') {
601  // not fussed what we're getting, so return me
602  $get_question = TRUE;
603  } else if ($strict_type_code) {
604  // strict type code check
605  $get_question = ($type_code == $data['question_type_code']);
606  } else {
607  // if we've already cached whether this question type is to be
608  // returned, then we don't need to look it up again
609  if (isset($get_question_types[$data['question_type_code']])) {
610  $get_question = $get_question_types[$data['question_type_code']];
611  $get_question_types[$data['question_type_code']] = $get_question;
612  } else {
613  $get_question = (in_array($data['question_type_code'], $GLOBALS['SQ_SYSTEM']->am->getTypeDescendants($type_code, TRUE)));
614  }
615  }
616 
617  if ($get_question) {
618  $links[] = Array(
619  'linkid' => 0,
620  'majorid' => $this->id,
621  'minorid' => $this->id.':q'.$questionid,
622  'minor_type_code' => $data['question_type_code'],
623  'value' => '',
624  'link_type' => SQ_LINK_TYPE_2,
625  'is_dependant' => TRUE,
626  'is_exclusive' => FALSE,
627  'sort_order' => $new_sort_order,
628  'locked' => 0,
629  );
630  }
631  $new_sort_order++;
632  }//end foreach
633  }//end if
634 
635  if (is_null($sort_by)) $sort_by = 'sort_order';
636 
637  uasort($links, create_function('$a,$b','return $a["'.$sort_by.'"] > $b["'.$sort_by.'"];'));
638 
639  return $links;
640 
641  }//end getLinks()
642 
643 
650  function getAssetMapLinks()
651  {
652  $new_sort_order = 0;
653  $old_links = $this->getLinks($this->id, SQ_SC_LINK_BACKEND_NAV);
654  $links = Array();
655  while (!empty($old_links)) {
656  array_push($links, array_shift($old_links));
657  }
658 
659  $questions = $this->attr('questions');
660 
661  foreach (array_keys($links) as $i) {
662 
663  $link =& $links[$i];
664 
665  // remove real links
666  if ($link['minor_type_code'] == 'folder') {
667  unset($links[$i]);
668  continue;
669  }
670 
671  if ($link['minor_type_code'] == 'form_section') {
672  unset($links[$i]);
673  continue;
674  }
675 
676  // mould it all to the asset map's liking
677  $link['url'] = '';
678  $link['path'] = '';
679  $link['num_kids'] = 0;
680  $link['accessible'] = 1;
681 
682  $link['assetid'] = $link['minorid'];
683  $link['type_code'] = $link['minor_type_code'];
684  $link['linkid'] = $link['majorid'].':'.$link['minorid'];
685 
686  $questionid = str_replace($this->id.':q', '', $link['assetid']);
687 
688  // make name and short name the same
689  $link['name'] = $questions[$questionid]['attributes']['name'];
690  $link['short_name'] = $link['name'];
691 
692  // make the status the same as the form's one
693  $link['status'] = $this->status;
694 
695  unset($link['minor_type_code']);
696  unset($link['majorid']);
697  unset($link['minorid']);
698  unset($link['value']);
699  unset($link['is_exclusive']);
700 
701  }//end foreach
702 
703  return $links;
704 
705  }//end getAssetMapLinks()
706 
707 
722  function getParents($shadowid, $type_code='', $strict_type_code=TRUE)
723  {
724  $id_parts = explode(':', $shadowid);
725  if (isset($id_parts[1])) $shadowid = $id_parts[1];
726 
727  // basically get the parents of the section, and add itself in the
728  $ret_val = $GLOBALS['SQ_SYSTEM']->am->generateGetParentsQuery($this->id, $type_code, $strict_type_code);
729 
730  if (empty($ret_val)) return Array();
731 
732  try {
733  $query = MatrixDAL::preparePdoQuery(implode(' ', $ret_val['sql_array']));
734  foreach ($ret_val['bind_vars'] as $bind_var => $bind_value) {
735  MatrixDAL::bindValueToPdo($query, $bind_var, $bind_value);
736  }
737 
738  $queried_parents = MatrixDAL::executePdoAssoc($query);
739  } catch (Exception $e) {
740  throw new Exception('Unable to get the parents of shadow id: '.$shadowid.' due to database error: '.$e->getMessage());
741  }
742  $parents = Array();
743 
744  foreach ($queried_parents as $queried_parent) {
745  $parents[$queried_parent['majorid']] = $queried_parent['type_code'];
746  }
747 
748  $parents[$this->id] = $this->type();
749 
750  return $parents;
751 
752  }//end getParents()
753 
754 
777  function getChildren($assetid, $type_code='', $strict_type_code=TRUE, $dependant=NULL, $sort_by=NULL)
778  {
779  // no shadow assets have children in this asset
780  if (!is_numeric($assetid)) return Array();
781 
782  if (!is_array($type_code)) {
783  if (empty($type_code)) {
784  $type_code = Array();
785  } else {
786  $type_code = Array($type_code);
787  }
788  }
789 
790  $children = Array();
791 
792  // this is so we get the sections as we go
793  // have to do this all in here because calling asset manager will bring on a vicious loop
794  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
795  if (($asset instanceof Bridge) && ($assetid != $this->id)) {
796  $children = $asset->getChildren($assetid, $type_code, $dependant, $strict_type_code, $sort_by);
797  $this->forgetAsset($asset);
798  } else {
799  $ret_val = $GLOBALS['SQ_SYSTEM']->am->generateGetChildrenQuery($asset, $type_code, $strict_type_code, $dependant, $sort_by);
800  unset($asset);
801 
802  if (!empty($ret_val)) {
803  try {
804  $sql_array = $ret_val['sql_array'];
805  $bind_vars = $ret_val['bind_vars'];
806 
807  $query = MatrixDAL::preparePdoQuery(implode(' ', $sql_array));
808  foreach ($bind_vars as $bind_var => $bind_value) {
809  MatrixDAL::bindValueToPdo($query, $bind_var, $bind_value);
810  }
811  $result = MatrixDAL::executePdoGroupedAssoc($query);
812  } catch (Exception $e) {
813  throw new Exception('Unable to get children for asset: '.$assetid.' due to database error: '.$e->getMessage());
814  }
815 
816  $children =& $result;
817  }
818  }
819 
820  // now questions
821  $entries = $this->attr('questions');
822  if (empty($type_code)) {
823  foreach ($entries as $questionid => $data) {
824  $children[$this->id.':q'.$questionid] = Array(Array('type_code' => $data['question_type_code']));
825  }
826  } else {
827  foreach ($type_code as $this_type_code) {
828  foreach ($entries as $questionid => $data) {
829  if ($strict_type_code) {
830  if ($data['question_type_code'] == $this_type_code) {
831  $children[$this->id.':q'.$questionid] = Array(Array('type_code' => $data['question_type_code']));
832  }
833  } else {
834  $type_desc = $GLOBALS['SQ_SYSTEM']->am->getTypeDescendants($this_type_code) + Array($this_type_code);
835  if (in_array($data['question_type_code'], $type_desc)) {
836  $children[$this->id.':q'.$questionid] = Array(Array('type_code' => $data['question_type_code']));
837  }
838  }
839  }
840  }
841  }
842 
843  foreach ($children as $child_id => $details) {
844  foreach ($type_code as $index => $code) {
845  if ($details[0]['type_code'] != $code) {
846  unset($children[$child_id]);
847  }
848  }
849  }
850 
851  if (!is_null($sort_by)) {
852  uasort($children, create_function('$a,$b','return $a["'.$sort_by.'"] > $b["'.$sort_by.'"]'));
853  }
854 
855  return $children;
856 
857  }//end getChildren()
858 
859 
874  function _updated($update_parents=TRUE)
875  {
876  if (!parent::_updated($update_parents)) return FALSE;
877  if (SQ_IN_BACKEND) return $this->fileRegeneration();
878  return TRUE;
879 
880  }//end _updated()
881 
882 
893  function canCreateLink(&$minor, $link_type, $exclusive)
894  {
895  if (($minor instanceof Form_Submission) || ($minor instanceof Form_Section)) {
896  return TRUE;
897  }
898 
899  return parent::canCreateLink($minor, $link_type, $exclusive);
900 
901  }//end canCreateLink()
902 
903 
924  function prepareLink(&$asset, $side_of_link, &$link_type, &$value, &$sort_order, &$dependant, &$exclusive)
925  {
926  if ($side_of_link == 'major' && $asset->type() == 'form_section') {
927  $link_type = SQ_LINK_TYPE_2;
928  $dependant = TRUE;
929  }
930 
931  }//end prepareLink()
932 
933 
955  function countLinks($assetid, $side_of_link='major', $link_types=0, $type_code='', $strict_type_code=TRUE, $ignore_linkid=0)
956  {
957  return 0;
958 
959  }//end countLinks()
960 
961 
962 
973  function getLineageFromURL($assetid, $protocol, $url)
974  {
975  return Array();
976 
977  }//end getLineageFromURL()
978 
979 
1000  function getPermission($assetid, $permission, $granted=NULL, $and_greater=TRUE, $expand_groups=FALSE, $all_info=FALSE)
1001  {
1002  return Array();
1003 
1004  }//end getPermission()
1005 
1006 
1018  function setPermission($assetid, $userid, $permission, $granted)
1019  {
1020  return FALSE;
1021 
1022  }//end setPermission()
1023 
1024 
1035  function deletePermission($assetid, $userid, $permission)
1036  {
1037  return FALSE;
1038 
1039  }//end deletePermission()
1040 
1041 
1056  function getAssetInfo($assetids, $type_code=Array(), $strict_type_code=TRUE, $field='')
1057  {
1058  return Array();
1059 
1060  }//end getAssetInfo()
1061 
1062 
1074  function assetExists($assetids)
1075  {
1076  return FALSE;
1077 
1078  }//end assetExists()
1079 
1080 
1102  function getLink($assetid, $link_type=NULL, $type_code='', $strict_type_code=TRUE, $value=NULL, $side_of_link='major', $exclusive=NULL)
1103  {
1104  return Array();
1105 
1106  }//end getLink()
1107 
1108 
1120  function getLinkById($linkid, $assetid=0, $side_of_link='major')
1121  {
1122  return Array();
1123 
1124  }//end getLinkById()
1125 
1126 
1147  function getLinkByAsset($assetid, $other_assetid, $link_types=NULL, $value=NULL, $side_of_link='major', $force_array=FALSE, $dependant=NULL, $exclusive=NULL)
1148  {
1149  return Array();
1150 
1151  }//end getLinkByAsset()
1152 
1153 
1163  function getAllChildLinks($assetid, $link_type=0)
1164  {
1165  return Array();
1166 
1167  }//end getAllChildLinks()
1168 
1169 
1184  function updateLink($linkid, $link_type=NULL, $value=NULL, $sort_order=NULL)
1185  {
1186  return FALSE;
1187 
1188  }//end updateLink()
1189 
1190 
1200  function deleteAssetLink($linkid, $moving=FALSE)
1201  {
1202  return FALSE;
1203 
1204  }//end deleteAssetLink()
1205 
1206 
1215  function getAssetMapAssetInfo($assetid)
1216  {
1217  return Array();
1218 
1219  }//end getAssetMapAssetInfo()
1220 
1221 
1238  function createAssetLink(&$major, &$minor, $link_type, $value='', $sort_order=NULL, $dependant='0', $exclusive='0', $moving=FALSE)
1239  {
1240  return 0;
1241 
1242  }//end createAssetLink()
1243 
1244 
1245 //-- QUESTION MANAGEMENT --//
1246 
1247 
1256  function &getQuestions()
1257  {
1258  $questions = $this->attr('questions');
1259  return $questions;
1260 
1261  }//end getQuestions()
1262 
1263 
1273  function &getAllQuestions()
1274  {
1275  // getQuestions() returns an array of (q_id => detail) - this is
1276  // ambiguous to return if sections are present, so we need to convert
1277  // to an asset ID
1278 
1279  $all_questions = Array();
1280  $questions = $this->getQuestions();
1281  foreach ($questions as $q_id => $question_detail) {
1282  $all_questions[$this->id.':q'.$q_id] = $question_detail;
1283  }
1284 
1285  $sections = $this->getAllSections();
1286  foreach ($sections as $section) {
1287  $questions = $section->getQuestions();
1288  foreach ($questions as $q_id => $question_detail) {
1289  $all_questions[$section->id.':q'.$q_id] = $question_detail;
1290  }
1291  }
1292 
1293  return $all_questions;
1294 
1295  }//end getAllQuestions()
1296 
1297 
1306  function &getQuestionAssets($type_code=NULL)
1307  {
1308  $am = $GLOBALS['SQ_SYSTEM']->am;
1309  $result = Array();
1310 
1311  $questions = $this->attr('questions');
1312  if (!empty($questions)) {
1313  foreach ($questions as $q_id => $question) {
1314  if (empty($type_code) || ($question['question_type_code'] === $type_code)) {
1315  $id = $this->id.':q'.$q_id;
1316  $result[$id] = $am->getAsset($id);
1317  }
1318  }
1319  }
1320 
1321  return $result;
1322 
1323  }//end getQuestionAssets()
1324 
1325 
1336  function &getAllQuestionAssets($type_code=NULL)
1337  {
1338  $all_questions = $this->getQuestionAssets($type_code);
1339 
1340  $sections = $this->getAllSections();
1341  foreach ($sections as $section) {
1342  $questions = $section->getQuestionAssets($type_code);
1343  $all_questions = array_merge($all_questions, $questions);
1344  }
1345 
1346  return $all_questions;
1347 
1348  }//end getAllQuestionAssets()
1349 
1350 
1358  public function getQuestionAssetsOnPage($page_number)
1359  {
1360  // If we are on the last page of a form with a confirmation page, then
1361  // we must be on the confirmation page, which by definition has no
1362  // answerable questions (possibly a CAPTCHA but this doesn't count).
1363  if (($page_number == $this->getTotalPages()) && $this->attr('use_confirmation_page')) {
1364  return Array();
1365  }
1366 
1367  if ($this->attr('multi_page')) {
1368  // do we have any unattached questions?
1369  $unattached_questions = $this->getQuestions();
1370 
1371  // Get top-level sections
1372  $section_links = $this->getSectionLinks();
1373 
1374  // If we have unattached questions, then reduce the page number
1375  // internally - so that section 1 will be on page 2
1376  if (!empty($unattached_questions)) {
1377  $page_number--;
1378  }
1379 
1380  if ($page_number == 0) {
1381  // Printing unattached questions (if any)
1382  // Grab the default "unattached questions" content file (at the moment)
1383  $questions = $this->getQuestionAssets();
1384  } else {
1385  // Printing a section - best way to do this is to slice it off
1386  $section_link = reset(array_slice($section_links, $page_number - 1, 1));
1387  $section = $GLOBALS['SQ_SYSTEM']->am->getAsset($section_link['minorid']);
1388  $questions = $section->getAllQuestionAssets();
1389  }
1390  } else {
1391  // Single page form - get everything
1392  $questions = $this->getAllQuestionAssets();
1393  }
1394 
1395  return $questions;
1396 
1397  }//end getAllQuestionAssets()
1398 
1399 
1400 //-- ADDING AND DELETING QUESTIONS --//
1401 
1402 
1414  function attachQuestion($type_code)
1415  {
1416  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
1417  $db = MatrixDAL::getDb();
1418 
1419  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
1420 
1421  $questions = $this->getQuestions();
1422  if ($this->attr('next_questionid') > 0) {
1423  $new_key = $this->attr('next_questionid');
1424  } else if (empty($questions)) {
1425  $new_key = 1;
1426  } else {
1427  $new_key = max(array_keys($questions)) + 1;
1428  }
1429 
1430  $questions[$new_key] = Array(
1431  'question_type_code' => $type_code,
1432  'attributes' => Array('name' => 'Question '.(count($questions)+1)),
1433  );
1434 
1435  $this->setAttrValue('questions', $questions);
1436  $this->setAttrValue('next_questionid', $new_key + 1);
1437 
1438  $sort_order = $this->attr('sort_order');
1439 
1440  if (empty($sort_order)) {
1441  $new_index = 0;
1442  } else {
1443  $new_index = max(array_keys($sort_order))+1;
1444  }
1445  $sort_order[$new_index] = $this->id.':q'.$new_key;
1446  $this->setAttrValue('sort_order', $sort_order);
1447 
1448  if (!$this->saveAttributes()) {
1449  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
1450  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1451  return FALSE;
1452  }
1453 
1454  // acquire the lock on the new question
1455  $GLOBALS['SQ_SYSTEM']->am->acquireLock($this->id.':q'.$new_key, 'all', $this->id, TRUE);
1456  $GLOBALS['SQ_SYSTEM']->am->updateLock($this->id, 'all');
1457 
1458  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
1459  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1460 
1461  return TRUE;
1462 
1463  }//end attachQuestion()
1464 
1465 
1477  function deleteQuestion(&$question)
1478  {
1479  if ($question->_questionid == 0) return FALSE;
1480 
1481  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
1482  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
1483  $questions = $this->getQuestions();
1484 
1485  unset($questions[$question->_questionid]);
1486 
1487  $this->setAttrValue('questions', $questions);
1488 
1489  $sort_order = $this->attr('sort_order');
1490 
1491  // remove it from the sort order - it does not matter that there are gaps
1492  $old_index = array_search($question->id, $sort_order);
1493  unset($sort_order[$old_index]);
1494 
1495  // unset the question
1496  unset($question);
1497 
1498  $this->setAttrValue('sort_order', $sort_order);
1499 
1500  if (!$this->saveAttributes()) {
1501  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
1502  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1503  return FALSE;
1504  }
1505  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
1506  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1507 
1508  return TRUE;
1509 
1510  }//end deleteQuestion()
1511 
1512 
1519  function getQuestionCount()
1520  {
1521  $questions = $this->getQuestions();
1522  return count($questions);
1523 
1524  }//end getQuestionCount()
1525 
1526 
1535  function getQuestionByID($questionid)
1536  {
1537  $questions = $this->getQuestions();
1538  return (isset($questions[$questionid])) ? $questions[$questionid] : Array();
1539 
1540  }//end getQuestionByID()
1541 
1542 
1551  function &getQuestionByOrder($orderid)
1552  {
1553  $sort_order = $this->attr('sort_order');
1554  $question = $GLOBALS['SQ_SYSTEM']->am->getAsset($sort_order[$orderid]);
1555  return $question;
1556 
1557  }//end getQuestionByOrder()
1558 
1559 
1560 //-- SECTION MANAGEMENT --//
1561 
1562 
1571  function &getSections()
1572  {
1573  $am = $GLOBALS['SQ_SYSTEM']->am;
1574  $section_links = $this->getSectionLinks();
1575  $sections = Array();
1576 
1577  foreach ($section_links as $link) {
1578  $sections[$link['sort_order']] = $am->getAsset($link['minorid'], $link['minor_type_code']);
1579  }
1580  return $sections;
1581 
1582  }//end getSections()
1583 
1584 
1593  function &getAllSections()
1594  {
1595  $am = $GLOBALS['SQ_SYSTEM']->am;
1596  $section_links = $this->getSectionLinks();
1597  $sections = Array();
1598 
1599  while (!empty($section_links)) {
1600  $link = array_shift($section_links);
1601  $sections[] = $am->getAsset($link['minorid'], $link['minor_type_code']);
1602 
1603  // get subsection links and add them onto the front
1604  $sub_section_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($link['minorid'], SQ_SC_LINK_ALL, 'form_section', FALSE);
1605  $section_links = array_merge($sub_section_links, $section_links);
1606  }
1607  return $sections;
1608 
1609  }//end getSections()
1610 
1611 
1618  function getSectionCount()
1619  {
1620  return $GLOBALS['SQ_SYSTEM']->am->countLinks($this->id, 'major', SQ_LINK_TYPE_2, 'form_section', TRUE);
1621 
1622  }//end getSectionCount()
1623 
1624 
1633  function &getSectionLinks()
1634  {
1635  // This is what one would find in Asset_Manager::&getAsset() except we can't call that,
1636  // lest we spin off into an infinite loop involving OUR &getAsset().
1637  $value = Array(
1638  'link_value' => Array(''),
1639  'equal' => TRUE,
1640  );
1641  $links_query = $GLOBALS['SQ_SYSTEM']->am->generateGetLinksQuery($this->id, SQ_LINK_TYPE_2, 'form_section', TRUE, 'major', $value, '1', '0');
1642  if (empty($links_query)) return Array();
1643  $links_query['sql_array']['select'] .= ', l.majorid'; // we need the major id too
1644 
1645  try {
1646  $sql = implode(' ', $links_query['sql_array']);
1647  $query = MatrixDAL::preparePdoQuery($sql);
1648 
1649  foreach ($links_query['bind_vars'] as $bind_var => $bind_value) {
1650  MatrixDAL::bindValueToPdo($query, $bind_var, $bind_value);
1651  }
1652 
1653  $result = MatrixDAL::executePdoAssoc($query);
1654  } catch (Exception $e) {
1655  throw new Exception('Unable to get section links attached to form: '.$this->id.' due to database error: '.$e->getMessage());
1656  }
1657  return $result;
1658 
1659  }//end getSectionLinks()
1660 
1661 
1662 //-- BODYCOPIES --//
1663 
1664 
1671  function printBody()
1672  {
1673  // Pick up the current submission, if any.
1674  if ($GLOBALS['SQ_SYSTEM']->userPublic()) {
1675  $this->restoreFromSubmissionCookie();
1676  $submission_var =& $_SESSION['SQ_FORM_'.$this->id.'_SUBMISSION'];
1677  } else {
1678  $submission = $this->getSubmissionAsset();
1679  }
1680 
1681  if (isset($submission)) {
1682  $current_page = array_get_index($_POST, 'SQ_FORM_'.$this->id.'_PAGE', $submission->attr('current_page'));
1683  $latest_page = $submission->attr('latest_page');
1684  } else if (isset($submission_var)) {
1685  $current_page = array_get_index($_POST, 'SQ_FORM_'.$this->id.'_PAGE', array_get_index($submission_var, 'current_page', NULL));
1686  $latest_page = array_get_index($submission_var, 'latest_page', NULL);
1687  } else {
1688  $current_page = $latest_page = NULL;
1689  }
1690 
1691  $prefix = $this->getPrefix();
1692  $total_pages = $this->getTotalPages();
1693 
1694  $action = NULL;
1695  $possible_actions = Array(
1696  'submit',
1697  'previous_page',
1698  'reset',
1699  'save',
1700  'save_and_exit',
1701  );
1702 
1703  foreach ($possible_actions as $possible_action) {
1704  if (isset($_POST[$prefix.'_'.$possible_action])) {
1705  $action = $possible_action;
1706  break;
1707  }
1708  }
1709 
1710  $submission_limit = $this->attr('submission_limit');
1711  // see if the submission limit is set and has reached
1712  // if so display the appropiate bodycopy
1713  if (is_null($action) && $submission_limit != 0 && $submission_limit <= $this->getSubmissionCount()) {
1715  return;
1716  }
1717 
1718  $jump_to_page = array_get_index($_GET, 'SQ_FORM_'.$this->id.'_PAGE', NULL);
1719  $finished = FALSE;
1720 
1721  // Process the current page, if any - process if:
1722  // - we are moving to the next or previous page (save submission), OR
1723  // - we are jumping to an arbitrary page (do NOT save submission).
1724  if (!is_null($current_page) || !is_null($jump_to_page) || (!is_null($action) && ($action !== 'reset'))) {
1725  // Are we jumping to another page? If not, then we do NOT save the
1726  // submission at this point.
1727  if (!is_null($jump_to_page)) {
1728  $current_page = $jump_to_page;
1729  } else {
1730  // Only validate the page responses if the "Next/Submit" button is pressed.
1731  // In other words, allow gunky data to be stored because we
1732  // aren't actually progressing in the form; we will get sto it
1733  // when we go forward from this page (whenever that happens).
1734  $validate_page = ($action === 'submit') ? TRUE : FALSE;
1735  // Mute validation errors if there is no action, such as a public user refreshs file upload question form
1736  $mute_errors = (empty($action)) ? TRUE : FALSE;
1737 
1738  if (is_null($current_page)) $current_page = 1;
1739  $success = $this->processPage($current_page, $validate_page, $mute_errors);
1740 
1741  // Run the step submission actions for current page, if any
1742  $success = $success && (!$validate_page || $this->performFormSectionActions($current_page)) && empty($this->submission_errors);
1743 
1744  if ($success) {
1745 
1746  // If the submission has not yet been created, create the submission
1747  // in the "Incomplete Submissions" folder - but if there was only
1748  // one page, we can fudge it
1749 
1750  if (!isset($submission_var) && !$submission) {
1751  $create_in_completed = ($total_pages == 1) ? TRUE : FALSE;
1752 
1753  // Get an incomplete submissions, and now we know
1754  // this is page one
1755  //if (!$create_in_completed) {
1756  $submission = $this->createSubmission($create_in_completed);
1757  $this->submission_asset = $submission;
1758  //}
1759  }
1760 
1761  // If we have a submission, then, update it
1762  if (isset($submission)) {
1763  $this->updateSubmission($submission);
1764  } else if (isset($submission_var)) {
1765  $this->updateSubmissionCookie();
1766  }
1767 
1768  // Are we there yet? Assuming that we are moving forward
1769  // on this page, and not backwards or same page...
1770  if (($current_page == $total_pages) && ($action === 'submit')) {
1771  $finished = TRUE;
1772  } else {
1773  if ($action === 'previous_page') {
1774  $current_page--;
1775  } else if ($action === 'submit') {
1776  $current_page++;
1777  }
1778 
1779  $current_page = max($current_page, 1);
1780  }
1781  }
1782  }
1783  } else {
1784  // just change it to first page as we haven't been to any page yet
1785  $latest_page = $current_page = 1;
1786  }
1787 
1788  // We have to do this here, because we cannot break the old behaviour
1789  // where even the summary/thank you page is surrounded by form tags
1790 
1791  if ($action === 'save_and_exit') {
1792  echo $this->getExitPage();
1793  } else {
1794  if (!$finished) {
1795  // If the submission hasn't been created yet, don't bother storing
1796  // current page or anything like
1797  if (isset($submission)) {
1798  $submission->setAttrValue('current_page', $current_page);
1799  if ($current_page > $latest_page) {
1800  $submission->setAttrValue('latest_page', $current_page);
1801  }
1802  $submission->saveAttributes(FALSE, $this->attr('log_submissions'));
1803  } else if (isset($submission_var)) {
1804  $_SESSION['SQ_FORM_'.$this->id.'_SUBMISSION']['current_page'] = $current_page;
1805  if ($current_page > $latest_page) {
1806  $_SESSION['SQ_FORM_'.$this->id.'_SUBMISSION']['latest_page'] = $current_page;
1807  }
1808  }
1809  }
1810 
1811 
1812 
1813  echo $this->getFormHeader();
1814 
1815  if (!$finished) {
1816  // Print the current page or the Confirmation Page if we're confirming things
1817  $total_pages = $this->getTotalPages();
1818 
1819  if (($current_page == $total_pages) && $this->attr('use_confirmation_page')) {
1820  echo $this->getConfirmationPageContents();
1821  }
1822  else {
1823  $this->printPageContentsBodycopy();
1824  }
1825  } else {
1826  $ok = $this->completeSubmission();
1827  if ($ok) {
1828  // On-submit actions went through okay, so we can finish
1829  // the form
1830  $this->finishForm();
1831  } else {
1832  // We had problems, so re-print the last page
1833  $this->printPageContentsBodycopy();
1834  }
1835  }
1836 
1837  echo $this->getFormFooter();
1838  }
1839 
1840  if ($finished) {
1841  $this->_finaliseStepsActions();
1842  }
1843 
1844  }//end printBody()
1845 
1846 
1853  public function getFormHeader()
1854  {
1855  $form_prefix = $this->getPrefix();
1856  $form_action = $this->getFormAction();
1857 
1858  if ($this->attr('use_client_side')) {
1859  $on_submit = 'onsubmit="return beforeSubmit_'.$form_prefix.'(this);"';
1860  $header = '<form id="'.$form_prefix.'" enctype="multipart/form-data" action="'.$form_action.'" method="post" '.$on_submit.'>';
1861  $header .= $this->getClientSideFunction();
1862  } else {
1863  $header = '<form id="'.$form_prefix.'" enctype="multipart/form-data" action="'.$form_action.'" method="post">';
1864  }
1865 
1866  $current_submission = $this->getSubmissionAsset();
1867 
1868  $submission_var = array_get_index($_SESSION, 'SQ_FORM_'.$this->id.'_SUBMISSION', NULL);
1869 
1870  if ($current_submission) {
1871  ob_start();
1872  hidden_field('SQ_FORM_'.$this->id.'_SUBMISSION', $current_submission->id);
1873  hidden_field('SQ_FORM_'.$this->id.'_PAGE', $current_submission->attr('current_page'));
1874  $header .= ob_get_clean();
1875  } else if ($submission_var) {
1876  ob_start();
1877  hidden_field('SQ_FORM_'.$this->id.'_PAGE', array_get_index($submission_var, 'current_page', 1));
1878  $header .= ob_get_clean();
1879  } else {
1880  ob_start();
1881  hidden_field('SQ_FORM_'.$this->id.'_PAGE', 1);
1882  $header .= ob_get_clean();
1883  }
1884 
1885  return $header;
1886 
1887  }//end getFormHeader()
1888 
1889 
1895  public function getFormAction()
1896  {
1897  $form_action = $_SERVER['PHP_SELF'];
1898 
1899  // Add the query string as well if it exists, so asset references with ?a=###
1900  // submit back to themselves.
1901  // BUT, remove internal page or submission override GET variables, otherwise
1902  // we'll be stuck at the one page once a GET var comes in
1903  if (!empty($_SERVER['QUERY_STRING'])) {
1904  preg_match_all('/([^=&]*)\=([^&]*)/', $_SERVER['QUERY_STRING'], $matches);
1905  $matches = array_combine($matches[1], $matches[2]);
1906 
1907  foreach ($matches as $key => $value) {
1908  if (($key === 'SQ_FORM_'.$this->id.'_PAGE') || ($key === 'SQ_FORM_'.$this->id.'_SUBMISSION')) {
1909  unset($matches[$key]);
1910  } else {
1911  // URLdecode the match because make_raw_post_data() will re-encode it
1912  $matches[$key] = urldecode($matches[$key]);
1913  }
1914  }
1915 
1916  $form_action .= '?'.htmlspecialchars(make_raw_post_data($matches), ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET);
1917  }
1918 
1919  return $form_action;
1920 
1921  }//end getFormAction()
1922 
1923 
1935  public function getPageContents($page_number)
1936  {
1937  $page_number = max($page_number, 1);
1938  ob_start();
1939 
1940  // Firstly, find out whether we should actually be displaying the
1941  // confirmation page: if it is enabled and we are supposedly on the
1942  // final page, it MUST be the confirmation page
1943  $total_pages = $this->getTotalPages();
1944 
1945  if (($page_number == $total_pages) && $this->attr('use_confirmation_page')) {
1946  echo $this->getConfirmationPageContents();
1947  } else {
1948  // We are on a "normal" form page
1949  if ($this->attr('multi_page')) {
1950  // do we have any unattached questions?
1951  $questions = $this->getQuestions();
1952 
1953  // Get top-level sections and re-base their indices
1954  $sections = $this->getSectionLinks();
1955 
1956  // If we have unattached questions, then reduce the page number
1957  // internally - so that section 1 will be on page 2
1958  if (!empty($questions)) {
1959  $page_number--;
1960  }
1961 
1962  if ($page_number == 0) {
1963  // Printing unattached questions (if any)
1964  $bodycopy = $this->getBodycopy('Unattached Questions Format');
1965 
1966  // check to see if the unattached bodycopy exists and has any content
1967  if ($this->attr('use_bodycopy_unattached') && $bodycopy && !$bodycopy->isEmpty()) {
1968  $keyword_list = $bodycopy->getKeywords();
1969 
1970  if (!empty($keyword_list)) {
1971  foreach ($keyword_list as $keyword) {
1972  // Question and section-related keywords are found here
1973  $replacement = $this->getDisplayKeywordReplacement($keyword);
1974 
1975  if (is_null($replacement)) {
1976  // If not picked up by first function, this will pick up
1977  // replacements placed in separate functions - CAPTCHA,
1978  // errors, for instance
1979  $replacement = $this->getKeywordReplacement($keyword);
1980  }
1981 
1982  if (!is_null($replacement)) {
1983  // We have something from one of the above
1984  $replacements[$keyword] = $replacement;
1985  }
1986  }
1987  }
1988 
1989  $bodycopy->setKeywordReplacements($replacements);
1990  $bodycopy->printBody();
1991 
1992  } else {
1993  // Grab the default "unattached questions" content file (at the moment)
1994  require_once $this->data_path.'/content_file_questions.php';
1995  }
1996  } else {
1997  // Printing a section - sections are zero based after they were
1998  // re-based by array_values
1999  $section_link = reset(array_slice($sections, $page_number - 1, 1));
2000  $section = $GLOBALS['SQ_SYSTEM']->am->getAsset($section_link['minorid']);
2001  $section->printBody($this, FALSE);
2002  }
2003  } else {
2004  $this->printPageContentsBodycopy();
2005  }
2006  }
2007 
2008  $contents = ob_get_clean();
2009  return $contents;
2010 
2011  }//end printPage()
2012 
2013 
2019  public function getFormFooter()
2020  {
2021  return '</form>';
2022 
2023  }//end getFormFooter()
2024 
2025 
2033  {
2034  $this->printForm();
2035 
2036  }//end printPageContentsBodycopy()
2037 
2038 
2046  {
2047  // Make sure that the thank you bodycopy is always able to access the
2048  // newly created submission in replicated database instances, by tying
2049  // it all to db2
2050  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
2051  $this->finishForm();
2052  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
2053 
2054  }//end printThankYouBodycopy()
2055 
2056 
2071  function printForm($replacements=Array())
2072  {
2073  $bodycopy = $this->getBodycopy('Page Contents');
2074 
2075  // check to see if the bodycopy attached to this form has any content
2076  if ($this->attr('use_bodycopy_main') && $bodycopy && !$bodycopy->isEmpty()) {
2077  $keyword_list = $bodycopy->getKeywords();
2078 
2079  if (!empty($keyword_list)) {
2080  foreach ($keyword_list as $keyword) {
2081  // Question and section-related keywords are found here
2082  $replacement = $this->getDisplayKeywordReplacement($keyword);
2083 
2084  if (is_null($replacement)) {
2085  // If not picked up by first function, this will pick up
2086  // replacements placed in separate functions - CAPTCHA,
2087  // errors, for instance
2088  $replacement = $this->getKeywordReplacement($keyword);
2089  }
2090 
2091  if (!is_null($replacement)) {
2092  // We have something from one of the above
2093  $replacements[$keyword] = $replacement;
2094  }
2095  }
2096  }
2097 
2098  $bodycopy->setKeywordReplacements($replacements);
2099  $bodycopy->printBody();
2100 
2101  } else {
2102  $multi_page = $this->attr('multi_page');
2103  echo $this->printDefaultPageContents($multi_page ? FALSE : TRUE);
2104 
2105  }//end else
2106 
2107  }//end printForm()
2108 
2109 
2119  public function printDefaultPageContents($also_print_sections=TRUE)
2120  {
2121  $current_page = $this->getCurrentPageNumber();
2122 
2123  $error = !empty($this->submission_errors);
2124 
2125  $form_width = $this->attr('form_width');
2126 
2127  if (!empty($form_width)) {
2128  $form_width = ' width="'.$form_width.'"';
2129  }
2130 
2131  $question_width = $this->attr('question_col_width');
2132  if (!empty($question_width)) {
2133  $question_width = ' width="'.$question_width.'"';
2134  }
2135 
2136  $answer_width = $this->attr('answer_col_width');
2137  if (!empty($answer_width)) {
2138  $answer_width = ' width="'.$answer_width.'"';
2139  }
2140 
2141  // note: the HTML section below is using the keywords so that it's easier
2142  // to move it to a bodycopy later (as a default value)
2143  // that way we will not need 2 paths of processing
2144  ob_start();
2145  ?>
2146  %<?php echo $also_print_sections ? 'form' : 'current_page' ?>_contents%<table <?php echo $form_width; ?>>
2147  <tr>
2148  <td <?php echo $question_width; ?>>&nbsp;</td>
2149  <td <?php echo $answer_width; ?>>
2150  <?php
2151  if ($this->attr('require_captcha') && ($current_page == $this->getTotalPages())) {
2152  ?>
2153  %form_captcha%
2154  <?php
2155  }
2156  ?>
2157  %submit_button%
2158  %reset_button%
2159  </td>
2160  </tr>
2161  <?php
2162  if ($error) {
2163  ?>
2164  <tr><td colspan="2">%form_errors%</td></tr>
2165  <?php
2166  }
2167  ?>
2168  </table>
2169  <?php
2170  $content = ob_get_clean();
2171 
2172  require_once SQ_FUDGE_PATH.'/general/text.inc';
2173 
2174  $keyword_list = extract_keywords($content);
2175  if (!empty($keyword_list)) {
2176  foreach ($keyword_list as $keyword) {
2177  if (isset($replacements[$keyword])) continue;
2178  $replacements[$keyword] = $this->getKeywordReplacement($keyword);
2179  }
2180 
2181  replace_keywords($content, $replacements);
2182  }
2183 
2184  return $content;
2185 
2186  }//end printDefaultPageContents()
2187 
2188 
2194  {
2195 
2196 
2197  }//end getResumeSubmissionPageContents()
2198 
2199 
2206  function printStandard()
2207  {
2208  $prefix = $this->getPrefix();
2209 
2210  $form_processed = FALSE;
2211 
2212  // check to see if the form has been submitted or not
2213  if (isset($_POST[$prefix.'_submit'])) {
2214  $form_processed = $this->processForm();
2215  }
2216 
2217  if ($form_processed) {
2218  $this->printThankYouBodycopy();
2219  } else {
2220  $this->printPageContentsBodycopy();
2221  }
2222 
2223  return TRUE;
2224 
2225  }//end printStandard()
2226 
2227 
2236  public function getExitPage()
2237  {
2238  $contents = '';
2239 
2240  if ($this->attr('use_bodycopy_exit')) {
2241  $bodycopy = $this->getBodycopy('Exit Page Contents');
2242 
2243  if ($bodycopy) {
2244  ob_start();
2245 
2246  $keyword_list = $bodycopy->getKeywords();
2247  $replacements = Array();
2248 
2249  foreach ($keyword_list as $keyword) {
2250  // Allow all other display keywords, but explicitly prohibit
2251  // HTML field keywords
2252  if (strpos($keyword, 'question_field_') !== 0) {
2253  $keyword_value = $this->getDisplayKeywordReplacement($keyword);
2254  if (!empty($keyword_value)) {
2255  $replacements[$keyword] = $keyword_value;
2256  }
2257  }
2258  }
2259 
2260  $bodycopy->setKeywordReplacements($replacements);
2261  $bodycopy->printBody();
2262 
2263  $contents = ob_get_clean();
2264  }
2265  }
2266 
2267  return $contents;
2268 
2269  }//end getExitPage()
2270 
2271 
2288  function createBodycopy($name, $content=Array())
2289  {
2290  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
2291  $db = MatrixDAL::getDb();
2292 
2293  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
2294 
2295  // search for a bodycopies folder and create one if there isn't one there
2296  $folder_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'bodycopies_folder');
2297  if (empty($folder_link)) {
2298 
2299  // create a folder for the bodycopies
2300  $GLOBALS['SQ_SYSTEM']->am->includeAsset('folder');
2301  $folder = new Folder();
2302  $folder->setAttrValue('name', 'Bodycopies');
2303 
2304  $folder_link = Array('asset' => &$this, 'link_type' => SQ_LINK_TYPE_2, 'value' => 'bodycopies_folder', 'is_dependant' => 1, 'is_exclusive' => 1);
2305 
2306  if (!$folder->create($folder_link)) {
2307  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
2308  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
2309  return FALSE;
2310  }
2311  } else {
2312  // folder already exists, get the reference
2313  $folder = $GLOBALS['SQ_SYSTEM']->am->getAsset($folder_link['minorid'], $folder_link['minor_type_code']);
2314  }
2315  if (is_null($folder)) {
2316  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
2317  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
2318  return FALSE;
2319  }
2320 
2321  // and see whether this bodycopy already exists
2322  $bodycopy_link = $GLOBALS['SQ_SYSTEM']->am->getLink($folder->id, SQ_LINK_TYPE_2, 'bodycopy', TRUE, $name);
2323  if (!empty($bodycopy_link)) {
2324  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
2325  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
2326  return TRUE;
2327  }
2328 
2329  // bodycopy doesn't exist, so create it
2330  // create a link to the folder so we can put the bodycopies in it
2331  $copy_link = Array('asset' => &$folder, 'link_type' => SQ_LINK_TYPE_2, 'is_dependant' => 1, 'is_exclusive' => 1, 'value' => $name);
2332 
2333  // create the thing
2334  $GLOBALS['SQ_SYSTEM']->am->includeAsset('bodycopy');
2335  $bodycopy = new Bodycopy();
2336  $bodycopy->setAttrValue('name', $name);
2337 
2338  // create an appropriate value for this link
2339  // to reflect that of the name
2340  $name = strtolower(str_replace(' ', '_', $name));
2341 
2342  if (!$bodycopy->create($copy_link, $content)) {
2343  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
2344  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
2345  return FALSE;
2346  }
2347 
2348  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
2349  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
2350 
2351  return TRUE;
2352 
2353  }//end createBodycopy()
2354 
2355 
2364  function &getBodycopy($name='', $link_type=SQ_LINK_TYPE_2)
2365  {
2366  $null = NULL;
2367 
2368  if (!$name) return $null;
2369  $am = $GLOBALS['SQ_SYSTEM']->am;
2370 
2371  $folder_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'bodycopies_folder');
2372  if (empty($folder_link)) return $null;
2373 
2374  $folder = $am->getAsset($folder_link['minorid'], $folder_link['minor_type_code']);
2375  if (is_null($folder)) return $null;
2376 
2377  $bodycopy_link = $GLOBALS['SQ_SYSTEM']->am->getLink($folder->id, $link_type, 'bodycopy', TRUE, $name);
2378  if (empty($bodycopy_link)) return $null;
2379 
2380  $bodycopy = $am->getAsset($bodycopy_link['minorid'], $bodycopy_link['minor_type_code']);
2381 
2382  return $bodycopy;
2383 
2384  }//end getBodycopy()
2385 
2386 
2399  function includeContentFile($type='Standard')
2400  {
2401  $edit_fns = $this->getEditFns();
2402 
2403  switch($type) {
2404  case 'Standard':
2405  if (!file_exists($this->data_path.'/content_file.php')) {
2406  if (!$edit_fns->generateStandardContentFile($this)) {
2407  return FALSE;
2408  }
2409  }
2410  include($this->data_path.'/content_file.php');
2411  break;
2412 
2413  case 'Questions':
2414  if (!file_exists($this->data_path.'/content_file_questions.php')) {
2415  if (!$edit_fns->generateQuestionsContentFile($this)) {
2416  return FALSE;
2417  }
2418  }
2419  include($this->data_path.'/content_file_questions.php');
2420 
2421  break;
2422  }
2423 
2424  return TRUE;
2425 
2426  }//end includeContentFile()
2427 
2428 
2440  public function getTotalPages()
2441  {
2442  if ($this->attr('multi_page')) {
2443  $pages = $this->getSectionCount() + ($this->getQuestionCount() > 0 ? 1 : 0);
2444  } else {
2445  $pages = 1;
2446  }
2447 
2448  // if confirmation page exists, add 1 page
2449  if ($this->attr('use_confirmation_page')) {
2450  $pages++;
2451  }
2452 
2453  return $pages;
2454 
2455  }//end getTotalPages()
2456 
2457 
2458 //-- CONFIRMATION PAGE FUNCTIONS --//
2459 
2460 
2467  {
2468  if ($this->attr('use_bodycopy_confirmation')) {
2469  $bodycopy = $this->getBodycopy('Confirmation Page Contents');
2470  $keyword_list = $bodycopy->getKeywords();
2471  } else {
2472  $contents = $this->getDefaultConfirmationPageContents();
2473  $keyword_list = extract_keywords($contents);
2474  }
2475 
2476  // Replace keywords
2477  $replacements = Array();
2478  if (!empty($keyword_list)) {
2479  foreach ($keyword_list as $keyword) {
2480  $lookup_keyword = $keyword;
2481  // If form_info or form_summary, we need to make sure the
2482  // HTML version gets printed
2483  if (($keyword === 'form_info') || ($keyword == 'form_summary')) {
2484  $lookup_keyword = $keyword.'_html';
2485  }
2486  $replacements[$keyword] = $this->_getThankYouKeywordReplacement($lookup_keyword);
2487  }
2488  }
2489 
2490  if ($this->attr('use_bodycopy_confirmation')) {
2491  ob_start();
2492  $bodycopy->setKeywordReplacements($replacements);
2493  $bodycopy->printBody();
2494  $contents = ob_get_clean();
2495  } else {
2496  replace_keywords($contents, $replacements);
2497  }
2498 
2499  return $contents;
2500 
2501  }//end getConfirmationPageContents()
2502 
2503 
2510  {
2511  ob_start();
2512  ?>
2513  %form_summary_html%
2514 
2515  <?php
2516  if ($this->attr('require_captcha')) {
2517  ?>
2518  <p>%form_captcha%</p>
2519  <?php
2520  }
2521  ?>
2522  %submit_button% %previous_page_button%
2523  <?php
2524  $contents = ob_get_clean();
2525 
2526  return $contents;
2527 
2528  }//end getDefaultConfirmationPageContents()
2529 
2530 
2531 //-- FORM PROCESSING --//
2532 
2533 
2540  function fileRegeneration()
2541  {
2542  $edit = $this->getEditFns();
2543  return $edit->generateStandardContentFile($this);
2544 
2545  }//end fileRegeneration()
2546 
2547 
2561  public function processPage($page_number, $validate_page=TRUE, $mute_errors=FALSE)
2562  {
2563  $ok = TRUE;
2564  // If the form has only one page, it doens't have incomplete submission asset nor session to store captcha_status.
2565  // this var is used to make sure captcha status is processed correctly in this case
2566  $one_page_captcha_status = FALSE;
2567 
2568  if (is_null($this->submission_errors)) $this->submission_errors = Array();
2569 
2570  // Has a captcha been completed on this page? Error if it is incorrect,
2571  // otherwise set the status that we have passed one
2572  // Only when we're going forward, though.
2573  $submission = $this->getSubmissionAsset();
2574 
2575  if ($validate_page && isset($_POST['SQ_SYSTEM_SECURITY_KEY_VALUE'])) {
2576  if (validate_security_key()) {
2577  $submission = $this->getSubmissionAsset();
2578 
2579  if (!empty($submission)) {
2580  $submission->setAttrValue('captcha_status', TRUE);
2581  $submission->saveAttributes(FALSE, $this->attr('log_submissions'));
2582  } else if (isset($_SESSION['SQ_FORM_'.$this->id.'_SUBMISSION'])) {
2583  $submission_var =& $_SESSION['SQ_FORM_'.$this->id.'_SUBMISSION'];
2584  $submission_var['captcha_status'] = TRUE;
2585  } else if ($this->getTotalPages() == 1) {
2586  $one_page_captcha_status = TRUE;
2587  }
2588 
2589  } else {
2590  $this->submission_errors = array_merge($this->submission_errors, Array($this->attr('captcha_invalid_error_message')));
2591  $ok = FALSE;
2592  }
2593  }
2594 
2595  // We have got to the last page (and are moving forward, ie. submitting),
2596  // but we need a captcha to pass and we don't have it yet!
2597  if (!empty($submission)) {
2598  $captcha_status = $submission->attr('captcha_status');
2599  } else if (isset($_SESSION['SQ_FORM_'.$this->id.'_SUBMISSION'])) {
2600  $submission_var =& $_SESSION['SQ_FORM_'.$this->id.'_SUBMISSION'];
2601  $captcha_status = array_get_index($submission_var, 'captcha_status', FALSE);
2602  } else if ($one_page_captcha_status === TRUE) {
2603  $captcha_status = TRUE;
2604  } else {
2605  $captcha_status = FALSE;
2606  }
2607 
2608  // first thing we need to check is if the number of submission is limited
2609  // and if so see if the submission limit has been reached while this user
2610  // was still filling up the form
2611  $submission_limit = $this->attr('submission_limit');
2612  if ($submission_limit != 0 && $submission_limit <= $this->getSubmissionCount()){
2613  $error_text = $this->attr('submission_limit_reached_text');
2614  $this->submission_errors = array_merge($this->submission_errors, Array($error_text));
2615  $ok = FALSE;
2616  }
2617 
2618  // only display this error message if captcha error isn't displayed at current page
2619  if (($page_number == $this->getTotalPages()) && !in_array($this->attr('captcha_invalid_error_message'),$this->submission_errors) && $validate_page && !$captcha_status && $this->attr('require_captcha')) {
2620  $this->submission_errors = array_merge($this->submission_errors, Array($this->attr('captcha_form_invalid_error_message')));
2621  $ok = FALSE;
2622  }
2623 
2624  // Questions to be verified by Mollom
2625  $questions_to_verify = $this->attr('mollom_questions_to_check');
2626  $mollom_parameters = Array();
2627 
2628  $questions = $this->getQuestionAssetsOnPage($page_number);
2629  for (reset($questions); $question =& $questions[key($questions)]; next($questions)) {
2630 
2631  // There is no point in populating the question if answer is not posted
2632  if (isset($_POST['SQ_FORM_'.$this->id.'_PAGE'])) {
2633  $question->populate($this);
2634  $content_type = isset($questions_to_verify[$question->id]) ? $questions_to_verify[$question->id] : -1;
2635  if ($content_type > -1) {
2636  $mollom_parameters[$content_type] = $question->getValue();
2637  }
2638  }
2639 
2640  // Only fail the page if we are moving forward
2641  // File upload question should always get validated
2642  $question_type = strtolower($question->type());
2643  $valid_value = $question->hasValidValue(NULL, FALSE);
2644 
2645  if (!$valid_value && (($question_type == 'form_question_type_file_upload') || $validate_page)) {
2646  //mute error if there is no action, such as a public user refreshs page when there is a file upload question
2647  if (!$mute_errors) {
2648  $this->submission_errors = array_merge($this->submission_errors, $question->failed_rules);
2649  }
2650  $ok = FALSE;
2651  }
2652 
2653  $this->current_answers[$question->id] = $question->getValue();
2654  $this->extra_data[$question->id] = $question->getExtraData();
2655  }
2656 
2657  // Mollom spam check
2658  if ($ok && $this->attr('use_mollom') && !empty($mollom_parameters)) {
2659  $mollom_result = Mollom::verifySubmission($this->id, $mollom_parameters);
2660  $mollom_error = '';
2661  switch ($mollom_result) {
2662  case SQ_MOLLOM_SPAM:
2663  // Mollom has identified this submission as spam
2664  $error_text = $this->attr('mollom_spam_submission_text');
2665  if ($error_text) {
2666  $mollom_error = $error_text;
2667  } else {
2668  $mollom_error = translate('cms_form_mollom_spam_submission_message');
2669  }
2670  break;
2671 
2672  case SQ_MOLLOM_UNSURE:
2673  // Mollom is not sure about the submission
2674  $error_text = $this->attr('mollom_unsure_submission_text');
2675  if ($error_text) {
2676  $mollom_error = $error_text;
2677  } else {
2678  $mollom_error = translate('cms_form_mollom_unsure_spam_submission_message');
2679  }
2680  break;
2681 
2682  case SQ_MOLLOM_HAM:
2683  // Everything cool
2684  break;
2685 
2686  case SQ_MOLLOM_UNKNOWN:
2687  // No response from Mollom
2688  break;
2689 
2690  case SQ_MOLLOM_INCORRECT_CAPTCHA:
2691  $error_text = $this->attr('mollom_captcha_incorrect_text');
2692  if($error_text) {
2693  $mollom_error = $error_text;
2694  } else {
2695  $mollom_error = translate('cms_form_mollom_invalid_captcha_message');
2696  }
2697  break;
2698  }//end switch
2699 
2700  if (!empty($mollom_error)) {
2701  $this->submission_errors = array_merge($this->submission_errors, Array($mollom_error));
2702  $ok = FALSE;
2703  }
2704 
2705  }//end use Mollom
2706 
2707  return $ok;
2708 
2709  }//end processPage()
2710 
2711 
2718  function repopulate()
2719  {
2720  if (empty($this->current_answers) && empty($this->extra_data)) return;
2721 
2722  $questions = $this->getAllQuestionAssets();
2723  foreach (array_keys($questions) as $key) {
2724  if (array_key_exists($key, $this->current_answers)) {
2725  $question = $questions[$key];
2726  $question->setValue($this->current_answers[$question->id]);
2727  }
2728 
2729  if (array_key_exists($key, $this->extra_data)) {
2730  $question = $questions[$key];
2731  $extra_data = $this->extra_data[$question->id];
2732  if (!empty($extra_data)) {
2733  $question->setExtraData($this->extra_data[$question->id]);
2734  }
2735  }
2736  }
2737 
2738  }//end repopulate()
2739 
2740 
2747  function printErrors()
2748  {
2749  if (!empty($this->submission_errors)) {
2750  echo '<ul>';
2751  foreach ($this->submission_errors as $error) {
2752  echo '<li>';
2753  echo $error;
2754  echo '</li>';
2755  }
2756  echo '</ul>';
2757  }
2758  return TRUE;
2759 
2760  }//end printErrors()
2761 
2762 
2771  function getXML($time=NULL)
2772  {
2773  $submission = $this->getSubmissionAsset();
2774  $submission_id = $submission->id;
2775  $submission_ip = $submission->getIP();
2776 
2777  ob_start();
2778  echo '<submission time="'.str_replace(' ', 'T', ts_iso8601((is_null($time) ? time() : $time))).'"';
2779  echo ' ip="'.$submission_ip.'"';
2780  echo ' assetid="'.$submission_id.'"';
2781  if (!is_null($cur_user = $GLOBALS['SQ_SYSTEM']->user)) {
2782  echo ' user="'.$cur_user->id.'"';
2783  }
2784  echo '>';
2785 
2786  // unattached questions first
2787  $sort_order = $this->attr('sort_order');
2788 
2789  foreach ($sort_order as $sort_order_index => $assetid) {
2790 
2791  // check to see whether this belongs to us (if it isn't then perhaps a section took care)
2792  if ($this->id == strtok($assetid, ':')) {
2793  $question = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
2794  echo $question->getXML($question->getValue());
2795  }
2796 
2797  }
2798 
2799  // now sections
2800  $sections = $this->getSections();
2801  for (reset($sections); NULL !== ($k = key($sections)); next($sections)) {
2802  $section =& $sections[$k];
2803  echo '<section id="'.$section->id.'" name="'.$section->attr('name').'">';
2804 
2805  $sort_order = $section->attr('sort_order');
2806 
2807  foreach ($sort_order as $sort_order_index => $assetid) {
2808 
2809  // check to see whether this belongs to the section
2810  if ($section->id == strtok($assetid, ':')) {
2811  $question = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
2812  echo $question->getXML($question->getValue($assetid));
2813  }
2814 
2815  }
2816 
2817  echo '</section>';
2818 
2819  }
2820 
2821  echo '</submission>';
2822 
2823  $contents = ob_get_contents();
2824  ob_end_clean();
2825 
2826  return $contents;
2827 
2828  }//end getXML()
2829 
2830 
2839  function printSummary($html=FALSE)
2840  {
2841  echo ($html) ? '<table>' : '';
2842 
2843  // unattached questions first
2844  $sort_order = $this->attr('sort_order');
2845 
2846  foreach ($sort_order as $sort_order_index => $assetid) {
2847 
2848  // check to see whether this belongs to us (if it isn't then perhaps a section took care)
2849  if ($this->id == strtok($assetid, ':')) {
2850  $question = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
2851  echo ($html) ? '<tr><td><ul><li>' : '';
2852  echo $question->attr('name');
2853  echo ($html) ? ': </li></ul></td><td>' : ' : ';
2854  if ($html) {
2855  echo htmlspecialchars($question->getSummary(), ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET);
2856  } else {
2857  echo $question->getSummary();
2858  }
2859  echo ($html) ? '</td></tr>' : "\n";
2860  }
2861 
2862  }
2863 
2864  // now sections
2865  $sections = $this->getAllSections();
2866  for (reset($sections); NULL !== ($k = key($sections)); next($sections)) {
2867  $section =& $sections[$k];
2868  $section->current_answers =& $this->current_answers;
2869  $section->extra_data =& $this->extra_data;
2870  echo ($html) ? '<tr><td colspan="2">' : "\n";
2871  echo ($html) ? '<strong>'.$section->attr('name').'</strong>' : '** '.$section->attr('name').' **';
2872  echo ($html) ? '</td></tr>' : "\n\n";
2873 
2874  $sort_order = $section->attr('sort_order');
2875 
2876  foreach ($sort_order as $sort_order_index => $assetid) {
2877 
2878  // check to see whether this belongs to the section
2879  if ($section->id == strtok($assetid, ':')) {
2880  $question = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
2881  echo ($html) ? '<tr><td><ul><li>' : '';
2882  echo $question->attr('name');
2883  echo ($html) ? ': </li></ul></td><td>' : ' : ';
2884  if ($html) {
2885  echo htmlspecialchars($question->getSummary(), ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET);
2886  } else {
2887  echo $question->getSummary();
2888  }
2889  echo ($html) ? '</td></tr>' : "\n";
2890  }
2891 
2892  }
2893  echo ($html) ? '' : "\n";
2894  }
2895 
2896  echo ($html) ? '</table>' : '';
2897 
2898  }//end printSummary()
2899 
2900 
2904  public function printSemanticSummary()
2905  {
2906  echo '<ul id="sq-form-summary-semantic-'.$this->id.'" class="sq-form-summary-semantic">'."\n";
2907 
2908  if ($this->getQuestionCount() > 0) {
2909  echo '<li>'."\n";
2910  echo '<strong>'.htmlspecialchars($this->attr('unattached_page_name'), ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET).'</strong>'."\n";
2911  echo '<dl>'."\n";
2912  foreach ($this->attr('sort_order') as $sort_order => $question_assetid) {
2913 
2914  // Make sure that we own this question
2915  if ($this->id == strtok($question_assetid, ':')) {
2916  $question = $GLOBALS['SQ_SYSTEM']->am->getAsset($question_assetid);
2917  echo '<dt>'.$question->attr('name').'</dt>'."\n";
2918  echo '<dd>'.htmlspecialchars($question->getSummary(), ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET).'</dd>'."\n";
2919  }
2920 
2921  }
2922  echo '</dl>'."\n";
2923  echo '</li>'."\n";
2924  }
2925 
2926  // now sections - get the section to print itself out
2927  $section_links = $this->getSectionLinks();
2928  foreach ($section_links as $section_link) {
2929  echo '<li>'."\n";
2930  $section = $GLOBALS['SQ_SYSTEM']->am->getAsset($section_link['minorid']);
2931  echo '<strong>'.htmlspecialchars($section->attr('name'), ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET).'</strong>'."\n";
2932  $section->printSemanticSummary();
2933  echo '</li>'."\n";
2934  }
2935 
2936  echo '</ul>';
2937 
2938  }//end printSemanticSummary()
2939 
2940 
2947  function getFormInfo()
2948  {
2949  $info = Array();
2950  $am = $GLOBALS['SQ_SYSTEM']->am;
2951 
2952  $lineage = $am->getLineageFromURL();
2953  foreach ($lineage as $asset) {
2954  $info[$asset['type_code'].'_name'] = $asset['name'];
2955  $info[$asset['type_code'].'_id'] = $asset['assetid'];
2956  }
2957  $info['url'] = current_url(TRUE, TRUE);
2958 
2959  if (!is_null($this->submission_asset)) {
2960  $info['submission_id'] = $this->submission_asset->id;
2961  }
2962 
2963  return $info;
2964 
2965  }//end getFormInfo()
2966 
2967 
2968 //-- CLIENT-SIDE VALIDATION --//
2969 
2970 
2980  function generateJSCode()
2981  {
2982  if (!$this->attr('use_client_side')) return '';
2983 
2984  $keywords = Array();
2985  $use_bc = ($this->attr('use_bodycopy_main') == 1) ? TRUE : FALSE;
2986 
2987  if ($use_bc) {
2988  // Bodycopy is being used, so lets get its keywords
2989  $bc = $this->getBodycopy('Page Contents');
2990  $keywords = $bc->getKeywords();
2991 
2992  // Form contents is printed, which is just like not using a bodycopy(in this context)
2993  // So we will print all questions' js code.
2994  if (in_array('form_contents', $keywords) || in_array('current_page_contents', $keywords)) {
2995  $use_bc = FALSE;
2996  } else {
2997  // Check if we have section keywords in bodycopy
2998  $section_keywords = Array();
2999  foreach($keywords as $keyword) {
3000  if (strpos($keyword, 'section_contents_') !== FALSE) {
3001  $section_id = substr($keyword, strlen('section_contents_'));
3002  if ($GLOBALS['SQ_SYSTEM']->am->getAssetInfo($section_id, Array('form_section'))) {
3003  $section = $GLOBALS['SQ_SYSTEM']->am->getAsset($section_id);
3004  $s_questions = array_keys($section->getQuestions());
3005  foreach($s_questions as $q_id) {
3006  $q_keyword = 'question_field_'.$section_id.'_q'.$q_id;
3007  if (!in_array($q_keyword, $keywords)) {
3008  $section_keywords[] = $q_keyword;
3009  }
3010  }//end foreach
3011  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($section);
3012  }//end if
3013  }//end if
3014  }//end foreach
3015 
3016  // Merge the questions (keywords) contained in form section
3017  $keywords = array_merge($keywords, $section_keywords);
3018  }//end else
3019  }
3020 
3021  ob_start();
3022  ?>
3023  submission_errors = new Array();
3024  <?php
3025  $code = ob_get_contents();
3026  ob_end_clean();
3027 
3028  if (!in_array('form_contents', $keywords) && !$use_bc && $this->attr('multi_page')) {
3029  $page_num = $this->getCurrentPageNumber();
3030  $questions = $this->getQuestionAssetsOnPage($page_num);
3031  } else {
3032  $questions = $this->getAllQuestionAssets();
3033  }
3034 
3035  foreach (array_keys($questions) as $key) {
3036  $question = $GLOBALS['SQ_SYSTEM']->am->getAsset($key);
3037  $q_code = '';
3038  if ($use_bc) {
3039  $keyword_search = 'question_field_'.str_replace(':', '_', $question->id);
3040  if (in_array($keyword_search, $keywords)) {
3041  // keyword is present in the bodycopy, we can safely print the js code.
3042  $q_code = $question->generateJSCode();
3043  }
3044  } else {
3045  $q_code = $question->generateJSCode();
3046  }
3047  $code .= $q_code.(empty($q_code) ? '' : "\n");
3048  }
3049 
3050  return $code;
3051 
3052  }//end generateJSCode()
3053 
3054 
3062  {
3063  $code = $this->generateJSCode();
3064  if (empty($code)) return '';
3065 
3066  ob_start();
3067  ?>
3068  <script type="text/javascript">
3069  function beforeSubmit_<?php echo $this->getPrefix() ?>(form)
3070  {
3071  var submission_errors = new Array();
3072  i = 0;
3073 
3074  <?php echo $code; ?>
3075 
3076  if (submission_errors.length > 0) {
3077  var errors_list = "<?php echo translate('cms_form_submission_errors'); ?>\n";
3078  for(x in submission_errors) {
3079  errors_list += submission_errors[x] + "\n";
3080  }
3081  alert(errors_list);
3082  return false;
3083  } else {
3084  return true;
3085  }
3086  }//end beforeSubmit_<?php echo $this->getPrefix() ?>()
3087  </script>
3088  <?php
3089  $code = ob_get_contents();
3090  ob_end_clean();
3091 
3092  return $code;
3093 
3094  }//end getClientSideFunction()
3095 
3096 
3097 //-- POST-SUBMISSION PROCESSING --//
3098 
3099 
3115  public function completeSubmission()
3116  {
3117  $submission_asset = $this->getSubmissionAsset();
3118  $all_questions = $this->getAllQuestions();
3119 
3120  $ok = TRUE;
3121 
3122  // if it's a multi page form, we have to revalidate all questions to prevent hacky behaviour
3123  if($this->attr('multi_page')) {
3124  foreach ($all_questions as $assetid => $question_info) {
3125  $question_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
3126  if(!$question_asset->hasValidValue()) {
3127  $this->submission_errors = array_merge($this->submission_errors, $question_asset->failed_rules);
3128  $ok = FALSE;
3129  }
3130  }
3131  }
3132  // don't bother going further, we don't want gunky file assets
3133  if(!$ok) return FALSE;
3134 
3135  foreach ($all_questions as $assetid => $question_info) {
3136  $type_code = $question_info['question_type_code'];
3137  $GLOBALS['SQ_SYSTEM']->am->includeAsset($type_code);
3138  if (method_exists($type_code, 'onSubmitForm')) {
3139  $question_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
3140  $question_asset->onSubmitForm($this);
3141 
3142  if (!empty($question_asset->failed_rules)) {
3143  if (!is_array($this->submission_errors)) $this->submission_errors = Array();
3144  $this->submission_errors = array_merge($this->submission_errors, $question_asset->failed_rules);
3145  $ok = FALSE;
3146  }
3147  }
3148  }
3149 
3150  // create the submission asset
3151  if ($ok) {
3152  if ($this->attr('log_submissions')) {
3153  if ($GLOBALS['SQ_SYSTEM']->userPublic()) {
3154  // Fill new submission asset with current answers
3155  $submission_asset = $this->createSubmission(TRUE);
3156  $this->submission_asset = $submission_asset;
3157  $this->updateSubmission($submission_asset);
3158  } else {
3159  // It'll exist in Incomplete. Move to completed (as long
3160  // as the Incomplete folder actually exists...)
3161  $incomplete_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'incomplete_submissions');
3162  if ($incomplete_link) {
3163  $complete_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'submissions_folder');
3164 
3165  $submission_link = $GLOBALS['SQ_SYSTEM']->am->getLinkByAsset($incomplete_link['minorid'], $submission_asset->id);
3166  if ($submission_link) {
3167  // We need to force this through, for the same reason as we
3168  // need to force for the creation of a submission. Without
3169  // this, a User asset without write access to the form would
3170  // not be able to move it to the Completed Submissions folder
3171  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
3172  $GLOBALS['SQ_SYSTEM']->am->moveLink($submission_link['linkid'], $complete_link['minorid'], SQ_LINK_TYPE_3, 0);
3173  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
3174  }
3175  }
3176  }
3177 
3178  // Handle file uploads depending on whether create location is set
3179  // We then need to update the Submission to handle any changed Extra Data
3180  $this->_handleCompletedFileUploads();
3181  $this->updateSubmission($submission_asset);
3182 
3183  $submission_asset->setIP($_SERVER['REMOTE_ADDR']);
3184  $submission_asset->setAttrValue('complete', TRUE);
3185  $submission_asset->setAttrValue('submitted', ts_iso8601(time()));
3186  $submission_asset->setAttrValue('xml', $this->getXML());
3187  $submission_asset->saveAttributes();
3188  } else {
3189  // Trash it if it exists
3190  // We need to force this through, for the same reason as we
3191  // need to force for the creation of a submission. Without
3192  // this, a User asset without write access to the form would
3193  // not be able to move it to the tarash and delete it permanently
3194  if ($submission_asset) {
3195  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_SECURITY_LINK_INTEGRITY);
3196  $GLOBALS['SQ_SYSTEM']->am->trashAsset($submission_asset->id);
3197  $submission_asset->delete();
3198  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
3199  }
3200  }
3201 
3202  }//end if
3203 
3204  return $ok;
3205 
3206  }//end completeSubmission()
3207 
3208 
3218  protected function _handleCompletedFileUploads()
3219  {
3220  $submission_asset = $this->getSubmissionAsset();
3221  $file_uploads = $this->getAllQuestionAssets('form_question_type_file_upload');
3222 
3223  foreach (array_keys($file_uploads) as $assetid) {
3224  $file_upload = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
3225 
3226  $create_location = $file_upload->attr('create_location');
3227  if (empty($create_location)) {
3228  $extra_data = $file_upload->getExtraData();
3229  if (!empty($extra_data['temp_filesystem_path'])) {
3230  $filename = $file_upload->getValue();
3231  $perm_path = $submission_asset->data_path.'/attachments/'.str_replace(':', '_', $file_upload->id);
3232 
3233  // Move it using Fudge file_system function
3234  create_directory($perm_path);
3235  $moved = move_file($extra_data['temp_filesystem_path'], $perm_path.'/'.$filename);
3236  if ($moved) delete_directory(dirname($extra_data['temp_filesystem_path']));
3237 
3238  $extra_data = Array(
3239  'filesystem_path' => $perm_path.'/'.$filename,
3240  );
3241 
3242  $file_upload->setExtraData($extra_data);
3243  $submission_asset->setExtraData($assetid, $extra_data);
3244  }
3245  } else {
3246  $extra_data = $file_upload->getExtraData();
3247  $uploaded_assetid = array_get_index($extra_data, 'existing_file_assetid', array_get_index($extra_data, 'new_file_assetid', NULL));
3248 
3249  if (!empty($uploaded_assetid)) {
3250  $uploaded_file = $GLOBALS['SQ_SYSTEM']->am->getAsset($uploaded_assetid);
3251 
3252  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
3253  $this->submission_asset->createLink($uploaded_file, SQ_LINK_NOTICE, 'file_submission');
3254  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
3255  }
3256  }
3257  }
3258 
3259  }//end _handleCompletedFileUploads()
3260 
3261 
3268  function finishForm()
3269  {
3270  $tyb = $this->getBodycopy('Thank You');
3271 
3272  ob_start();
3273  $this->printSummary(TRUE);
3274  $summary_info = ob_get_contents();
3275  ob_end_clean();
3276 
3277  // check to see if the bodycopy attached to this form has any content
3278  if ($this->attr('use_bodycopy_thank_you') && $tyb && !$tyb->isEmpty()) {
3279 
3280  if ($this->attr('show_results')) {
3281  $keyword_list = $tyb->getKeywords();
3282  $replacements = Array();
3283 
3284  foreach ($keyword_list as $keyword) {
3285  $lookup_keyword = $keyword;
3286 
3287  // If form_info or form_summary, we need to make sure the
3288  // HTML version gets printed
3289  if (($keyword === 'form_info') || ($keyword == 'form_summary')) {
3290  $lookup_keyword = $keyword.'_html';
3291  }
3292 
3293  $replacement = $this->_getThankYouKeywordReplacement($lookup_keyword);
3294  $replacements[$keyword] = $replacement;
3295  }
3296 
3297  $tyb->setKeywordReplacements($replacements);
3298 
3299  }//end if
3300 
3301  $tyb->printBody();
3302 
3303  } else {
3304  // no bodycopy
3305  // print the results if we need to
3306  if ($this->attr('show_results')) echo $summary_info;
3307 
3308  }
3309 
3310  // Clean-up questions
3311  $all_questions = $this->getAllQuestions();
3312 
3313  foreach ($all_questions as $assetid => $question_info) {
3314  $type_code = $question_info['question_type_code'];
3315  $GLOBALS['SQ_SYSTEM']->am->includeAsset($type_code);
3316  if (method_exists($type_code, 'onSubmitForm')) {
3317  $question_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
3318  $question_asset->cleanUp($this);
3319  }
3320  }
3321 
3322  if (!is_null($this->submission_asset)) {
3323  $submission_path = $this->data_path.'/incomplete_attachments/s'.$this->submission_asset->id;
3324  if (is_dir($submission_path)){
3325  $dirs = list_dirs($submission_path);
3326  if (empty($dirs)) delete_directory($submission_path);
3327  }
3328  }
3329 
3330  $public_submission_path = $this->data_path.'/incomplete_attachments/'.session_id();
3331  if (is_dir($public_submission_path)){
3332  $dirs = list_dirs($public_submission_path);
3333  if (empty($dirs)) delete_directory($public_submission_path);
3334  }
3335 
3336  // Clean-up submission session variable
3337  unset($_SESSION['SQ_FORM_'.$this->id.'_SUBMISSION']);
3338 
3339  }//end finishForm()
3340 
3341 
3342 //-- SUBMISSIONS --//
3343 
3344 
3357  function getSubmissionsFolder($link_value='submissions_folder')
3358  {
3359  $res = NULL;
3360  $link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, $link_value);
3361  if (!empty($link)) {
3362  $res = $GLOBALS['SQ_SYSTEM']->am->getAsset($link['minorid'], 'folder');
3363  }
3364  return $res;
3365 
3366  }//end getSubmissionsFolder()
3367 
3368 
3378  function createSubmissionsFolder($folder_name, $link_value)
3379  {
3380  $am = $GLOBALS['SQ_SYSTEM']->am;
3381  $ok = TRUE;
3382 
3383  // Find an existing folder - if it exists, just return that
3384  $existing_folder = $this->getSubmissionsFolder($link_value);
3385  if ($existing_folder) {
3386  return $existing_folder;
3387  }
3388 
3389  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
3390  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
3391 
3392  $GLOBALS['SQ_SYSTEM']->am->includeAsset('folder');
3393  $folder = new Folder();
3394  $create_info = Array(
3395  'asset' => $this,
3396  'link_type' => SQ_LINK_TYPE_2,
3397  'value' => $link_value,
3398  'is_dependant' => 1,
3399  'is_exclusive' => 1,
3400  );
3401 
3402  $folder->setAttrValue('name', $folder_name);
3403  if (!$folder->create($create_info)) {
3404  // Cannot create it.
3405  $ok = FALSE;
3406  }
3407 
3408  if ($ok) {
3409  // deny public read and write access by default - but may need to force it
3410  // because usually setting permissions requires admin access
3411  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
3412 
3413  $public_userid = $GLOBALS['SQ_SYSTEM']->am->getSystemAssetid('public_user');
3414 
3415  if (!$am->setPermission($folder->id, $public_userid, SQ_PERMISSION_WRITE, 0, TRUE, TRUE)) {
3416  // couldn't deny it - we can't just return the folder with write permission to a public user
3417  $ok = FALSE;
3418  }
3419 
3420  if (!$am->setPermission($folder->id, $public_userid, SQ_PERMISSION_READ, 0, TRUE, TRUE)) {
3421  $ok = FALSE;
3422  }
3423 
3424  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
3425  }
3426 
3427  // End of transaction. If things didn't work out, null the folder out
3428  // and rollback the transaction, otherwise push it through
3429  if ($ok) {
3430  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
3431  } else {
3432  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
3433  $folder = NULL;
3434  }
3435 
3436  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
3437 
3438  return $folder;
3439 
3440  }//end createSubmissionsFolder()
3441 
3442 
3453  function &createSubmission($create_in_completed=FALSE)
3454  {
3455  $am = $GLOBALS['SQ_SYSTEM']->am;
3456 
3457  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
3458  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
3459  $GLOBALS['SQ_SYSTEM']->am->includeAsset('form_submission');
3460 
3461  if ($create_in_completed) {
3462  $link_value = 'submissions_folder';
3463  } else {
3464  $link_value = 'incomplete_submissions';
3465  }
3466 
3467  // create the form submission asset
3468  $submission_asset = new Form_Submission();
3469  $submissions_folder = $this->getSubmissionsFolder($link_value);
3470  if (is_null($submissions_folder)) {
3471  // If we need to create the Incomplete Submissions folder, do so now.
3472  // We shall not do this with the Completed Submissions folder, because
3473  // this should have been created when the form was created
3474  if (!$create_in_completed) {
3475  $submissions_folder = $this->createSubmissionsFolder(translate('cms_form_incomplete_submissions'), 'incomplete_submissions');
3476  }
3477 
3478  if (is_null($submissions_folder)) {
3479  trigger_localised_error('CMS0069', E_USER_WARNING);
3480  $null = NULL;
3481  return $null;
3482  }
3483  }
3484  $copy_link = Array(
3485  'asset' => $submissions_folder,
3486  'link_type' => SQ_LINK_TYPE_3,
3487  'is_dependant' => 0,
3488  'is_exclusive' => 0,
3489  );
3490 
3491  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
3492 
3493  if (!$submission_asset->create($copy_link)) {
3494  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
3495  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
3496  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
3497  return FALSE;
3498  }
3499 
3500  $submission_asset->setAttrValue('complete', FALSE);
3501  // initially created submission asset should have current page set to first page.
3502  $submission_asset->setAttrValue('current_page', 1);
3503  $submission_asset->saveAttributes(FALSE, $this->attr('log_submissions'));
3504 
3505  // The current user will receive write access to this submission
3506  // (as long as one is actually logged in, ie. we are not public)
3507  $public_userid = $GLOBALS['SQ_SYSTEM']->am->getSystemAssetid('public_user');
3508  $current_userid = $GLOBALS['SQ_SYSTEM']->currentUserid();
3509  if ((string)$current_userid !== (string)$public_userid) {
3510  if (!$am->setPermission($submission_asset->id, $current_userid, SQ_PERMISSION_WRITE, 1, TRUE, TRUE)) {
3511  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
3512  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
3513  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
3514  return FALSE;
3515  }
3516  }
3517 
3518  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
3519  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
3520  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
3521 
3522  return $submission_asset;
3523 
3524  }//end createSubmission()
3525 
3526 
3534  {
3535  if (!$this->submission_asset) {
3536  // Find the submission asset, but ensure we are getting it from _POST first
3537  $submission_assetid = array_get_index($_POST, 'SQ_FORM_'.$this->id.'_SUBMISSION', NULL);
3538  if (is_null($submission_assetid)) {
3539  $submission_assetid = array_get_index($_GET, 'SQ_FORM_'.$this->id.'_SUBMISSION', NULL);
3540 
3541  }
3542 
3543  if (!is_null($submission_assetid)) {
3544  $this->submission_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($submission_assetid, 'form_submission');
3545 
3546  if (!$this->submission_asset || !$this->submission_asset->writeAccess()) {
3547  $this->submission_asset = NULL;
3548  }
3549  }
3550 
3551  if (!$this->submission_asset) {
3552  $this->submission_asset = NULL;
3553  }
3554 
3555  // Fill up the current answers from previous parts of the submission
3556  if ($this->submission_asset) {
3557  $answers = $this->submission_asset->getAnswers();
3558  foreach ($answers as $assetid => $response) {
3559  if (is_string($response['answer'])) {
3560  $response['answer'] = html_entity_decode($response['answer']);
3561  }
3562  $this->current_answers[$assetid] = $response['answer'];
3563  $this->extra_data[$assetid] = array_get_index($response, 'extra_data', '');
3564  }
3565  $this->repopulate();
3566  }
3567  }
3568 
3569  return $this->submission_asset;
3570 
3571  }//end getSubmissionAsset()
3572 
3573 
3581  public function updateSubmission(Form_Submission $submission_asset)
3582  {
3583  foreach ($this->current_answers as $assetid => $response) {
3584  $question = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
3585 
3586  // Save and stick the value - but only if the question still exists
3587  if ($question instanceof Form_Question) {
3588  $question->saveValue($this);
3589 
3590  // If the question is sticky, stick the value then
3591  if ($question->attr('sticky')) {
3592  $question->stickValue($question->getValue());
3593  }
3594 
3595  // only re-set the answer if it has changed
3596  $value = $question->getValue();
3597  if (is_string($value)) {
3598  $value = htmlspecialchars($value, ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET);
3599  }
3600  if ($value !== $submission_asset->getAnswer($assetid)) {
3601  $submission_asset->setAnswer($assetid, $value);
3602  }
3603 
3604  // same with Extra Data
3605  if ($question->getExtraData() !== $submission_asset->getExtraData($assetid)) {
3606  $submission_asset->setExtraData($assetid, $question->getExtraData());
3607  }
3608 
3609  $submission_asset->setSummary($assetid, $question->attr('name'), htmlspecialchars($question->getSummary(), ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET));
3610  }
3611  }
3612 
3613  $submission_asset->saveAttributes(FALSE, $this->attr('log_submissions'));
3614 
3615  // If submission asset has metadata schema applied, broadcast the MetadataUpdate event
3616  $mm = $GLOBALS['SQ_SYSTEM']->getMetadataManager();
3617  $applied_schemas = $mm->getSchemas($submission_asset->id);
3618  if (!empty($applied_schemas)) {
3619  $em = $GLOBALS['SQ_SYSTEM']->getEventManager();
3620  $em->broadCastEvent($submission_asset, 'MetadataUpdate', Array('all'));
3621  }
3622 
3623  }//end updateSubmission()
3624 
3625 
3632  public function getIncompleteSubmissions()
3633  {
3634  // If we are not a
3635  if ($GLOBALS['SQ_SYSTEM']->userPublic()) {
3636  return Array();
3637  }
3638 
3639  $incomplete_folder = $this->getSubmissionsFolder('incomplete_submissions');
3640  if (!$incomplete_folder) {
3641  return Array();
3642  }
3643 
3644  $sql_parts = $GLOBALS['SQ_SYSTEM']->am->generateGetChildrenQuery($incomplete_folder, 'form_submission', FALSE);
3645  $sql_parts['sql_array']['where'] .= ' AND (a.assetid IN (SELECT assetid FROM sq_vw_ast_perm WHERE userid = :is_userid AND permission >= :is_min_perm AND granted = :is_granted))';
3646  $sql_parts['bind_vars']['is_userid'] = $GLOBALS['SQ_SYSTEM']->currentUserid();
3647  $sql_parts['bind_vars']['is_min_perm'] = SQ_PERMISSION_WRITE;
3648  $sql_parts['bind_vars']['is_granted'] = '1';
3649 
3650  $sql = implode(' ', $sql_parts['sql_array']);
3651 
3652  try {
3653  $query = MatrixDAL::preparePdoQuery($sql);
3654  foreach ($sql_parts['bind_vars'] as $bind_var => $bind_value) {
3655  MatrixDAL::bindValueToPdo($query, $bind_var, $bind_value);
3656  }
3657  $results = array_keys(MatrixDAL::executePdoGroupedAssoc($query));
3658  } catch (DALException $e) {
3659  throw new Exception($e->getMessage());
3660  }
3661 
3662  return $results;
3663 
3664  }//end getIncompleteSubmissions()
3665 
3666 
3672  public function getCurrentPageNumber()
3673  {
3674  if ($GLOBALS['SQ_SYSTEM']->userPublic()) {
3675  $submission_var =& $_SESSION['SQ_FORM_'.$this->id.'_SUBMISSION'];
3676  $current_page = array_get_index($submission_var, 'current_page', 1);
3677  } else {
3678  $submission_asset = $this->getSubmissionAsset();
3679  if ($submission_asset) {
3680  $current_page = $submission_asset->attr('current_page');
3681  } else {
3682  $current_page = 1;
3683  }
3684  }
3685 
3686  return $current_page;
3687 
3688  }//end getCurrentPageNumber()
3689 
3690 
3696  public function getLatestPageNumber()
3697  {
3698  if ($GLOBALS['SQ_SYSTEM']->userPublic()) {
3699  $submission_var =& $_SESSION['SQ_FORM_'.$this->id.'_SUBMISSION'];
3700  $latest_page = array_get_index($submission_var, 'latest_page', '1');
3701  } else {
3702  $submission_asset = $this->getSubmissionAsset();
3703  if ($submission_asset) {
3704  $latest_page = $submission_asset->attr('latest_page');
3705  } else {
3706  $latest_page = '1';
3707  }
3708  }
3709 
3710  return $latest_page;
3711 
3712  }//end getLatestPageNumber()
3713 
3714 
3727  public function getTempFileUploadPath()
3728  {
3729  $submission_asset = $this->getSubmissionAsset();
3730 
3731  if ($submission_asset) {
3732  $path = $this->data_path.'/incomplete_attachments/s'.$submission_asset->id;
3733  } else {
3734  $path = $this->data_path.'/incomplete_attachments/'.session_id();
3735  }
3736 
3737  return $path;
3738 
3739  }//end getTempFileUploadPath()
3740 
3741 
3742 //-- SUBMISSION VIA SESSION VARIABLE --//
3743 
3744 
3751  {
3752  $session_var_name = 'SQ_FORM_'.$this->id.'_SUBMISSION';
3753 
3754  if (!isset($_SESSION[$session_var_name])) {
3755  $_SESSION[$session_var_name] = Array();
3756  }
3757 
3758  $session_var =& $_SESSION[$session_var_name];
3759 
3760  foreach ($this->getAllQuestionAssets() as $question) {
3761  if (isset($session_var[$question->id])) {
3762  //$question->setValue($session_var[$question->id]['answer']);
3763  //$question->setExtraData($session_var[$question->id]['extra_data']);
3764 
3765  $this->current_answers[$question->id] = $session_var[$question->id]['answer'];
3766  $this->extra_data[$question->id] = array_get_index($session_var[$question->id], 'extra_data', Array());
3767 
3768  $question->setValue($this->current_answers[$question->id]);
3769  $question->setExtraData($this->extra_data[$question->id]);
3770  }
3771  }
3772 
3773  }//end restoreFromSubmissionCookie()
3774 
3775 
3782  public function updateSubmissionCookie()
3783  {
3784  $session_var_name = 'SQ_FORM_'.$this->id.'_SUBMISSION';
3785 
3786  if (!isset($_SESSION[$session_var_name])) {
3787  $_SESSION[$session_var_name] = Array();
3788  }
3789 
3790  foreach ($this->current_answers as $assetid => $response) {
3791  $question = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
3792 
3793  // Save and stick the value
3794  $question->saveValue($this);
3795 
3796  // If the question is sticky, stick the value then
3797  if ($question->attr('sticky')) {
3798  $question->stickValue($question->getValue());
3799  }
3800 
3801  if (isset($_SESSION[$session_var_name][$assetid])) {
3802  $current_answer = $_SESSION[$session_var_name][$assetid]['answer'];
3803  } else {
3804  $current_answer = NULL;
3805  }
3806 
3807  // only re-set the answer if it has changed
3808  if ($question->getValue() !== $current_answer) {
3809  $_SESSION[$session_var_name][$assetid]['answer'] = $question->getValue();
3810  }
3811 
3812  $_SESSION[$session_var_name][$assetid]['name'] = $question->attr('name');
3813  $_SESSION[$session_var_name][$assetid]['summary'] = $question->getSummary();
3814  $_SESSION[$session_var_name][$assetid]['extra_data'] = $question->getExtraData();
3815  }
3816 
3817 
3818  }//end updateSubmissionCookie()
3819 
3820 
3825  {
3826  $submission_asset =& $this->createSubmission(TRUE);
3827  $this->updateSubmission($submission_asset);
3828 
3829  }//end convertSubmissionCookieToAsset()
3830 
3831 
3832 //-- KEYWORDS AVAILABLE --//
3833 
3834 
3845  function onRequestKeywords(&$broadcaster, $vars=Array())
3846  {
3847  if (!isset($vars['keywords'])) return;
3848 
3849  // Find out whether we've actually been sent a form section's keyword
3850  // request; go silent on it if it is
3851  $bc_parents = $GLOBALS['SQ_SYSTEM']->am->getParents($broadcaster->id);
3852  if (!in_array('form_section', $bc_parents)) {
3853 
3854  $keywords = Array();
3855  // search for a bodycopies folder
3856  $folder_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'bodycopies_folder');
3857 
3858  $folder = $GLOBALS['SQ_SYSTEM']->am->getAsset($folder_link['minorid'], $folder_link['minor_type_code']);
3859 
3860  if (is_null($folder)) return FALSE;
3861 
3862  // lets work out which bodycopy the WYSIWYG cell that wants our keywords
3863  // is in so we can return the correct keywords for the screen
3864  $bodycopy_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($folder->id, SQ_LINK_TYPE_2, 'bodycopy');
3865  $parents = $GLOBALS['SQ_SYSTEM']->am->getParents($broadcaster->id, 'bodycopy', TRUE);
3866 
3867  $bodycopy_code = NULL;
3868  foreach ($bodycopy_links as $link_info) {
3869  if (isset($parents[$link_info['minorid']])) {
3870  $bodycopy_code = $link_info['value'];
3871  break;
3872  }
3873  }
3874 
3875  if (is_null($bodycopy_code)) return;
3876 
3877  $keywords = @$this->_tmp['keywords'][$bodycopy_code];
3878  if (is_null($keywords) || !is_array($keywords)) {
3879  $keywords = Array();
3880  // note: if the value on the link is "Thank You", the method providing the keywords should be _getThankYouBodycopyKeywords
3881  $keyword_provider_method = '_get'.str_replace(' ','',ucfirst($bodycopy_code)).'BodycopyKeywords';
3882 
3883  if (method_exists($this, $keyword_provider_method)) {
3884  $keywords = $this->$keyword_provider_method();
3885  $this->_tmp['keywords'][$bodycopy_code] = $keywords;
3886  }
3887  }
3888 
3889  $vars['keywords'] = array_merge($vars['keywords'], $keywords);
3890 
3891  }
3892 
3893  }//end onRequestKeywords()
3894 
3895 
3903  {
3904  $questions = $this->getQuestionAssets();
3905  $sections = $this->getSections();
3906 
3907  $keywords['form_contents'] = translate('cms_form_contents');
3908  $keywords['current_page_contents'] = translate('cms_current_page_contents');
3909  $keywords['form_errors'] = translate('cms_form_errors');
3910  $keywords['form_errors_message'] = translate('cms_form_errors_message');
3911 
3912  foreach ($questions as $question) {
3913  $keywords['question_name_' .$question->id] = translate('cms_form_question_name', ellipsisize($question->attr('name'), 30)).' ('.$question->id.')';
3914  $keywords += $this->_getCommonQuestionKeywords($question, $this);
3915  }
3916 
3917  foreach ($sections as $section) {
3918  $keywords['section_title_'.$section->id] = 'Section Name: '.$section->attr('name').' ('.$section->id.')';
3919  $keywords['section_contents_'.$section->id] = 'Section Contents: '.$section->attr('name').' ('.$section->id.')';
3920  $keywords += $section->getDisplayKeywords(FALSE);
3921  }
3922 
3923  // These are best at the end
3924  $keywords['reset_button'] = translate('cms_form_reset_button');
3925  $keywords['submit_button'] = translate('cms_form_submit_button');
3926 
3927  if ($this->getTotalPages() > 1) {
3928  $keywords['previous_page_button'] = translate('cms_form_previous_page_button');
3929  }
3930 
3931  $keywords += $this->_getCaptchaDisplayKeywords();
3932 
3933  if ($this->attr('use_mollom')) {
3934  $keywords += $this->_getMollomCaptchaDisplayKeywords();
3935  }
3936 
3937  return $keywords;
3938 
3939  }//end _getPageContentsBodycopyKeywords()
3940 
3941 
3949  {
3950  $keywords['form_submissions_limit'] = translate('cms_form_submissions_limit');
3951  $keywords['form_submissions_count'] = translate('cms_form_submissions_count');
3952  $keywords['form_submissions_left'] = translate('cms_form_submissions_left');
3953 
3954  return $keywords;
3955 
3956  }//end _getSubmissionLimitReachedBodycopyKeywords()
3957 
3958 
3966  {
3967  $questions = $this->getQuestions();
3968  $sections = $this->getSections();
3969 
3970  $keywords['form_summary'] = translate('cms_form_submission_summary');
3971  $keywords['form_summary_html'] = translate('cms_form_submission_summary').' (html)';
3972  $keywords['form_info'] = translate('cms_form_submission_info');
3973  $keywords['form_info_html'] = translate('cms_form_submission_info').' (html)';
3974  $keywords['form_submission_id'] = translate('cms_form_submission_id');
3975  $keywords['form_submission_ip_address'] = translate('cms_form_submission_ip_address');
3976  $keywords['form_submission_time'] = translate('cms_form_submission_time_format', translate('date_format_default'));
3977 
3978  foreach (get_date_format_names() as $date_name => $date_format) {
3979  $keywords['form_submission_time_'.$date_name] = translate('cms_form_submission_time_format', translate('date_format_'.$date_name));
3980  }
3981 
3982  foreach ($questions as $q_id => $question) {
3983  $q_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->id.':q'.$q_id);
3984  $q_name = $q_asset->attr('name');
3985  $keywords['response_'.$this->id.'_q'.$q_id] = translate('cms_form_question_response', ellipsisize($q_name,30));
3986  $keywords['question_name_'.$this->id.'_q'.$q_id] = translate('cms_form_question_name', ellipsisize($q_name,30));
3987  $keywords['question_note_'.$this->id.'_q'.$q_id] = translate('cms_form_question_note', ellipsisize($q_name,30));
3988  }
3989 
3990  foreach ($sections as $section) {
3991  $keywords['section_title_'.$section->id] = translate('cms_form_section_title', $section->attr('name'));
3992  $keywords += $section->getResponseKeywords();
3993  }
3994 
3995  return $keywords;
3996 
3997  }//end _getThankYouBodycopyKeywords()
3998 
3999 
4006  {
4007 
4008  $questions = $this->getQuestionAssets();
4009  $sections = $this->getSections();
4010 
4011  $keywords = Array();
4012 
4013  $keywords['form_summary'] = translate('cms_form_submission_summary');
4014  $keywords['form_info'] = translate('cms_form_submission_info');
4015 
4016  foreach ($questions as $question) {
4017  $keywords += $this->_getCommonQuestionKeywords($question, $this);
4018  }
4019 
4020  foreach ($sections as $section) {
4021  $keywords['section_title_'.$section->id] = 'Section Name: '.$section->attr('name').' ('.$section->id.')';
4022  $keywords += $section->getResponseKeywords();
4023  }
4024 
4025  // These are best at the end
4026  $keywords['reset_button'] = translate('cms_form_reset_button');
4027  $keywords['submit_button'] = translate('cms_form_submit_button');
4028  $keywords['previous_page_button'] = translate('cms_form_previous_page_button');
4029 
4030  $keywords += $this->_getCaptchaDisplayKeywords();
4031 
4032  return $keywords;
4033 
4034  }//end _getConfirmationPageBodycopyKeywords()
4035 
4036 
4045  protected function _getCommonQuestionKeywords(Form_Question $question, Asset $parent)
4046  {
4047  // Since we cannot assert a mixed asset argument in the formal parameter list
4048  // (further than "instanceof Asset"), double-check that we are being sent
4049  // a Form or Form Section
4050  assert_true(($parent instanceof Form) || ($parent instanceof Form_Section), 'Argument 2 must be a Form or Form Section asset');
4051 
4052  $keywords = Array();
4053 
4054  // Replace the shadow asset colon with an underscore
4055  $keyword_assetid = str_replace(':', '_', $question->id);
4056 
4057  if ($parent instanceof Form_Section) {
4058  $keywords['question_field_' .$keyword_assetid] = translate('cms_form_section_q_field', $parent->attr('name'), ellipsisize($question->attr('name'), 30)).' ('.$question->id.')';
4059  $keywords['question_name_' .$keyword_assetid] = translate('cms_form_section_q_name', $parent->attr('name'), ellipsisize($question->attr('name'), 30)).' ('.$question->id.')';
4060  $keywords['question_id_' .$keyword_assetid] = translate('cms_form_section_q_id', $parent->attr('name'), ellipsisize($question->attr('name'), 30)).' ('.$question->id.')';
4061  $keywords['question_note_' .$keyword_assetid] = translate('cms_form_section_q_note', $parent->attr('name'), ellipsisize($question->attr('name'), 30)).' ('.$question->id.')';
4062  $keywords['question_label_'.$keyword_assetid] = translate('cms_form_section_q_label', $parent->attr('name'), ellipsisize($question->attr('name'), 30)).' ('.$question->id.')';
4063  } else {
4064  $keywords['question_field_' .$keyword_assetid] = translate('cms_form_question_field', ellipsisize($question->attr('name'), 30)).' ('.$question->id.')';
4065  $keywords['question_name_' .$keyword_assetid] = translate('cms_form_question_name', ellipsisize($question->attr('name'), 30)).' ('.$question->id.')';
4066  $keywords['question_id_' .$keyword_assetid] = translate('cms_form_question_id', ellipsisize($question->attr('name'), 30)).' ('.$question->id.')';
4067  $keywords['question_note_' .$keyword_assetid] = translate('cms_form_question_note', ellipsisize($question->attr('name'), 30)).' ('.$question->id.')';
4068  $keywords['question_label_'.$keyword_assetid] = translate('cms_form_question_label', ellipsisize($question->attr('name'), 30)).' ('.$question->id.')';
4069  }
4070 
4071  return $keywords;
4072 
4073  }//end _getCommonQuestionKeywords()
4074 
4075 
4083  protected function _getCommonSectionKeywords(Form_Section $section)
4084  {
4085  $keywords = Array();
4086 
4087  $keywords['section_title_'.$section->id] = translate('cms_form_section_title', $section->attr('name')).' ('.$section->id.')';
4088 
4089  return $keywords;
4090 
4091  }//end _getCommonSectionKeywords()
4092 
4093 
4105  protected function _getQuestionResponseKeywords(Form_Question $question, Asset $parent)
4106  {
4107  // Since we cannot assert a mixed asset argument in the formal parameter list
4108  // (further than "instanceof Asset"), double-check that we are being sent
4109  // a Form or Form Section
4110  assert_true(($parent instanceof Form) || ($parent instanceof Form_Section), 'Argument 2 must be a Form or Form Section asset');
4111 
4112  $keywords = Array();
4113 
4114  // Replace the shadow asset colon with an underscore
4115  $keyword_assetid = str_replace(':', '_', $question->id);
4116 
4117  if ($parent instanceof Form_Section) {
4118  $keywords['response_' .$keyword_assetid] = translate('cms_form_section_response', $parent->attr('name'), ellipsisize($question->attr('name'), 30)).' ('.$question->id.')';
4119  } else {
4120  $keywords['response_' .$keyword_assetid] = translate('cms_form_question_response', ellipsisize($question->attr('name'), 30)).' ('.$question->id.')';
4121  }
4122 
4123  return $keywords;
4124 
4125  }//end _getQuestionResponseKeywords()
4126 
4127 
4133  protected function _getCaptchaDisplayKeywords()
4134  {
4135  $keywords = Array();
4136 
4137  $keywords['form_captcha'] = translate('cms_form_captcha');
4138  $keywords['form_captcha_image'] = translate('cms_form_captcha_image');
4139  $keywords['form_captcha_field'] = translate('cms_form_captcha_field');
4140  $keywords['form_captcha_link'] = translate('cms_form_captcha_link');
4141 
4142  return $keywords;
4143 
4144  }//end _getCaptchaDisplayKeywords()
4145 
4146 
4147 
4154  {
4155  $keywords = Array();
4156 
4157  $keywords['form_mollom_captcha'] = translate('cms_form_mollom_captcha');
4158  $keywords['form_mollom_captcha_image'] = translate('cms_form_mollom_captcha_image');
4159  $keywords['form_mollom_captcha_audio'] = translate('cms_form_mollom_captcha_audio');
4160  $keywords['form_mollom_captcha_field'] = translate('cms_form_mollom_captcha_field');
4161  $keywords['form_mollom_captcha_regen_link'] = translate('cms_form_mollom_captcha_link');
4162 
4163  return $keywords;
4164 
4165  }//end _getMollomCaptchaDisplayKeywords()
4166 
4167 
4168 
4169 //-- KEYWORD REPLACEMENTS --//
4170 
4171 
4181  public function getDisplayKeywordReplacement($keyword)
4182  {
4183  $replacement = NULL;
4184  $keyword = parse_keyword($keyword, $modifiers);
4185  $is_question_keyword = preg_match('/^question\_(.*)\_(\d+[\_\:]q\d+)$/', $keyword, $question_matches);
4186  $is_response_keyword = preg_match('/^response\_(\d+[\_\:]q\d+)(\_raw)?$/', $keyword, $response_matches);
4187  $is_section_keyword = preg_match('/^section\_(.*)\_(\d+)$/', $keyword, $section_matches);
4188  $is_form_keyword = preg_match('/^form\_(.*)$/', $keyword, $form_matches);
4189 
4190  if ($is_question_keyword) {
4191  // Sub keyword in $matches[1] - eg. 'field', 'id', 'note', 'label'
4192  // Question assetid in $matches[2] - but need to replace underscore
4193  // with colon
4194  list(, $sub_keyword, $assetid) = $question_matches;
4195  $assetid = str_replace('_', ':', $assetid);
4196 
4197  $question_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
4198 
4199  if (!empty($question_asset)) {
4200 
4201  switch ($sub_keyword) {
4202  case 'field' :
4203  $replacement = $question_asset->getHtmlField();
4204  break;
4205 
4206  case 'name' :
4207  $replacement = $question_asset->attr('name');
4208  break;
4209 
4210  case 'id' :
4211  $replacement = 'q'.str_replace(':', '_', $assetid);
4212  break;
4213 
4214  case 'note' :
4215  $replacement = $question_asset->attr('note');
4216  break;
4217 
4218  case 'label' :
4219  $replacement = $question_asset->getHtmlLabel();
4220  break;
4221 
4222  }//end switch
4223  }
4224 
4225  } else if ($is_section_keyword) {
4226  // Sub keyword in $matches[1] - eg. 'title' or 'contents'
4227  // Section assetid in $matches[2]
4228  list(, $sub_keyword, $assetid) = $section_matches;
4229 
4230  $section_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
4231 
4232  switch ($sub_keyword) {
4233  case 'title' :
4234  $replacement = $section_asset->attr('name');
4235  break;
4236 
4237  case 'contents' :
4238  ob_start();
4239  include_once($section_asset->data_path.'/content_file.php');
4240  $replacement = ob_get_clean();
4241  break;
4242 
4243  }//end switch
4244 
4245  } else if ($is_response_keyword) {
4246  $raw_response = (count($response_matches) == 3);
4247  list(, $assetid) = $response_matches;
4248  $assetid = str_replace('_', ':', $assetid);
4249 
4250  $question_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
4251  if (!empty($question_asset)) {
4252  if ($raw_response) {
4253  $replacement = htmlspecialchars($question_asset->getValue(), ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET);
4254  } else {
4255  $replacement = htmlspecialchars($question_asset->getSummary(), ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET);
4256  }
4257  }
4258 
4259  } else if ($is_form_keyword) {
4260  list(, $sub_keyword) = $form_matches;
4261  switch ($sub_keyword) {
4262  case 'mollom_captcha':
4263  $replacement = Mollom::getMollomCaptchaKeywordReplacement($this->id);
4264  break;
4265  case 'mollom_captcha_field':
4266  $replacement = Mollom::getMollomCaptchaFieldKeywordReplacement($this->id);
4267  break;
4268 
4269  case 'mollom_captcha_image':
4270  $replacement = Mollom::getMollomCaptchaImageKeywordReplacement($this->id);
4271  break;
4272 
4273  case 'mollom_captcha_audio':
4274  $replacement = Mollom::getMollomCaptchaAudioKeywordReplacement($this->id);
4275  break;
4276 
4277  case 'mollom_captcha_regen_link':
4278  $regen_link_text = $this->attr('mollom_captcha_regen_link_text');
4279  if (empty($regen_link_text)) {
4280  $regen_link_text = translate('cms_form_mollom_regen_link_text');
4281  }
4282  $replacement = Mollom::getMollomCaptchaRegenLinkKeywordReplacement($this->id, $regen_link_text);
4283  break;
4284 
4285  case 'submission_id':
4286  $submission = $this->getSubmissionAsset();
4287  if ($submission) {
4288  $replacement = $submission->id;
4289  }
4290  break;
4291 
4292  case 'submissions_limit':
4293  $replacement = $this->attr('submission_limit');
4294  break;
4295 
4296  case 'submissions_count':
4297  if ($this->attr('log_submissions')) {
4298  $replacement = $this->getSubmissionCount();
4299  } else {
4300  $replacement = '';
4301  }
4302  break;
4303 
4304  case 'submissions_left':
4305  if ($this->attr('log_submissions')) {
4306  $sub_limit = $this->attr('submission_limit');
4307  $sub_done = $this->getSubmissionCount();
4308  $replacement = $sub_limit - $sub_done;
4309  } else {
4310  $replacement = '';
4311  }
4312  break;
4313  }//end switch
4314 
4315  }
4316  if (!is_null($replacement)) {
4317  $replace_keywords = Array(
4318  'assetid' => $this->id,
4319  'call_fns' => Array ('getDisplayKeywordReplacement', 'getKeywordReplacement'),
4320  );
4321  apply_keyword_modifiers($replacement, $modifiers, $replace_keywords);
4322  }
4323 
4324  return $replacement;
4325 
4326  }//end getDisplayKeywordReplacement()
4327 
4328 
4336  {
4337  if ($this->attr('multi_page')) {
4338  $current_page = $this->getCurrentPageNumber();
4339  return $this->getPageContents($current_page);
4340  } else {
4341  return $this->getFormContentsKeywordReplacement();
4342  }
4343 
4344  }//end getCurrentPageContentsKeywordReplacement()
4345 
4346 
4356  function getCurrentPageNameKeywordReplacement($current_page=NULL)
4357  {
4358  if (is_null($current_page)) {
4359  $current_page = $this->getCurrentPageNumber();
4360  }
4361  $replacement = NULL;
4362 
4363  if (($current_page == $this->getTotalPages()) && $this->attr('use_confirmation_page')) {
4364  $replacement = $this->attr('confirmation_page_name');
4365  } else {
4366  $question_count = $this->getQuestionCount();
4367  if ($question_count > 0) $current_page--;
4368 
4369  if ($current_page == 0) {
4370  $replacement = $this->attr('unattached_page_name');
4371  } else {
4372  $sections = $this->getSectionLinks();
4373  $current_section_link = reset(array_slice($sections, $current_page - 1, 1));
4374  $current_section_id = $current_section_link['minorid'];
4375  $current_section_info = $GLOBALS['SQ_SYSTEM']->am->getAssetInfo($current_section_id);
4376 
4377  $replacement = $current_section_info[$current_section_id]['name'];
4378  }
4379  }
4380 
4381  return $replacement;
4382 
4383  }//end getCurrentPageContentsKeywordReplacement()
4384 
4385 
4393  {
4394  return $this->getCurrentPageNumber();
4395 
4396  }//end getPageNumberKeywordReplacement()
4397 
4398 
4406  {
4407  return $this->getTotalPages();
4408 
4409  }//end getTotalPagesKeywordReplacement()
4410 
4411 
4419  {
4420  ob_start();
4421  if ($this->attr('use_confirmation_page') && ($this->getCurrentPageNumber() == $this->getTotalPages())) {
4422  echo $this->getConfirmationPageContents();
4423  } else {
4424  $this->includeContentFile('Standard');
4425  }
4426  return ob_get_clean();
4427 
4428  }//end getFormContentsKeywordReplacement()
4429 
4430 
4438  {
4439  ob_start();
4440  $this->includeContentFile('Questions');
4441  return ob_get_clean();
4442 
4443  }//end getFormContentsKeywordReplacement()
4444 
4452  {
4453  if (!$this->attr('captcha_ttf') == '') {
4454  $font = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->attr('captcha_ttf'));
4455  $file_path = $font->data_path.'/'.$font->attr('name');
4456  } else {
4457  $file_path = '';
4458  }
4459 
4460  ob_start();
4461  security_key(
4462  $this->attr('captcha_length'),
4463  $this->attr('captcha_length'),
4464  $this->attr('captcha_zoom'),
4465  FALSE,
4466  trim($this->attr('captcha_bgcolour'), '#'),
4467  trim($this->attr('captcha_textcolour'), '#'),
4468  trim($this->attr('captcha_bordercolour'), '#'),
4469  $this->attr('captcha_use_colours'),
4470  $this->attr('captcha_usefont'),
4471  $file_path,
4472  $this->attr('captcha_font_size'),
4473  $this->attr('captcha_min_angle'),
4474  $this->attr('captcha_max_angle'),
4475  $this->attr('captcha_x_start'),
4476  $this->attr('captcha_min_distance'),
4477  $this->attr('captcha_max_distance'),
4478  $this->attr('captcha_width'),
4479  $this->attr('captcha_height'),
4480  $this->attr('captcha_arc'),
4481  trim($this->attr('captcha_arc_colour'), '#'),
4482  $this->attr('captcha_include_uppercase'),
4483  $this->attr('captcha_include_numbers'),
4484  $this->attr('provide_accessible_captcha')
4485  );
4486  return ob_get_clean();
4487 
4488  }//end getFormCaptchaKeywordReplacement()
4489 
4490 
4498  {
4499  if (!$this->attr('captcha_ttf') == '') {
4500  $font = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->attr('captcha_ttf'));
4501  $file_path = $font->data_path.'/'.$font->attr('name');
4502  } else {
4503  $file_path = '';
4504  }
4505 
4506  ob_start();
4507  security_key_image_box(
4508  $this->attr('captcha_length'),
4509  $this->attr('captcha_zoom'),
4510  trim($this->attr('captcha_bgcolour'), '#'),
4511  trim($this->attr('captcha_textcolour'), '#'),
4512  trim($this->attr('captcha_bordercolour'), '#'),
4513  $this->attr('captcha_use_colours'),
4514  $this->attr('captcha_usefont'),
4515  $file_path,
4516  $this->attr('captcha_font_size'),
4517  $this->attr('captcha_min_angle'),
4518  $this->attr('captcha_max_angle'),
4519  $this->attr('captcha_x_start'),
4520  $this->attr('captcha_min_distance'),
4521  $this->attr('captcha_max_distance'),
4522  $this->attr('captcha_width'),
4523  $this->attr('captcha_height'),
4524  $this->attr('captcha_arc'),
4525  trim($this->attr('captcha_arc_colour'), '#'),
4526  $this->attr('captcha_include_uppercase'),
4527  $this->attr('captcha_include_numbers'),
4528  $this->attr('provide_accessible_captcha')
4529  );
4530  return ob_get_clean();
4531 
4532  }//end getFormCaptchaImageKeywordReplacement()
4533 
4534 
4542  {
4543  ob_start();
4544  security_key_field($this->attr('captcha_length'));
4545  return ob_get_clean();
4546 
4547  }//end getFormCaptchaFieldKeywordReplacement()
4548 
4549 
4557  {
4558  ob_start();
4559  security_key_image_link($this->attr('captcha_link_text'));
4560  return ob_get_clean();
4561 
4562  }//end getFormCaptchaLinkKeywordReplacement()
4563 
4564 
4572  {
4573  if (empty($this->submission_errors)) return '';
4574 
4575  ob_start();
4576  $this->printErrors();
4577  return ob_get_clean();
4578 
4579  }//end getFormErrorsKeywordReplacement()
4580 
4581 
4589  {
4590  if (empty($this->submission_errors)) return '';
4591 
4592  return $this->attr('submission_errors_msg');
4593 
4594  }//end getFormErrorsMessageKeywordReplacement()
4595 
4596 
4604  {
4605  $current_page = $this->getCurrentPageNumber();
4606  $total_pages = $this->getTotalPages();
4607 
4608  if ($current_page >= $total_pages) {
4609  $button_name = ($this->attr('submit_button')) ? $this->attr('submit_button') : translate('submit');
4610  } else {
4611  $button_name = ($this->attr('next_button')) ? $this->attr('next_button') : translate('next');
4612  }
4613 
4614  ob_start();
4615  submit_button($this->getPrefix().'_submit', $button_name, '', $this->attr('submit_button_extras'));
4616  return ob_get_clean();
4617 
4618  }//end getSubmitButtonKeywordReplacement()
4619 
4620 
4628  {
4629  $button_name = ($this->attr('save_button')) ? $this->attr('save_button') : translate('save');
4630 
4631  ob_start();
4632  submit_button($this->getPrefix().'_save', $button_name);
4633  return ob_get_clean();
4634 
4635  }//end getSaveButtonKeywordReplacement()
4636 
4637 
4645  {
4646  $prefix = $this->getPrefix();
4647  if (isset($_POST[$prefix.'_save'])) {
4648  $message = ($this->attr('save_confirmation_message')) ? $this->attr('save_confirmation_message') : translate('cms_form_save_confirmation');
4649  } else {
4650  $message = '';
4651  }
4652  return $message;
4653 
4654  }//end getSaveConfirmationKeywordReplacement()
4655 
4656 
4666  {
4667  $bodycopy = $this->getBodycopy('Exit Page Contents');
4668 
4669  if ($bodycopy) {
4670  $button_name = ($this->attr('save_and_exit_button')) ? $this->attr('save_and_exit_button') : translate('save_and_exit');
4671 
4672  ob_start();
4673  submit_button($this->getPrefix().'_save_and_exit', $button_name);
4674  $replacement = ob_get_clean();
4675  } else {
4676  $replacement = NULL;
4677  }
4678 
4679  return $replacement;
4680 
4681  }//end getSaveAndExitButtonKeywordReplacement()
4682 
4683 
4691  {
4692  $button_name = ($this->attr('reset_button')) ? $this->attr('reset_button') : translate('reset');
4693 
4694  ob_start();
4695  reset_button($this->getPrefix().'_reset', $button_name, '', $this->attr('reset_button_extras'));
4696  return ob_get_clean();
4697 
4698  }//end getResetButtonKeywordReplacement()
4699 
4700 
4708  {
4709  // Only display the previous page keyword if we are beyond the first page
4710  $current_page = $this->getCurrentPageNumber();
4711 
4712  if ($current_page > 1) {
4713  $button_name = ($this->attr('previous_button')) ? $this->attr('previous_button') : translate('previous_page');
4714  ob_start();
4715  submit_button($this->getPrefix().'_previous_page', $button_name);
4716  $replacement = ob_get_clean();
4717  } else {
4718  $replacement = '';
4719  }
4720 
4721  return $replacement;
4722 
4723  }//end getPreviousPageButtonKeywordReplacement()
4724 
4725 
4732  {
4733  ob_start();
4734 
4735  $submission_asset = $this->getSubmissionAsset();
4736  $current_page = $this->getCurrentPageNumber();
4737  $latest_page = $this->getLatestPageNumber();
4738 
4739  $total_pages = $this->getTotalPages();
4740 
4741  $page_list_format = $this->attr('page_list_format');
4742  $keywords = extract_keywords($page_list_format);
4743 
4744  ?>
4745  <ul class="sq-form-page-list">
4746  <?php
4747  for ($page_number = 1; $page_number <= $total_pages; $page_number++) {
4748  // Get this page's very own keyword replacements
4749  $page_entry = $page_list_format;
4750  $replacements = Array();
4751 
4752  $replacements['page_number'] = htmlspecialchars($page_number, ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET);
4753  $replacements['total_pages'] = htmlspecialchars($total_pages, ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET);
4754  $replacements['page_name'] = htmlspecialchars($this->getCurrentPageNameKeywordReplacement($page_number), ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET);
4755 
4756  replace_keywords($page_entry, $replacements);
4757 
4758  $page_has_link = ($page_number <= $latest_page) && ($page_number != $current_page);
4759 
4760  $style_classes = Array();
4761  if ($page_number == $current_page) {
4762  $style_classes[] = 'sq-form-current-page-number';
4763  } else if ($page_number > $latest_page) {
4764  $style_classes[] = 'sq-form-unseen-page-number';
4765  } else if ($page_number > $current_page) {
4766  $style_classes[] = 'sq-form-later-page-number';
4767  } else {
4768  $style_classes[] = 'sq-form-prior-page-number';
4769  }
4770 
4771  if ($page_number == $latest_page) {
4772  $style_classes[] = 'sq-form-last-seen-page-number';
4773  }
4774  ?>
4775  <li<?php echo !empty($style_classes) ? ' class="'.implode(' ', $style_classes).'"' : '' ?>><?php
4776  if ($page_has_link) {
4777  ?><a href="<?php echo $this->getURL() ?>?SQ_FORM_<?php echo $this->id ?>_PAGE=<?php echo $page_number ?><?php
4778  if ($submission_asset) {
4779  echo '&SQ_FORM_'.$this->id.'_SUBMISSION='.$submission_asset->id;
4780  }
4781  ?>"><?php
4782  }
4783 
4784  echo $page_entry;
4785 
4786  if ($page_has_link) {
4787  ?></a><?php
4788  }
4789  ?></li>
4790  <?php }//end for
4791  ?></ul>
4792  <?php
4793  return ob_get_clean();
4794 
4795  }//end getPageListKeywordReplacement()
4796 
4797 
4808  public function _getThankYouKeywordReplacement($keyword)
4809  {
4810  $replacement = '%'.$keyword.'%';
4811  $prefix = $this->getPrefix();
4812  $keyword = parse_keyword($keyword, $modifiers);
4813 
4814  // Easier to use if-else if rather than switch, because some of these
4815  // will be regex matches
4816 
4817  if ($keyword === 'form_info_html') {
4818  // Form info keyword - HTML email
4819  // %form_info_html%
4820 
4821  $replacement = $this->getFormInfo(TRUE);
4822 
4823  } else if ($keyword === 'form_info_text') {
4824  // Form info keyword - text email
4825  // %form_info_text%
4826 
4827  $replacement = $this->getFormInfo(FALSE);
4828 
4829  } else if ($keyword === 'form_summary_html') {
4830  // Form summary keyword - HTML email
4831  // %form_summary_html%
4832 
4833  ob_start();
4834  $this->printSummary(TRUE);
4835  $replacement = ob_get_contents();
4836  ob_end_clean();
4837 
4838  } else if ($keyword === 'form_summary_semantic') {
4839  // Form semantic summary - intended for HTML ONLY
4840 
4841  ob_start();
4842  $this->printSemanticSummary();
4843  $replacement = ob_get_contents();
4844  ob_end_clean();
4845 
4846  } else if ($keyword === 'form_summary_text') {
4847  // Form summary keyword
4848  // %form_summary_text%
4849 
4850  ob_start();
4851  $this->printSummary(FALSE);
4852  $replacement = ob_get_contents();
4853  ob_end_clean();
4854 
4855  } else if ($keyword === 'form_submission_id') {
4856  // Asset ID of the form submission
4857  // %form_submission_id%
4858 
4859  if (!is_null($this->submission_asset)) {
4860  $replacement = $this->submission_asset->id;
4861  }
4862 
4863  } else if (preg_match('|^form_submission_time(_?.*)|', $keyword, $matches)) {
4864  // Submission time - can use date formats - if not recognised as
4865  // a valid shorthand date format, it's assumed to be a date() format
4866  // (just like date keywords elsewhere)
4867  // %form_submission_time_rfc2822%
4868 
4869  if (empty($matches[1])) {
4870  $date_format = 'Y-m-d H:i:s';
4871  } else {
4872  $date_format = substr($matches[1], 1);
4873  }
4874 
4875  if (!is_null($this->submission_asset)) {
4876  $date_formats_list = get_date_formats();
4877  if (array_key_exists($date_format, $date_formats_list)) {
4878  $replacement = format_date($this->submission_asset->created, $date_format);
4879  } else {
4880  $replacement = date($date_format, $this->submission_asset->created);
4881  }
4882  }
4883 
4884  } else if ($keyword === 'form_submission_ip_address') {
4885  // IP address of the submitting user
4886  // %form_submission_ip_address%
4887 
4888  // TODO: Should we handle proxy-forwarded IP addresses
4889  // (X-Forwarded-For header)?
4890  $ip_address = $_SERVER['REMOTE_ADDR'];
4891  $replacement = $ip_address;
4892 
4893  } else if (preg_match('|^form_asset_(.*)|', $keyword, $matches)) {
4894  // form PAGE asset keyword
4895  // eg. %form_asset_assetid%
4896 
4897  $parent_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'page_custom_form', FALSE, NULL, 'minor');
4898  $parent_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($parent_link['majorid']);
4899 
4900  $form_keyword = 'asset_'.$matches[1];
4901  $asset_replacement = $parent_asset->getKeywordReplacement($form_keyword);
4902  if ($asset_replacement !== "%$form_keyword%") {
4903  $replacement = $asset_replacement;
4904  }
4905 
4906  } else {
4907  // questions that depend on knowing what questions and sections we have
4908  // We will cache them though, so we don't load them up all the time
4909 
4910  if (empty($this->_tmp['keyword_cache'])) {
4911  $this->_tmp['keyword_cache']['questions'] = $this->getQuestions();
4912  $this->_tmp['keyword_cache']['sections' ] = $this->getSections();
4913  }
4914 
4915  $questions =& $this->_tmp['keyword_cache']['questions'];
4916  $sections =& $this->_tmp['keyword_cache']['sections'];
4917 
4918  if (preg_match('|^section_title_(.*)|', $keyword, $matches)) {
4919  // Section title keyword
4920  // eg. %section_title_123%
4921 
4922  $section_assetid = $matches[1];
4923  $section_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($section_assetid, 'form_section');
4924  if (!empty($section_asset)) {
4925  $replacement = $section_asset->attr('name');
4926  }
4927 
4928  } else if (preg_match('|^question_([^_]*)_([^_]*_)?q(.*)|', $keyword, $matches)) {
4929  // Question name/note keywords, eg.
4930  // %question_note_123_q4% ... note for q.4 from form/section
4931  // with ID 123
4932  // %question_name_q4% ....... name of q.4 from the form
4933 
4934  $question_keyword = $matches[1];
4935  if (empty($matches[2])) {
4936  // One asset ID
4937  $section_assetid = $this->id;
4938  $question_assetid = $matches[3];
4939  } else {
4940  $section_assetid = str_replace('_', '', $matches[2]);
4941  $question_assetid = $matches[3];
4942  }
4943 
4944  $question_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($section_assetid.':q'.$question_assetid);
4945  if (!empty($question_asset)) {
4946  switch ($question_keyword) {
4947  case 'name':
4948  $replacement = $question_asset->attr('name');
4949  break;
4950 
4951  case 'note':
4952  $replacement = $question_asset->attr('note');
4953  break;
4954  }
4955  }
4956 
4957  } else if (preg_match('|^response_([0-9]*_)?q([0-9]*)?(_raw)?|', $keyword, $matches)) {
4958  // Question response keywords, eg.
4959  // %response_123_q4% ... question 4 from form/section ID 123
4960  // %response_q4% ....... question 4 from the form
4961 
4962  if (empty($matches[1])) {
4963  // One asset ID
4964  $section_assetid = $this->id;
4965  $question_assetid = $matches[2];
4966  } else {
4967  $section_assetid = str_replace('_', '', $matches[1]);
4968  $question_assetid = $matches[2];
4969  }
4970 
4971  $raw = (array_get_index($matches, 3, '') == '_raw');
4972 
4973  $question_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($section_assetid.':q'.$question_assetid);
4974  if (!empty($question_asset)) {
4975  if ($raw) {
4976  $value = $question_asset->getValue();
4977  if (is_string($question_asset->getValue())) {
4978  $replacement = htmlspecialchars($question_asset->getValue(), ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET);
4979  } else {
4980  $replacement = '';
4981  }
4982  } else {
4983  $replacement = htmlspecialchars($question_asset->getSummary(), ENT_NOQUOTES, SQ_CONF_DEFAULT_CHARACTER_SET);
4984  }
4985  }
4986 
4987  }
4988 
4989  }//end else - keyword list
4990 
4991  // if replacement not found yet, see if it is not context specific
4992  if ($replacement == '%'.$keyword.'%') {
4993  $replacement = $this->getKeywordReplacement($keyword);
4994  }
4995 
4996  $replace_keywords = Array(
4997  'assetid' => $this->id,
4998  'call_fns' => Array ('_getThankYouKeywordReplacement', 'getKeywordReplacement'),
4999  );
5000  apply_keyword_modifiers($replacement, $modifiers, $replace_keywords);
5001 
5002  return $replacement;
5003 
5004  }//end _getThankYouKeywordReplacement()
5005 
5006 
5016  function performFormSectionActions($page_number)
5017  {
5018  $success = TRUE;
5019  if ($this->attr('multi_page')) {
5020 
5021  $section_links = $this->getSectionLinks();
5022 
5023  // Get current section
5024  $section_link = reset(array_slice($section_links, $page_number - 1, 1));
5025  if (isset($section_link['minorid'])) {
5026  $section = $GLOBALS['SQ_SYSTEM']->am->getAsset($section_link['minorid']);
5027 
5028  $actions = $section->attr('actions');
5029  foreach ($actions as $action) {
5030  $action_type = $action['type_code'];
5031  $GLOBALS['SQ_SYSTEM']->am->includeAsset($action_type);
5032 
5033  if ($action['active'] && call_user_func(Array($action_type, 'isValid'), $section, $action['settings'])) {
5034  $success = call_user_func(Array($action_type, 'execute'), $section, $this, $action['settings']);
5035  if (!$success) {
5036  break;
5037  }
5038  }//end if
5039  }//end foreach
5040 
5041  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($section);
5042  }
5043  }
5044 
5045  return $success;
5046 
5047  }//end performFormSectionActions()
5048 
5049 
5059  {
5060  if ($this->attr('multi_page')) {
5061 
5062  $section_links = $this->getSectionLinks();
5063  foreach($section_links as $section_link) {
5064  if (!isset($section_link['minorid'])) continue;
5065  $section = $GLOBALS['SQ_SYSTEM']->am->getAsset($section_link['minorid']);
5066  $actions = $section->attr('actions');
5067 
5068  foreach ($actions as $action) {
5069  $action_type = $action['type_code'];
5070  $GLOBALS['SQ_SYSTEM']->am->includeAsset($action_type);
5071  call_user_func(Array($action_type, 'finalise'), $this);
5072  }//end foreach
5073 
5074  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($section);
5075  }//end foreach
5076  }//end if
5077 
5078  }//end _finaliseStepsActions()
5079 
5080 
5088  {
5089  if (!$this->attr('log_submissions')) return FALSE;
5090 
5091  $submission_folder = $this->getSubmissionsFolder();
5092  if (is_null($submission_folder)) return FALSE;
5093 
5094  $submissions = $GLOBALS['SQ_SYSTEM']->am->getChildren($submission_folder->id, 'form_submission', TRUE, NULL, NULL, NULL, TRUE, 1, 1);
5095 
5096  return count($submissions);
5097 
5098  }//end getSubmissionCount()
5099 
5100 
5111  function printSubmissionLimitReachedBodycopy($replacements=Array())
5112  {
5113  $bodycopy = $this->getBodycopy('Submission Limit Reached');
5114 
5115  // check to see if the bodycopy attached to this form has any content
5116  if ($bodycopy && !$bodycopy->isEmpty()) {
5117  $keyword_list = $bodycopy->getKeywords();
5118 
5119  if (!empty($keyword_list)) {
5120  foreach ($keyword_list as $keyword) {
5121  // Question and section-related keywords are found here
5122  $replacement = $this->getDisplayKeywordReplacement($keyword);
5123 
5124  if (is_null($replacement)) {
5125  // If not picked up by first function, this will pick up
5126  // replacements placed in separate functions - CAPTCHA,
5127  // errors, for instance
5128  $replacement = $this->getKeywordReplacement($keyword);
5129  }
5130 
5131  if (!is_null($replacement)) {
5132  // We have something from one of the above
5133  $replacements[$keyword] = $replacement;
5134  }
5135  }
5136  }
5137 
5138  $bodycopy->setKeywordReplacements($replacements);
5139  $bodycopy->printBody();
5140  }
5141 
5142  }//end printSubmissionLimitReachedBodycopy()
5143 
5144 
5145 }//end class
5146 ?>