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