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