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