b6b573f7a3c44e4aed1d8f21636749cd122ad1bd
[civicrm-core.git] / CRM / Core / Form.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.4 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 * This is our base form. It is part of the Form/Controller/StateMachine
30 * trifecta. Each form is associated with a specific state in the state
31 * machine. Each form can also operate in various modes
32 *
33 * @package CRM
34 * @copyright CiviCRM LLC (c) 2004-2013
35 * $Id$
36 *
37 */
38
39 require_once 'HTML/QuickForm/Page.php';
40 class CRM_Core_Form extends HTML_QuickForm_Page {
41
42 /**
43 * The state object that this form belongs to
44 * @var object
45 */
46 protected $_state;
47
48 /**
49 * The name of this form
50 * @var string
51 */
52 protected $_name;
53
54 /**
55 * The title of this form
56 * @var string
57 */
58 protected $_title = NULL;
59
60 /**
61 * The options passed into this form
62 * @var mixed
63 */
64 protected $_options = NULL;
65
66 /**
67 * The mode of operation for this form
68 * @var int
69 */
70 protected $_action;
71
72 /**
73 * the renderer used for this form
74 *
75 * @var object
76 */
77 protected $_renderer;
78
79 /**
80 * An array to hold a list of datefields on the form
81 * so that they can be converted to ISO in a consistent manner
82 *
83 * @var array
84 *
85 * e.g on a form declare $_dateFields = array(
86 * 'receive_date' => array('default' => 'now'),
87 * );
88 * then in postProcess call $this->convertDateFieldsToMySQL($formValues)
89 * to have the time field re-incorporated into the field & 'now' set if
90 * no value has been passed in
91 */
92 protected $_dateFields = array();
93
94 /**
95 * cache the smarty template for efficiency reasons
96 *
97 * @var CRM_Core_Smarty
98 */
99 static protected $_template;
100
101 /**
102 * What to return to the client if in ajax mode (snippet=json)
103 *
104 * @var array
105 */
106 public $ajaxResponse = array();
107
108 /**
109 * Stores info about reference fields for preprocessing
110 * Public so that hooks can access it
111 *
112 * @var array
113 */
114 public $entityReferenceFields = array();
115
116 /**
117 * constants for attributes for various form elements
118 * attempt to standardize on the number of variations that we
119 * use of the below form elements
120 *
121 * @var const string
122 */
123 CONST ATTR_SPACING = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
124
125 /**
126 * All checkboxes are defined with a common prefix. This allows us to
127 * have the same javascript to check / clear all the checkboxes etc
128 * If u have multiple groups of checkboxes, you will need to give them different
129 * ids to avoid potential name collision
130 *
131 * @var const string / int
132 */
133 CONST CB_PREFIX = 'mark_x_', CB_PREFIY = 'mark_y_', CB_PREFIZ = 'mark_z_', CB_PREFIX_LEN = 7;
134
135 /**
136 * Constructor for the basic form page
137 *
138 * We should not use QuickForm directly. This class provides a lot
139 * of default convenient functions, rules and buttons
140 *
141 * @param object $state State associated with this form
142 * @param enum $action The mode the form is operating in (None/Create/View/Update/Delete)
143 * @param string $method The type of http method used (GET/POST)
144 * @param string $name The name of the form if different from class name
145 *
146 * @return object
147 * @access public
148 */
149 function __construct(
150 $state = NULL,
151 $action = CRM_Core_Action::NONE,
152 $method = 'post',
153 $name = NULL
154 ) {
155
156 if ($name) {
157 $this->_name = $name;
158 }
159 else {
160 $this->_name = CRM_Utils_String::getClassName(CRM_Utils_System::getClassName($this));
161 }
162
163 $this->HTML_QuickForm_Page($this->_name, $method);
164
165 $this->_state =& $state;
166 if ($this->_state) {
167 $this->_state->setName($this->_name);
168 }
169 $this->_action = (int) $action;
170
171 $this->registerRules();
172
173 // let the constructor initialize this, should happen only once
174 if (!isset(self::$_template)) {
175 self::$_template = CRM_Core_Smarty::singleton();
176 }
177
178 $this->assign('snippet', (int) CRM_Utils_Array::value('snippet', $_REQUEST));
179 }
180
181 static function generateID() {
182 }
183
184 /**
185 * register all the standard rules that most forms potentially use
186 *
187 * @return void
188 * @access private
189 *
190 */
191 function registerRules() {
192 static $rules = array(
193 'title', 'longTitle', 'variable', 'qfVariable',
194 'phone', 'integer', 'query',
195 'url', 'wikiURL',
196 'domain', 'numberOfDigit',
197 'date', 'currentDate',
198 'asciiFile', 'htmlFile', 'utf8File',
199 'objectExists', 'optionExists', 'postalCode', 'money', 'positiveInteger',
200 'xssString', 'fileExists', 'autocomplete', 'validContact',
201 );
202
203 foreach ($rules as $rule) {
204 $this->registerRule($rule, 'callback', $rule, 'CRM_Utils_Rule');
205 }
206 }
207
208 /**
209 * Simple easy to use wrapper around addElement. Deal with
210 * simple validation rules
211 *
212 * @param string type of html element to be added
213 * @param string name of the html element
214 * @param string display label for the html element
215 * @param string attributes used for this element.
216 * These are not default values
217 * @param bool is this a required field
218 *
219 * @return HTML_QuickForm_Element could be an error object
220 * @access public
221 *
222 */
223 function &add($type, $name, $label = '',
224 $attributes = '', $required = FALSE, $javascript = NULL
225 ) {
226 $element = $this->addElement($type, $name, $label, $attributes, $javascript);
227 if (HTML_QuickForm::isError($element)) {
228 CRM_Core_Error::fatal(HTML_QuickForm::errorMessage($element));
229 }
230
231 if ($required) {
232 if ($type == 'file') {
233 $error = $this->addRule($name, ts('%1 is a required field.', array(1 => $label)), 'uploadedfile');
234 }
235 else {
236 $error = $this->addRule($name, ts('%1 is a required field.', array(1 => $label)), 'required');
237 }
238 if (HTML_QuickForm::isError($error)) {
239 CRM_Core_Error::fatal(HTML_QuickForm::errorMessage($element));
240 }
241 }
242
243 return $element;
244 }
245
246 /**
247 * This function is called before buildForm. Any pre-processing that
248 * needs to be done for buildForm should be done here
249 *
250 * This is a virtual function and should be redefined if needed
251 *
252 * @access public
253 *
254 * @return void
255 *
256 */
257 function preProcess() {}
258
259 /**
260 * This function is called after the form is validated. Any
261 * processing of form state etc should be done in this function.
262 * Typically all processing associated with a form should be done
263 * here and relevant state should be stored in the session
264 *
265 * This is a virtual function and should be redefined if needed
266 *
267 * @access public
268 *
269 * @return void
270 *
271 */
272 function postProcess() {}
273
274 /**
275 * This function is just a wrapper, so that we can call all the hook functions
276 */
277 function mainProcess() {
278 $this->postProcess();
279 $this->postProcessHook();
280
281 // Respond with JSON if in AJAX context (also support legacy value '6')
282 if (!empty($_REQUEST['snippet']) && in_array($_REQUEST['snippet'], array(CRM_Core_Smarty::PRINT_JSON, 6))) {
283 $this->ajaxResponse['buttonName'] = str_replace('_qf_' . $this->getAttribute('id') . '_', '', $this->controller->getButtonName());
284 $this->ajaxResponse['action'] = $this->_action;
285 if (isset($this->_id) || isset($this->id)) {
286 $this->ajaxResponse['id'] = isset($this->id) ? $this->id : $this->_id;
287 }
288 CRM_Core_Page_AJAX::returnJsonResponse($this->ajaxResponse);
289 }
290 }
291
292 /**
293 * The postProcess hook is typically called by the framework
294 * However in a few cases, the form exits or redirects early in which
295 * case it needs to call this function so other modules can do the needful
296 * Calling this function directly should be avoided if possible. In general a
297 * better way is to do setUserContext so the framework does the redirect
298 *
299 */
300 function postProcessHook() {
301 CRM_Utils_Hook::postProcess(get_class($this), $this);
302 }
303
304 /**
305 * This virtual function is used to build the form. It replaces the
306 * buildForm associated with QuickForm_Page. This allows us to put
307 * preProcess in front of the actual form building routine
308 *
309 * @access public
310 *
311 * @return void
312 *
313 */
314 function buildQuickForm() {}
315
316 /**
317 * This virtual function is used to set the default values of
318 * various form elements
319 *
320 * access public
321 *
322 * @return array reference to the array of default values
323 *
324 */
325 function setDefaultValues() {}
326
327 /**
328 * This is a virtual function that adds group and global rules to
329 * the form. Keeping it distinct from the form to keep code small
330 * and localized in the form building code
331 *
332 * @access public
333 *
334 * @return void
335 *
336 */
337 function addRules() {}
338
339 function validate() {
340 $error = parent::validate();
341
342 $hookErrors = CRM_Utils_Hook::validate(
343 get_class($this),
344 $this->_submitValues,
345 $this->_submitFiles,
346 $this
347 );
348
349 if (!is_array($hookErrors)) {
350 $hookErrors = array();
351 }
352
353 CRM_Utils_Hook::validateForm(
354 get_class($this),
355 $this->_submitValues,
356 $this->_submitFiles,
357 $this,
358 $hookErrors
359 );
360
361 if (!empty($hookErrors)) {
362 $this->_errors += $hookErrors;
363 }
364
365 return (0 == count($this->_errors));
366 }
367
368 /**
369 * Core function that builds the form. We redefine this function
370 * here and expect all CRM forms to build their form in the function
371 * buildQuickForm.
372 *
373 */
374 function buildForm() {
375 $this->_formBuilt = TRUE;
376
377 $this->preProcess();
378
379 $this->assign('translatePermission', CRM_Core_Permission::check('translate CiviCRM'));
380
381 if (
382 $this->controller->_key &&
383 $this->controller->_generateQFKey
384 ) {
385 $this->addElement('hidden', 'qfKey', $this->controller->_key);
386 $this->assign('qfKey', $this->controller->_key);
387
388 }
389
390 // _generateQFKey suppresses the qfKey generation on form snippets that
391 // are part of other forms, hence we use that to avoid adding entryURL
392 if ($this->controller->_generateQFKey && $this->controller->_entryURL) {
393 $this->addElement('hidden', 'entryURL', $this->controller->_entryURL);
394 }
395
396 $this->buildQuickForm();
397
398 $defaults = $this->setDefaultValues();
399 unset($defaults['qfKey']);
400
401 if (!empty($defaults)) {
402 $this->setDefaults($defaults);
403 }
404
405 // call the form hook
406 // also call the hook function so any modules can set thier own custom defaults
407 // the user can do both the form and set default values with this hook
408 CRM_Utils_Hook::buildForm(get_class($this), $this);
409
410 $this->preprocessReferenceFields();
411
412 $this->addRules();
413 }
414
415 /**
416 * Add default Next / Back buttons
417 *
418 * @param array array of associative arrays in the order in which the buttons should be
419 * displayed. The associate array has 3 fields: 'type', 'name' and 'isDefault'
420 * The base form class will define a bunch of static arrays for commonly used
421 * formats
422 *
423 * @return void
424 *
425 * @access public
426 *
427 */
428 function addButtons($params) {
429 $prevnext = array();
430 $spacing = array();
431 foreach ($params as $button) {
432 $js = CRM_Utils_Array::value('js', $button);
433 $isDefault = CRM_Utils_Array::value('isDefault', $button, FALSE);
434 if ($isDefault) {
435 $attrs = array('class' => 'form-submit default');
436 }
437 else {
438 $attrs = array('class' => 'form-submit');
439 }
440
441 if ($js) {
442 $attrs = array_merge($js, $attrs);
443 }
444
445 if ($button['type'] === 'reset') {
446 $prevnext[] = $this->createElement($button['type'], 'reset', $button['name'], $attrs);
447 }
448 else {
449 if (!empty($button['subName'])) {
450 $buttonName = $this->getButtonName($button['type'], $button['subName']);
451 }
452 else {
453 $buttonName = $this->getButtonName($button['type']);
454 }
455
456 if (in_array($button['type'], array(
457 'next', 'upload')) && $button['name'] === 'Save') {
458 $attrs = array_merge($attrs, (array('accesskey' => 'S')));
459 }
460 $prevnext[] = $this->createElement('submit', $buttonName, $button['name'], $attrs);
461 }
462 if (!empty($button['isDefault'])) {
463 $this->setDefaultAction($button['type']);
464 }
465
466 // if button type is upload, set the enctype
467 if ($button['type'] == 'upload') {
468 $this->updateAttributes(array('enctype' => 'multipart/form-data'));
469 $this->setMaxFileSize();
470 }
471
472 // hack - addGroup uses an array to express variable spacing, read from the last element
473 $spacing[] = CRM_Utils_Array::value('spacing', $button, self::ATTR_SPACING);
474 }
475 $this->addGroup($prevnext, 'buttons', '', $spacing, FALSE);
476 }
477
478 /**
479 * getter function for Name
480 *
481 * @return string
482 * @access public
483 */
484 function getName() {
485 return $this->_name;
486 }
487
488 /**
489 * getter function for State
490 *
491 * @return object
492 * @access public
493 */
494 function &getState() {
495 return $this->_state;
496 }
497
498 /**
499 * getter function for StateType
500 *
501 * @return int
502 * @access public
503 */
504 function getStateType() {
505 return $this->_state->getType();
506 }
507
508 /**
509 * getter function for title. Should be over-ridden by derived class
510 *
511 * @return string
512 * @access public
513 */
514 function getTitle() {
515 return $this->_title ? $this->_title : ts('ERROR: Title is not Set');
516 }
517
518 /**
519 * setter function for title.
520 *
521 * @param string $title the title of the form
522 *
523 * @return void
524 * @access public
525 */
526 function setTitle($title) {
527 $this->_title = $title;
528 }
529
530 /**
531 * Setter function for options
532 *
533 * @param mixed
534 *
535 * @return void
536 * @access public
537 */
538 function setOptions($options) {
539 $this->_options = $options;
540 }
541
542 /**
543 * getter function for link.
544 *
545 * @return string
546 * @access public
547 */
548 function getLink() {
549 $config = CRM_Core_Config::singleton();
550 return CRM_Utils_System::url($_GET[$config->userFrameworkURLVar],
551 '_qf_' . $this->_name . '_display=true'
552 );
553 }
554
555 /**
556 * boolean function to determine if this is a one form page
557 *
558 * @return boolean
559 * @access public
560 */
561 function isSimpleForm() {
562 return $this->_state->getType() & (CRM_Core_State::START | CRM_Core_State::FINISH);
563 }
564
565 /**
566 * getter function for Form Action
567 *
568 * @return string
569 * @access public
570 */
571 function getFormAction() {
572 return $this->_attributes['action'];
573 }
574
575 /**
576 * setter function for Form Action
577 *
578 * @param string
579 *
580 * @return void
581 * @access public
582 */
583 function setFormAction($action) {
584 $this->_attributes['action'] = $action;
585 }
586
587 /**
588 * render form and return contents
589 *
590 * @return string
591 * @access public
592 */
593 function toSmarty() {
594 $renderer = $this->getRenderer();
595 $this->accept($renderer);
596 $content = $renderer->toArray();
597 $content['formName'] = $this->getName();
598 return $content;
599 }
600
601 /**
602 * getter function for renderer. If renderer is not set
603 * create one and initialize it
604 *
605 * @return object
606 * @access public
607 */
608 function &getRenderer() {
609 if (!isset($this->_renderer)) {
610 $this->_renderer = CRM_Core_Form_Renderer::singleton();
611 }
612 return $this->_renderer;
613 }
614
615 /**
616 * Use the form name to create the tpl file name
617 *
618 * @return string
619 * @access public
620 */
621 function getTemplateFileName() {
622 $ext = CRM_Extension_System::singleton()->getMapper();
623 if ($ext->isExtensionClass(CRM_Utils_System::getClassName($this))) {
624 $filename = $ext->getTemplateName(CRM_Utils_System::getClassName($this));
625 $tplname = $ext->getTemplatePath(CRM_Utils_System::getClassName($this)) . DIRECTORY_SEPARATOR . $filename;
626 }
627 else {
628 $tplname = str_replace('_',
629 DIRECTORY_SEPARATOR,
630 CRM_Utils_System::getClassName($this)
631 ) . '.tpl';
632 }
633 return $tplname;
634 }
635
636 /**
637 * A wrapper for getTemplateFileName that includes calling the hook to
638 * prevent us from having to copy & paste the logic of calling the hook
639 */
640 function getHookedTemplateFileName() {
641 $pageTemplateFile = $this->getTemplateFileName();
642 CRM_Utils_Hook::alterTemplateFile(get_class($this), $this, 'page', $pageTemplateFile);
643 return $pageTemplateFile;
644 }
645
646 /**
647 * Default extra tpl file basically just replaces .tpl with .extra.tpl
648 * i.e. we dont override
649 *
650 * @return string
651 * @access public
652 */
653 function overrideExtraTemplateFileName() {
654 return NULL;
655 }
656
657 /**
658 * Error reporting mechanism
659 *
660 * @param string $message Error Message
661 * @param int $code Error Code
662 * @param CRM_Core_DAO $dao A data access object on which we perform a rollback if non - empty
663 *
664 * @return void
665 * @access public
666 */
667 function error($message, $code = NULL, $dao = NULL) {
668 if ($dao) {
669 $dao->query('ROLLBACK');
670 }
671
672 $error = CRM_Core_Error::singleton();
673
674 $error->push($code, $message);
675 }
676
677 /**
678 * Store the variable with the value in the form scope
679 *
680 * @param string name : name of the variable
681 * @param mixed value : value of the variable
682 *
683 * @access public
684 *
685 * @return void
686 *
687 */
688 function set($name, $value) {
689 $this->controller->set($name, $value);
690 }
691
692 /**
693 * Get the variable from the form scope
694 *
695 * @param string name : name of the variable
696 *
697 * @access public
698 *
699 * @return mixed
700 *
701 */
702 function get($name) {
703 return $this->controller->get($name);
704 }
705
706 /**
707 * getter for action
708 *
709 * @return int
710 * @access public
711 */
712 function getAction() {
713 return $this->_action;
714 }
715
716 /**
717 * setter for action
718 *
719 * @param int $action the mode we want to set the form
720 *
721 * @return void
722 * @access public
723 */
724 function setAction($action) {
725 $this->_action = $action;
726 }
727
728 /**
729 * assign value to name in template
730 *
731 * @param array|string $name name of variable
732 * @param mixed $value value of varaible
733 *
734 * @return void
735 * @access public
736 */
737 function assign($var, $value = NULL) {
738 self::$_template->assign($var, $value);
739 }
740
741 /**
742 * assign value to name in template by reference
743 *
744 * @param array|string $name name of variable
745 * @param mixed $value value of varaible
746 *
747 * @return void
748 * @access public
749 */
750 function assign_by_ref($var, &$value) {
751 self::$_template->assign_by_ref($var, $value);
752 }
753
754 /**
755 * appends values to template variables
756 *
757 * @param array|string $tpl_var the template variable name(s)
758 * @param mixed $value the value to append
759 * @param bool $merge
760 */
761 function append($tpl_var, $value=NULL, $merge=FALSE) {
762 self::$_template->append($tpl_var, $value, $merge);
763 }
764
765 /**
766 * Returns an array containing template variables
767 *
768 * @param string $name
769 * @param string $type
770 * @return array
771 */
772 function get_template_vars($name=null) {
773 return self::$_template->get_template_vars($name);
774 }
775
776 function &addRadio($name, $title, &$values, $attributes = NULL, $separator = NULL, $required = FALSE) {
777 $options = array();
778 if (empty($attributes)) {
779 $attributes = array('id_suffix' => $name);
780 }
781 else {
782 $attributes = array_merge($attributes, array('id_suffix' => $name));
783 }
784 foreach ($values as $key => $var) {
785 $options[] = $this->createElement('radio', NULL, NULL, $var, $key, $attributes);
786 }
787 $group = $this->addGroup($options, $name, $title, $separator);
788 if ($required) {
789 $this->addRule($name, ts('%1 is a required field.', array(1 => $title)), 'required');
790 }
791 return $group;
792 }
793
794 function addYesNo($id, $title, $dontKnow = NULL, $required = NULL, $attribute = NULL) {
795 if (empty($attribute)) {
796 $attribute = array('id_suffix' => $id);
797 }
798 else {
799 $attribute = array_merge($attribute, array('id_suffix' => $id));
800 }
801 $choice = array();
802 $choice[] = $this->createElement('radio', NULL, '11', ts('Yes'), '1', $attribute);
803 $choice[] = $this->createElement('radio', NULL, '11', ts('No'), '0', $attribute);
804 if ($dontKnow) {
805 $choice[] = $this->createElement('radio', NULL, '22', ts("Don't Know"), '2', $attribute);
806 }
807 $this->addGroup($choice, $id, $title);
808
809 if ($required) {
810 $this->addRule($id, ts('%1 is a required field.', array(1 => $title)), 'required');
811 }
812 }
813
814 function addCheckBox($id, $title, $values, $other = NULL,
815 $attributes = NULL, $required = NULL,
816 $javascriptMethod = NULL,
817 $separator = '<br />', $flipValues = FALSE
818 ) {
819 $options = array();
820
821 if ($javascriptMethod) {
822 foreach ($values as $key => $var) {
823 if (!$flipValues) {
824 $options[] = $this->createElement('checkbox', $var, NULL, $key, $javascriptMethod);
825 }
826 else {
827 $options[] = $this->createElement('checkbox', $key, NULL, $var, $javascriptMethod);
828 }
829 }
830 }
831 else {
832 foreach ($values as $key => $var) {
833 if (!$flipValues) {
834 $options[] = $this->createElement('checkbox', $var, NULL, $key);
835 }
836 else {
837 $options[] = $this->createElement('checkbox', $key, NULL, $var);
838 }
839 }
840 }
841
842 $this->addGroup($options, $id, $title, $separator);
843
844 if ($other) {
845 $this->addElement('text', $id . '_other', ts('Other'), $attributes[$id . '_other']);
846 }
847
848 if ($required) {
849 $this->addRule($id,
850 ts('%1 is a required field.', array(1 => $title)),
851 'required'
852 );
853 }
854 }
855
856 function resetValues() {
857 $data = $this->controller->container();
858 $data['values'][$this->_name] = array();
859 }
860
861 /**
862 * simple shell that derived classes can call to add buttons to
863 * the form with a customized title for the main Submit
864 *
865 * @param string $title title of the main button
866 * @param string $type button type for the form after processing
867 * @param string $submitOnce If true, add javascript to next button submit which prevents it from being clicked more than once
868 *
869 * @return void
870 * @access public
871 */
872 function addDefaultButtons($title, $nextType = 'next', $backType = 'back', $submitOnce = FALSE) {
873 $buttons = array();
874 if ($backType != NULL) {
875 $buttons[] = array(
876 'type' => $backType,
877 'name' => ts('Previous'),
878 );
879 }
880 if ($nextType != NULL) {
881 $nextButton = array(
882 'type' => $nextType,
883 'name' => $title,
884 'isDefault' => TRUE,
885 );
886 if ($submitOnce) {
887 $nextButton['js'] = array('onclick' => "return submitOnce(this,'{$this->_name}','" . ts('Processing') . "');");
888 }
889 $buttons[] = $nextButton;
890 }
891 $this->addButtons($buttons);
892 }
893
894 function addDateRange($name, $from = '_from', $to = '_to', $label = 'From:', $dateFormat = 'searchDate', $required = FALSE, $displayTime = FALSE) {
895 if ($displayTime) {
896 $this->addDateTime($name . $from, $label, $required, array('formatType' => $dateFormat));
897 $this->addDateTime($name . $to, ts('To:'), $required, array('formatType' => $dateFormat));
898 } else {
899 $this->addDate($name . $from, $label, $required, array('formatType' => $dateFormat));
900 $this->addDate($name . $to, ts('To:'), $required, array('formatType' => $dateFormat));
901 }
902 }
903
904 /**
905 * Adds a select based on field metadata
906 * TODO: This could be even more generic and widget type (select in this case) could also be read from metadata
907 * Perhaps a method like $form->bind($name) which would look up all metadata for named field
908 * @param CRM_Core_DAO $baoName - string representing bao object
909 * @param $name
910 * @param array $props
911 * @param bool $required
912 * @throws CRM_Core_Exception
913 * @return HTML_QuickForm_Element
914 */
915 function addSelect($baoName, $name, $props = array(), $required = FALSE) {
916 $options = $baoName::buildOptions($name, 'create');
917 if ($options === FALSE) {
918 throw new CRM_Core_Exception('No option list for field ' . $name);
919 }
920 if (!array_key_exists('placeholder', $props)) {
921 $props['placeholder'] = $required ? ts('- select -') : ts('- none -');
922 }
923 if (!empty($props['placeholder']) && empty($props['multiple'])) {
924 $options = array('' => '') + $options;
925 }
926 $bao = new $baoName;
927 // Handle custom field
928 if (strpos($name, 'custom_') === 0 && is_numeric($name[7])) {
929 list(, $id) = explode('_', $name);
930 $label = isset($props['label']) ? $props['label'] : CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', 'label', $id);
931 $gid = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', 'option_group_id', $id);
932 $props['data-option-group-url'] = 'civicrm/admin/options/' . CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $gid);
933 }
934 // Core field
935 else {
936 $meta = $bao->getFieldSpec($name);
937 $label = isset($props['label']) ? $props['label'] : ts($meta['title']);
938 if (!empty($meta['pseudoconstant']['optionGroupName'])) {
939 $props['data-option-group-url'] = 'civicrm/admin/options/' . $meta['pseudoconstant']['optionGroupName'];
940 }
941 }
942 require_once 'api/api.php';
943 $props['data-api-entity'] = _civicrm_api_get_entity_name_from_dao($bao);
944 $bao->free();
945 $props['class'] = isset($props['class']) ? $props['class'] . ' ' : '';
946 $props['class'] .= "crm-select2";
947 CRM_Utils_Array::remove($props, 'label');
948 return $this->add('select', $name, $label, $options, $required, $props);
949 }
950
951 /**
952 * Add a widget for selecting/editing/creating/copying a profile form
953 *
954 * @param string $name HTML form-element name
955 * @param string $label Printable label
956 * @param string $allowCoreTypes only present a UFGroup if its group_type includes a subset of $allowCoreTypes; e.g. 'Individual', 'Activity'
957 * @param string $allowSubTypes only present a UFGroup if its group_type is compatible with $allowSubypes
958 * @param array $entities
959 */
960 function addProfileSelector($name, $label, $allowCoreTypes, $allowSubTypes, $entities) {
961 // Output widget
962 // FIXME: Instead of adhoc serialization, use a single json_encode()
963 CRM_UF_Page_ProfileEditor::registerProfileScripts();
964 CRM_UF_Page_ProfileEditor::registerSchemas(CRM_Utils_Array::collect('entity_type', $entities));
965 $this->add('text', $name, $label, array(
966 'class' => 'crm-profile-selector',
967 // Note: client treats ';;' as equivalent to \0, and ';;' works better in HTML
968 'data-group-type' => CRM_Core_BAO_UFGroup::encodeGroupType($allowCoreTypes, $allowSubTypes, ';;'),
969 'data-entities' => json_encode($entities),
970 ));
971 }
972
973 function addWysiwyg($name, $label, $attributes, $forceTextarea = FALSE) {
974 // 1. Get configuration option for editor (tinymce, ckeditor, pure textarea)
975 // 2. Based on the option, initialise proper editor
976 $editorID = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
977 'editor_id'
978 );
979 $editor = strtolower(CRM_Utils_Array::value($editorID,
980 CRM_Core_OptionGroup::values('wysiwyg_editor')
981 ));
982 if (!$editor || $forceTextarea) {
983 $editor = 'textarea';
984 }
985 if ($editor == 'joomla default editor') {
986 $editor = 'joomlaeditor';
987 }
988
989 if ($editor == 'drupal default editor') {
990 $editor = 'drupalwysiwyg';
991 }
992
993 //lets add the editor as a attribute
994 $attributes['editor'] = $editor;
995
996 $this->addElement($editor, $name, $label, $attributes);
997 $this->assign('editor', $editor);
998
999 // include wysiwyg editor js files
1000 // FIXME: This code does not make any sense
1001 $includeWysiwygEditor = FALSE;
1002 $includeWysiwygEditor = $this->get('includeWysiwygEditor');
1003 if (!$includeWysiwygEditor) {
1004 $includeWysiwygEditor = TRUE;
1005 $this->set('includeWysiwygEditor', $includeWysiwygEditor);
1006 }
1007
1008 $this->assign('includeWysiwygEditor', $includeWysiwygEditor);
1009 }
1010
1011 function addCountry($id, $title, $required = NULL, $extra = NULL) {
1012 $this->addElement('select', $id, $title,
1013 array(
1014 '' => ts('- select -')) + CRM_Core_PseudoConstant::country(), $extra
1015 );
1016 if ($required) {
1017 $this->addRule($id, ts('Please select %1', array(1 => $title)), 'required');
1018 }
1019 }
1020
1021 function addSelectOther($name, $label, $options, $attributes, $required = NULL, $javascriptMethod = NULL) {
1022
1023 $this->addElement('select', $name . '_id', $label, $options, $javascriptMethod);
1024
1025 if ($required) {
1026 $this->addRule($name . '_id', ts('Please select %1', array(1 => $label)), 'required');
1027 }
1028 }
1029
1030 public function getRootTitle() {
1031 return NULL;
1032 }
1033
1034 public function getCompleteTitle() {
1035 return $this->getRootTitle() . $this->getTitle();
1036 }
1037
1038 static function &getTemplate() {
1039 return self::$_template;
1040 }
1041
1042 function addUploadElement($elementName) {
1043 $uploadNames = $this->get('uploadNames');
1044 if (!$uploadNames) {
1045 $uploadNames = array();
1046 }
1047 if (is_array($elementName)) {
1048 foreach ($elementName as $name) {
1049 if (!in_array($name, $uploadNames)) {
1050 $uploadNames[] = $name;
1051 }
1052 }
1053 }
1054 else {
1055 if (!in_array($elementName, $uploadNames)) {
1056 $uploadNames[] = $elementName;
1057 }
1058 }
1059 $this->set('uploadNames', $uploadNames);
1060
1061 $config = CRM_Core_Config::singleton();
1062 if (!empty($uploadNames)) {
1063 $this->controller->addUploadAction($config->customFileUploadDir, $uploadNames);
1064 }
1065 }
1066
1067 function buttonType() {
1068 $uploadNames = $this->get('uploadNames');
1069 $buttonType = (is_array($uploadNames) && !empty($uploadNames)) ? 'upload' : 'next';
1070 $this->assign('buttonType', $buttonType);
1071 return $buttonType;
1072 }
1073
1074 function getVar($name) {
1075 return isset($this->$name) ? $this->$name : NULL;
1076 }
1077
1078 function setVar($name, $value) {
1079 $this->$name = $value;
1080 }
1081
1082 /**
1083 * Function to add date
1084 * @param string $name name of the element
1085 * @param string $label label of the element
1086 * @param array $attributes key / value pair
1087 *
1088 // if you need time
1089 * $attributes = array ( 'addTime' => true,
1090 * 'formatType' => 'relative' or 'birth' etc check advanced date settings
1091 * );
1092 * @param boolean $required true if required
1093 *
1094 */
1095 function addDate($name, $label, $required = FALSE, $attributes = NULL) {
1096 if (!empty($attributes['formatType'])) {
1097 // get actual format
1098 $params = array('name' => $attributes['formatType']);
1099 $values = array();
1100
1101 // cache date information
1102 static $dateFormat;
1103 $key = "dateFormat_" . str_replace(' ', '_', $attributes['formatType']);
1104 if (empty($dateFormat[$key])) {
1105 CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_PreferencesDate', $params, $values);
1106 $dateFormat[$key] = $values;
1107 }
1108 else {
1109 $values = $dateFormat[$key];
1110 }
1111
1112 if ($values['date_format']) {
1113 $attributes['format'] = $values['date_format'];
1114 }
1115
1116 if (!empty($values['time_format'])) {
1117 $attributes['timeFormat'] = $values['time_format'];
1118 }
1119 $attributes['startOffset'] = $values['start'];
1120 $attributes['endOffset'] = $values['end'];
1121 }
1122
1123 $config = CRM_Core_Config::singleton();
1124 if (empty($attributes['format'])) {
1125 $attributes['format'] = $config->dateInputFormat;
1126 }
1127
1128 if (!isset($attributes['startOffset'])) {
1129 $attributes['startOffset'] = 10;
1130 }
1131
1132 if (!isset($attributes['endOffset'])) {
1133 $attributes['endOffset'] = 10;
1134 }
1135
1136 $this->add('text', $name, $label, $attributes);
1137
1138 if (!empty($attributes['addTime']) || !empty($attributes['timeFormat'])) {
1139
1140 if (!isset($attributes['timeFormat'])) {
1141 $timeFormat = $config->timeInputFormat;
1142 }
1143 else {
1144 $timeFormat = $attributes['timeFormat'];
1145 }
1146
1147 // 1 - 12 hours and 2 - 24 hours, but for jquery widget it is 0 and 1 respectively
1148 if ($timeFormat) {
1149 $show24Hours = TRUE;
1150 if ($timeFormat == 1) {
1151 $show24Hours = FALSE;
1152 }
1153
1154 //CRM-6664 -we are having time element name
1155 //in either flat string or an array format.
1156 $elementName = $name . '_time';
1157 if (substr($name, -1) == ']') {
1158 $elementName = substr($name, 0, strlen($name) - 1) . '_time]';
1159 }
1160
1161 $this->add('text', $elementName, ts('Time'), array('timeFormat' => $show24Hours));
1162 }
1163 }
1164
1165 if ($required) {
1166 $this->addRule($name, ts('Please select %1', array(1 => $label)), 'required');
1167 if (!empty($attributes['addTime']) && !empty($attributes['addTimeRequired'])) {
1168 $this->addRule($elementName, ts('Please enter a time.'), 'required');
1169 }
1170 }
1171 }
1172
1173 /**
1174 * Function that will add date and time
1175 */
1176 function addDateTime($name, $label, $required = FALSE, $attributes = NULL) {
1177 $addTime = array('addTime' => TRUE);
1178 if (is_array($attributes)) {
1179 $attributes = array_merge($attributes, $addTime);
1180 }
1181 else {
1182 $attributes = $addTime;
1183 }
1184
1185 $this->addDate($name, $label, $required, $attributes);
1186 }
1187
1188 /**
1189 * add a currency and money element to the form
1190 */
1191 function addMoney($name,
1192 $label,
1193 $required = FALSE,
1194 $attributes = NULL,
1195 $addCurrency = TRUE,
1196 $currencyName = 'currency',
1197 $defaultCurrency = NULL,
1198 $freezeCurrency = FALSE
1199 ) {
1200 $element = $this->add('text', $name, $label, $attributes, $required);
1201 $this->addRule($name, ts('Please enter a valid amount.'), 'money');
1202
1203 if ($addCurrency) {
1204 $ele = $this->addCurrency($currencyName, NULL, TRUE, $defaultCurrency, $freezeCurrency);
1205 }
1206
1207 return $element;
1208 }
1209
1210 /**
1211 * add currency element to the form
1212 */
1213 function addCurrency($name = 'currency',
1214 $label = NULL,
1215 $required = TRUE,
1216 $defaultCurrency = NULL,
1217 $freezeCurrency = FALSE
1218 ) {
1219 $currencies = CRM_Core_OptionGroup::values('currencies_enabled');
1220 if (!$required) {
1221 $currencies = array(
1222 '' => ts('- select -')) + $currencies;
1223 }
1224 $ele = $this->add('select', $name, $label, $currencies, $required);
1225 if ($freezeCurrency) {
1226 $ele->freeze();
1227 }
1228 if (!$defaultCurrency) {
1229 $config = CRM_Core_Config::singleton();
1230 $defaultCurrency = $config->defaultCurrency;
1231 }
1232 $this->setDefaults(array($name => $defaultCurrency));
1233 }
1234
1235 /**
1236 * Create a single or multiple entity ref field
1237 * @param string $name
1238 * @param string $label
1239 * @param array $props mix of html and widget properties, including:
1240 * - select - params to give to select2 widget
1241 * - api - array of settings for the api:
1242 * - "entity" - defaults to contact
1243 * - "action" - defaults to get (getquick when entity is contact)
1244 * - "params" - additional params to pass to the api
1245 * - "key" - what to store as this field's value - defaults to "id"
1246 * - "label" - what to show to the user - defaults to "label"
1247 * - "search" - rarely used - only needed if search field is different from label field
1248 * - placeholder - string
1249 * - multiple - bool
1250 * - class, etc. - other html properties
1251 * @param bool $required
1252 * @return HTML_QuickForm_Element
1253 */
1254 function addEntityRef($name, $label, $props = array(), $required = FALSE) {
1255 $props['api'] = CRM_Utils_Array::value('api', $props, array());
1256 // Merge in defaults for api params
1257 $props['api'] += array(
1258 'entity' => 'contact',
1259 'key' => 'id',
1260 );
1261 $props['api'] += array(
1262 'action' => $props['api']['entity'] == 'contact' ? 'getquick' : 'get',
1263 'label' => $props['api']['entity'] == 'contact' ? 'data' : 'label',
1264 );
1265 // this can be ommitted for normal apis since search field is usually the same as label field. But getquick is bizarre.
1266 $props['api'] += array(
1267 'search' => $props['api']['entity'] == 'contact' ? 'name' : $props['api']['label'],
1268 );
1269
1270 $props['class'] = isset($props['class']) ? $props['class'] . ' ' : '';
1271 $props['class'] .= "crm-select2 crm-{$props['api']['entity']}-ref-field";
1272
1273 $props['select'] = CRM_Utils_Array::value('select', $props, array()) + array(
1274 'minimumInputLength' => 1,
1275 'multiple' => !empty($props['multiple']),
1276 'placeholder' => CRM_Utils_Array::value('placeholder', $props, $required ? ts('- select -') : ts('- none -')),
1277 'allowClear' => !$required,
1278 // Disabled pending https://github.com/ivaynberg/select2/pull/2092
1279 //'formatInputTooShort' => ts('Start typing a name or email address...'),
1280 //'formatNoMatches' => ts('No contacts found.'),
1281 );
1282
1283 $this->entityReferenceFields[] = $name;
1284 $this->formatReferenceFieldAttributes($props);
1285 return $this->add('text', $name, $label, $props, $required);
1286 }
1287
1288 /**
1289 * @param $props
1290 */
1291 private function formatReferenceFieldAttributes(&$props) {
1292 $props['data-select-params'] = json_encode($props['select']);
1293 $props['data-api-params'] = json_encode($props['api']);
1294 CRM_Utils_Array::remove($props, 'multiple', 'select', 'api', 'placeholder');
1295 }
1296
1297 /**
1298 * Convert IDs to values and format for display
1299 */
1300 private function preprocessReferenceFields() {
1301 foreach ($this->entityReferenceFields as $name) {
1302 $field = $this->getElement($name);
1303 $val = $field->getValue();
1304 // Support array values
1305 if (is_array($val)) {
1306 $val = implode(',', $val);
1307 $field->setValue($val);
1308 }
1309 if ($val) {
1310 $data = array();
1311 $api = json_decode($field->getAttribute('data-api-params'), TRUE);
1312 $select = json_decode($field->getAttribute('data-select-params'), TRUE);
1313 // Support serialized values
1314 if (strpos($val, CRM_Core_DAO::VALUE_SEPARATOR) !== FALSE) {
1315 $val = str_replace(CRM_Core_DAO::VALUE_SEPARATOR, ',', trim($val, CRM_Core_DAO::VALUE_SEPARATOR));
1316 $field->setValue($val);
1317 }
1318 // TODO: we could fetch multiple values with array(IN => $val) but not all apis support this
1319 foreach (explode(',', $val) as $v) {
1320 $result = civicrm_api3($api['entity'], $api['action'], array('sequential' => 1, $api['key'] => $v));
1321 if (!empty($result['values'])) {
1322 $data[] = array('id' => $v, 'text' => $result['values'][0][$api['label']]);
1323 }
1324 }
1325 if ($field->isFrozen()) {
1326 $field->removeAttribute('class');
1327 }
1328 if ($data) {
1329 // Simplify array for single selects - makes client-side code simpler (but feels somehow wrong)
1330 if (empty($select['multiple'])) {
1331 $data = $data[0];
1332 }
1333 $field->setAttribute('data-entity-value', json_encode($data));
1334 }
1335 }
1336 }
1337 }
1338
1339 /**
1340 * Convert all date fields within the params to mysql date ready for the
1341 * BAO layer. In this case fields are checked against the $_datefields defined for the form
1342 * and if time is defined it is incorporated
1343 *
1344 * @param array $params input params from the form
1345 *
1346 * @todo it would probably be better to work on $this->_params than a passed array
1347 * @todo standardise the format which dates are passed to the BAO layer in & remove date
1348 * handling from BAO
1349 */
1350 function convertDateFieldsToMySQL(&$params){
1351 foreach ($this->_dateFields as $fieldName => $specs){
1352 if(!empty($params[$fieldName])){
1353 $params[$fieldName] = CRM_Utils_Date::isoToMysql(
1354 CRM_Utils_Date::processDate(
1355 $params[$fieldName],
1356 CRM_Utils_Array::value("{$fieldName}_time", $params), TRUE)
1357 );
1358 }
1359 else{
1360 if(isset($specs['default'])){
1361 $params[$fieldName] = date('YmdHis', strtotime($specs['default']));
1362 }
1363 }
1364 }
1365 }
1366
1367 function removeFileRequiredRules($elementName) {
1368 $this->_required = array_diff($this->_required, array($elementName));
1369 if (isset($this->_rules[$elementName])) {
1370 foreach ($this->_rules[$elementName] as $index => $ruleInfo) {
1371 if ($ruleInfo['type'] == 'uploadedfile') {
1372 unset($this->_rules[$elementName][$index]);
1373 }
1374 }
1375 if (empty($this->_rules[$elementName])) {
1376 unset($this->_rules[$elementName]);
1377 }
1378 }
1379 }
1380
1381 /**
1382 * Function that can be defined in Form to override or
1383 * perform specific action on cancel action
1384 *
1385 * @access public
1386 */
1387 function cancelAction() {}
1388
1389 /**
1390 * Helper function to verify that required fields have been filled
1391 * Typically called within the scope of a FormRule function
1392 */
1393 static function validateMandatoryFields($fields, $values, &$errors) {
1394 foreach ($fields as $name => $fld) {
1395 if (!empty($fld['is_required']) && CRM_Utils_System::isNull(CRM_Utils_Array::value($name, $values))) {
1396 $errors[$name] = ts('%1 is a required field.', array(1 => $fld['title']));
1397 }
1398 }
1399 }
1400
1401 /**
1402 * Get contact if for a form object. Prioritise
1403 * - cid in URL if 0 (on behalf on someoneelse)
1404 * (@todo consider setting a variable if onbehalf for clarity of downstream 'if's
1405 * - logged in user id if it matches the one in the cid in the URL
1406 * - contact id validated from a checksum from a checksum
1407 * - cid from the url if the caller has ACL permission to view
1408 * - fallback is logged in user (or ? NULL if no logged in user) (@todo wouldn't 0 be more intuitive?)
1409 *
1410 * @return Ambigous <mixed, NULL, value, unknown, array, number>|unknown
1411 */
1412 function getContactID() {
1413 $tempID = CRM_Utils_Request::retrieve('cid', 'Positive', $this);
1414 if(isset($this->_params) && isset($this->_params['select_contact_id'])) {
1415 $tempID = $this->_params['select_contact_id'];
1416 }
1417 if(isset($this->_params, $this->_params[0]) && !empty($this->_params[0]['select_contact_id'])) {
1418 // event form stores as an indexed array, contribution form not so much...
1419 $tempID = $this->_params[0]['select_contact_id'];
1420 }
1421
1422 // force to ignore the authenticated user
1423 if ($tempID === '0' || $tempID === 0) {
1424 // we set the cid on the form so that this will be retained for the Confirm page
1425 // in the multi-page form & prevent us returning the $userID when this is called
1426 // from that page
1427 // we don't really need to set it when $tempID is set because the params have that stored
1428 $this->set('cid', 0);
1429 return $tempID;
1430 }
1431
1432 $userID = $this->getLoggedInUserContactID();
1433
1434 if ($tempID == $userID) {
1435 return $userID;
1436 }
1437
1438 //check if this is a checksum authentication
1439 $userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this);
1440 if ($userChecksum) {
1441 //check for anonymous user.
1442 $validUser = CRM_Contact_BAO_Contact_Utils::validChecksum($tempID, $userChecksum);
1443 if ($validUser) {
1444 return $tempID;
1445 }
1446 }
1447 // check if user has permission, CRM-12062
1448 else if ($tempID && CRM_Contact_BAO_Contact_Permission::allow($tempID)) {
1449 return $tempID;
1450 }
1451
1452 return $userID;
1453 }
1454
1455 /**
1456 * Get the contact id of the logged in user
1457 */
1458 function getLoggedInUserContactID() {
1459 // check if the user is logged in and has a contact ID
1460 $session = CRM_Core_Session::singleton();
1461 return $session->get('userID');
1462 }
1463
1464 /**
1465 * add autoselector field -if user has permission to view contacts
1466 * If adding this to a form you also need to add to the tpl e.g
1467 *
1468 * {if !empty($selectable)}
1469 * <div class="crm-summary-row">
1470 * <div class="crm-label">{$form.select_contact.label}</div>
1471 * <div class="crm-content">
1472 * {$form.select_contact.html}
1473 * </div>
1474 * </div>
1475 * {/if}
1476 * @param array $profiles ids of profiles that are on the form (to be autofilled)
1477 * @param array $field metadata of field to use as selector including
1478 * - name_field
1479 * - id_field
1480 * - url (for ajax lookup)
1481 *
1482 * @todo add data attributes so we can deal with multiple instances on a form
1483 */
1484 function addAutoSelector($profiles = array(), $autoCompleteField = array()) {
1485 $autoCompleteField = array_merge(array(
1486 'name_field' => 'select_contact',
1487 'id_field' => 'select_contact_id',
1488 'field_text' => ts('Select Contact'),
1489 'show_hide' => TRUE,
1490 'show_text' => ts('to select someone already in our database.'),
1491 'hide_text' => ts('to clear this person\'s information, and fill the form in for someone else'),
1492 'url' => array('civicrm/ajax/rest', 'className=CRM_Contact_Page_AJAX&fnName=getContactList&json=1'),
1493 'max' => civicrm_api3('setting', 'getvalue', array(
1494 'name' => 'search_autocomplete_count',
1495 'group' => 'Search Preferences',
1496 ))
1497 ), $autoCompleteField);
1498
1499 if(0 < (civicrm_api3('contact', 'getcount', array('check_permissions' => 1)))) {
1500 $this->addElement('text', $autoCompleteField['name_field'] , $autoCompleteField['field_text']);
1501 $this->addElement('hidden', $autoCompleteField['id_field'], '', array('id' => $autoCompleteField['id_field']));
1502 $this->assign('selectable', $autoCompleteField['id_field']);
1503
1504 CRM_Core_Resources::singleton()->addScriptFile('civicrm', 'js/AutoComplete.js')
1505 ->addSetting(array(
1506 'form' => array('autocompletes' => $autoCompleteField),
1507 'ids' => array('profile' => $profiles),
1508 ));
1509 }
1510 }
1511
1512 /**
1513 * Add the options appropriate to cid = zero - ie. autocomplete
1514 *
1515 * @todo there is considerable code duplication between the contribution forms & event forms. It is apparent
1516 * that small pieces of duplication are not being refactored into separate functions because their only shared parent
1517 * is this form. Inserting a class FrontEndForm.php between the contribution & event & this class would allow functions like this
1518 * and a dozen other small ones to be refactored into a shared parent with the reduction of much code duplication
1519 */
1520 function addCIDZeroOptions($onlinePaymentProcessorEnabled) {
1521 $this->assign('nocid', TRUE);
1522 $profiles = array();
1523 if($this->_values['custom_pre_id']) {
1524 $profiles[] = $this->_values['custom_pre_id'];
1525 }
1526 if($this->_values['custom_post_id']) {
1527 $profiles[] = $this->_values['custom_post_id'];
1528 }
1529 if($onlinePaymentProcessorEnabled) {
1530 $profiles[] = 'billing';
1531 }
1532 if(!empty($this->_values)) {
1533 $this->addAutoSelector($profiles);
1534 }
1535 }
1536
1537 /**
1538 * Set default values on form for given contact (or no contact defaults)
1539 * @param mixed $profile_id (can be id, or profile name)
1540 * @param integer $contactID
1541 */
1542 function getProfileDefaults($profile_id = 'Billing', $contactID = NULL) {
1543 try{
1544 $defaults = civicrm_api3('profile', 'getsingle', array(
1545 'profile_id' => (array) $profile_id,
1546 'contact_id' => $contactID,
1547 ));
1548 return $defaults;
1549 }
1550 catch (Exception $e) {
1551 // the try catch block gives us silent failure -not 100% sure this is a good idea
1552 // as silent failures are often worse than noisy ones
1553 return array();
1554 }
1555 }
1556 }
1557