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