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