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