Merge pull request #3251 from agh1/empowered-by
[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 $var
756 * @param mixed $value value of varaible
757 *
758 * @internal param array|string $name name of variable
759 * @return void
760 * @access public
761 */
762 function assign($var, $value = NULL) {
763 self::$_template->assign($var, $value);
764 }
765
766 /**
767 * assign value to name in template by reference
768 *
769 * @param $var
770 * @param mixed $value value of varaible
771 *
772 * @internal param array|string $name name of variable
773 * @return void
774 * @access public
775 */
776 function assign_by_ref($var, &$value) {
777 self::$_template->assign_by_ref($var, $value);
778 }
779
780 /**
781 * appends values to template variables
782 *
783 * @param array|string $tpl_var the template variable name(s)
784 * @param mixed $value the value to append
785 * @param bool $merge
786 */
787 function append($tpl_var, $value=NULL, $merge=FALSE) {
788 self::$_template->append($tpl_var, $value, $merge);
789 }
790
791 /**
792 * Returns an array containing template variables
793 *
794 * @param string $name
795 *
796 * @internal param string $type
797 * @return array
798 */
799 function get_template_vars($name=null) {
800 return self::$_template->get_template_vars($name);
801 }
802
803 function &addRadio($name, $title, $values, $attributes = array(), $separator = NULL, $required = FALSE) {
804 $options = array();
805 $attributes = $attributes ? $attributes : array();
806 $allowClear = !empty($attributes['allowClear']);
807 unset($attributes['allowClear']);
808 $attributes += array('id_suffix' => $name);
809 foreach ($values as $key => $var) {
810 $options[] = $this->createElement('radio', NULL, NULL, $var, $key, $attributes);
811 }
812 $group = $this->addGroup($options, $name, $title, $separator);
813 if ($required) {
814 $this->addRule($name, ts('%1 is a required field.', array(1 => $title)), 'required');
815 }
816 if ($allowClear) {
817 $group->setAttribute('allowClear', TRUE);
818 }
819 return $group;
820 }
821
822 function addYesNo($id, $title, $allowClear = FALSE, $required = NULL, $attributes = array()) {
823 $attributes += array('id_suffix' => $id);
824 $choice = array();
825 $choice[] = $this->createElement('radio', NULL, '11', ts('Yes'), '1', $attributes);
826 $choice[] = $this->createElement('radio', NULL, '11', ts('No'), '0', $attributes);
827
828 $group = $this->addGroup($choice, $id, $title);
829 if ($allowClear) {
830 $group->setAttribute('allowClear', TRUE);
831 }
832 if ($required) {
833 $this->addRule($id, ts('%1 is a required field.', array(1 => $title)), 'required');
834 }
835 }
836
837 function addCheckBox($id, $title, $values, $other = NULL,
838 $attributes = NULL, $required = NULL,
839 $javascriptMethod = NULL,
840 $separator = '<br />', $flipValues = FALSE
841 ) {
842 $options = array();
843
844 if ($javascriptMethod) {
845 foreach ($values as $key => $var) {
846 if (!$flipValues) {
847 $options[] = $this->createElement('checkbox', $var, NULL, $key, $javascriptMethod);
848 }
849 else {
850 $options[] = $this->createElement('checkbox', $key, NULL, $var, $javascriptMethod);
851 }
852 }
853 }
854 else {
855 foreach ($values as $key => $var) {
856 if (!$flipValues) {
857 $options[] = $this->createElement('checkbox', $var, NULL, $key);
858 }
859 else {
860 $options[] = $this->createElement('checkbox', $key, NULL, $var);
861 }
862 }
863 }
864
865 $this->addGroup($options, $id, $title, $separator);
866
867 if ($other) {
868 $this->addElement('text', $id . '_other', ts('Other'), $attributes[$id . '_other']);
869 }
870
871 if ($required) {
872 $this->addRule($id,
873 ts('%1 is a required field.', array(1 => $title)),
874 'required'
875 );
876 }
877 }
878
879 function resetValues() {
880 $data = $this->controller->container();
881 $data['values'][$this->_name] = array();
882 }
883
884 /**
885 * simple shell that derived classes can call to add buttons to
886 * the form with a customized title for the main Submit
887 *
888 * @param string $title title of the main button
889 * @param string $nextType
890 * @param string $backType
891 * @param bool|string $submitOnce If true, add javascript to next button submit which prevents it from being clicked more than once
892 *
893 * @internal param string $type button type for the form after processing
894 * @return void
895 * @access public
896 */
897 function addDefaultButtons($title, $nextType = 'next', $backType = 'back', $submitOnce = FALSE) {
898 $buttons = array();
899 if ($backType != NULL) {
900 $buttons[] = array(
901 'type' => $backType,
902 'name' => ts('Previous'),
903 );
904 }
905 if ($nextType != NULL) {
906 $nextButton = array(
907 'type' => $nextType,
908 'name' => $title,
909 'isDefault' => TRUE,
910 );
911 if ($submitOnce) {
912 $nextButton['js'] = array('onclick' => "return submitOnce(this,'{$this->_name}','" . ts('Processing') . "');");
913 }
914 $buttons[] = $nextButton;
915 }
916 $this->addButtons($buttons);
917 }
918
919 function addDateRange($name, $from = '_from', $to = '_to', $label = 'From:', $dateFormat = 'searchDate', $required = FALSE, $displayTime = FALSE) {
920 if ($displayTime) {
921 $this->addDateTime($name . $from, $label, $required, array('formatType' => $dateFormat));
922 $this->addDateTime($name . $to, ts('To:'), $required, array('formatType' => $dateFormat));
923 } else {
924 $this->addDate($name . $from, $label, $required, array('formatType' => $dateFormat));
925 $this->addDate($name . $to, ts('To:'), $required, array('formatType' => $dateFormat));
926 }
927 }
928
929 /**
930 * Adds a select based on field metadata
931 * TODO: This could be even more generic and widget type (select in this case) could also be read from metadata
932 * Perhaps a method like $form->bind($name) which would look up all metadata for named field
933 * @param $name - field name to go on the form
934 * @param array $props - mix of html attributes and special properties, namely
935 * - entity (api entity name, can usually be inferred automatically from the form class)
936 * - field (field name - only needed if different from name used on the form)
937 * - option_url - path to edit this option list - usually retrieved automatically - set to NULL to disable link
938 * - placeholder - set to NULL to disable
939 * - multiple - bool
940 * @param bool $required
941 * @throws CRM_Core_Exception
942 * @return HTML_QuickForm_Element
943 */
944 function addSelect($name, $props = array(), $required = FALSE) {
945 if (!isset($props['entity'])) {
946 $props['entity'] = CRM_Utils_Api::getEntityName($this);
947 }
948 if (!isset($props['field'])) {
949 $props['field'] = strrpos($name, '[') ? rtrim(substr($name, 1 + strrpos($name, '[')), ']') : $name;
950 }
951 $info = civicrm_api3($props['entity'], 'getoptions', array(
952 'field' => $props['field'],
953 'options' => array('metadata' => array('fields'))
954 )
955 );
956 $options = $info['values'];
957 if (!array_key_exists('placeholder', $props)) {
958 $props['placeholder'] = $required ? ts('- select -') : ts('- none -');
959 }
960 if ($props['placeholder'] !== NULL && empty($props['multiple'])) {
961 $options = array('' => '') + $options;
962 }
963 // Handle custom field
964 if (strpos($name, 'custom_') === 0 && is_numeric($name[7])) {
965 list(, $id) = explode('_', $name);
966 $label = isset($props['label']) ? $props['label'] : CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', 'label', $id);
967 $gid = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', 'option_group_id', $id);
968 $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);
969 }
970 // Core field
971 else {
972 foreach($info['metadata']['fields'] as $uniqueName => $fieldSpec) {
973 if (
974 $uniqueName === $props['field'] ||
975 CRM_Utils_Array::value('name', $fieldSpec) === $props['field'] ||
976 in_array($props['field'], CRM_Utils_Array::value('api.aliases', $fieldSpec, array()))
977 ) {
978 break;
979 }
980 }
981 $label = isset($props['label']) ? $props['label'] : $fieldSpec['title'];
982 $props['data-option-edit-path'] = array_key_exists('option_url', $props) ? $props['option_url'] : $props['data-option-edit-path'] = CRM_Core_PseudoConstant::getOptionEditUrl($fieldSpec);
983 }
984 $props['class'] = (isset($props['class']) ? $props['class'] . ' ' : '') . "crm-select2";
985 $props['data-api-entity'] = $props['entity'];
986 $props['data-api-field'] = $props['field'];
987 CRM_Utils_Array::remove($props, 'label', 'entity', 'field', 'option_url');
988 return $this->add('select', $name, $label, $options, $required, $props);
989 }
990
991 /**
992 * Add a widget for selecting/editing/creating/copying a profile form
993 *
994 * @param string $name HTML form-element name
995 * @param string $label Printable label
996 * @param string $allowCoreTypes only present a UFGroup if its group_type includes a subset of $allowCoreTypes; e.g. 'Individual', 'Activity'
997 * @param string $allowSubTypes only present a UFGroup if its group_type is compatible with $allowSubypes
998 * @param array $entities
999 */
1000 function addProfileSelector($name, $label, $allowCoreTypes, $allowSubTypes, $entities) {
1001 // Output widget
1002 // FIXME: Instead of adhoc serialization, use a single json_encode()
1003 CRM_UF_Page_ProfileEditor::registerProfileScripts();
1004 CRM_UF_Page_ProfileEditor::registerSchemas(CRM_Utils_Array::collect('entity_type', $entities));
1005 $this->add('text', $name, $label, array(
1006 'class' => 'crm-profile-selector',
1007 // Note: client treats ';;' as equivalent to \0, and ';;' works better in HTML
1008 'data-group-type' => CRM_Core_BAO_UFGroup::encodeGroupType($allowCoreTypes, $allowSubTypes, ';;'),
1009 'data-entities' => json_encode($entities),
1010 ));
1011 }
1012
1013 function addWysiwyg($name, $label, $attributes, $forceTextarea = FALSE) {
1014 // 1. Get configuration option for editor (tinymce, ckeditor, pure textarea)
1015 // 2. Based on the option, initialise proper editor
1016 $editorID = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
1017 'editor_id'
1018 );
1019 $editor = strtolower(CRM_Utils_Array::value($editorID,
1020 CRM_Core_OptionGroup::values('wysiwyg_editor')
1021 ));
1022 if (!$editor || $forceTextarea) {
1023 $editor = 'textarea';
1024 }
1025 if ($editor == 'joomla default editor') {
1026 $editor = 'joomlaeditor';
1027 }
1028
1029 if ($editor == 'drupal default editor') {
1030 $editor = 'drupalwysiwyg';
1031 }
1032
1033 //lets add the editor as a attribute
1034 $attributes['editor'] = $editor;
1035
1036 $this->addElement($editor, $name, $label, $attributes);
1037 $this->assign('editor', $editor);
1038
1039 // include wysiwyg editor js files
1040 // FIXME: This code does not make any sense
1041 $includeWysiwygEditor = FALSE;
1042 $includeWysiwygEditor = $this->get('includeWysiwygEditor');
1043 if (!$includeWysiwygEditor) {
1044 $includeWysiwygEditor = TRUE;
1045 $this->set('includeWysiwygEditor', $includeWysiwygEditor);
1046 }
1047
1048 $this->assign('includeWysiwygEditor', $includeWysiwygEditor);
1049 }
1050
1051 function addCountry($id, $title, $required = NULL, $extra = NULL) {
1052 $this->addElement('select', $id, $title,
1053 array(
1054 '' => ts('- select -')) + CRM_Core_PseudoConstant::country(), $extra
1055 );
1056 if ($required) {
1057 $this->addRule($id, ts('Please select %1', array(1 => $title)), 'required');
1058 }
1059 }
1060
1061 function addSelectOther($name, $label, $options, $attributes, $required = NULL, $javascriptMethod = NULL) {
1062
1063 $this->addElement('select', $name . '_id', $label, $options, $javascriptMethod);
1064
1065 if ($required) {
1066 $this->addRule($name . '_id', ts('Please select %1', array(1 => $label)), 'required');
1067 }
1068 }
1069
1070 public function getRootTitle() {
1071 return NULL;
1072 }
1073
1074 public function getCompleteTitle() {
1075 return $this->getRootTitle() . $this->getTitle();
1076 }
1077
1078 static function &getTemplate() {
1079 return self::$_template;
1080 }
1081
1082 function addUploadElement($elementName) {
1083 $uploadNames = $this->get('uploadNames');
1084 if (!$uploadNames) {
1085 $uploadNames = array();
1086 }
1087 if (is_array($elementName)) {
1088 foreach ($elementName as $name) {
1089 if (!in_array($name, $uploadNames)) {
1090 $uploadNames[] = $name;
1091 }
1092 }
1093 }
1094 else {
1095 if (!in_array($elementName, $uploadNames)) {
1096 $uploadNames[] = $elementName;
1097 }
1098 }
1099 $this->set('uploadNames', $uploadNames);
1100
1101 $config = CRM_Core_Config::singleton();
1102 if (!empty($uploadNames)) {
1103 $this->controller->addUploadAction($config->customFileUploadDir, $uploadNames);
1104 }
1105 }
1106
1107 function buttonType() {
1108 $uploadNames = $this->get('uploadNames');
1109 $buttonType = (is_array($uploadNames) && !empty($uploadNames)) ? 'upload' : 'next';
1110 $this->assign('buttonType', $buttonType);
1111 return $buttonType;
1112 }
1113
1114 function getVar($name) {
1115 return isset($this->$name) ? $this->$name : NULL;
1116 }
1117
1118 function setVar($name, $value) {
1119 $this->$name = $value;
1120 }
1121
1122 /**
1123 * Function to add date
1124 * @param string $name name of the element
1125 * @param string $label label of the element
1126 * @param array $attributes key / value pair
1127 *
1128 // if you need time
1129 * $attributes = array ( 'addTime' => true,
1130 * 'formatType' => 'relative' or 'birth' etc check advanced date settings
1131 * );
1132 * @param boolean $required true if required
1133 *
1134 */
1135 function addDate($name, $label, $required = FALSE, $attributes = NULL) {
1136 if (!empty($attributes['formatType'])) {
1137 // get actual format
1138 $params = array('name' => $attributes['formatType']);
1139 $values = array();
1140
1141 // cache date information
1142 static $dateFormat;
1143 $key = "dateFormat_" . str_replace(' ', '_', $attributes['formatType']);
1144 if (empty($dateFormat[$key])) {
1145 CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_PreferencesDate', $params, $values);
1146 $dateFormat[$key] = $values;
1147 }
1148 else {
1149 $values = $dateFormat[$key];
1150 }
1151
1152 if ($values['date_format']) {
1153 $attributes['format'] = $values['date_format'];
1154 }
1155
1156 if (!empty($values['time_format'])) {
1157 $attributes['timeFormat'] = $values['time_format'];
1158 }
1159 $attributes['startOffset'] = $values['start'];
1160 $attributes['endOffset'] = $values['end'];
1161 }
1162
1163 $config = CRM_Core_Config::singleton();
1164 if (empty($attributes['format'])) {
1165 $attributes['format'] = $config->dateInputFormat;
1166 }
1167
1168 if (!isset($attributes['startOffset'])) {
1169 $attributes['startOffset'] = 10;
1170 }
1171
1172 if (!isset($attributes['endOffset'])) {
1173 $attributes['endOffset'] = 10;
1174 }
1175
1176 $this->add('text', $name, $label, $attributes);
1177
1178 if (!empty($attributes['addTime']) || !empty($attributes['timeFormat'])) {
1179
1180 if (!isset($attributes['timeFormat'])) {
1181 $timeFormat = $config->timeInputFormat;
1182 }
1183 else {
1184 $timeFormat = $attributes['timeFormat'];
1185 }
1186
1187 // 1 - 12 hours and 2 - 24 hours, but for jquery widget it is 0 and 1 respectively
1188 if ($timeFormat) {
1189 $show24Hours = TRUE;
1190 if ($timeFormat == 1) {
1191 $show24Hours = FALSE;
1192 }
1193
1194 //CRM-6664 -we are having time element name
1195 //in either flat string or an array format.
1196 $elementName = $name . '_time';
1197 if (substr($name, -1) == ']') {
1198 $elementName = substr($name, 0, strlen($name) - 1) . '_time]';
1199 }
1200
1201 $this->add('text', $elementName, ts('Time'), array('timeFormat' => $show24Hours));
1202 }
1203 }
1204
1205 if ($required) {
1206 $this->addRule($name, ts('Please select %1', array(1 => $label)), 'required');
1207 if (!empty($attributes['addTime']) && !empty($attributes['addTimeRequired'])) {
1208 $this->addRule($elementName, ts('Please enter a time.'), 'required');
1209 }
1210 }
1211 }
1212
1213 /**
1214 * Function that will add date and time
1215 */
1216 function addDateTime($name, $label, $required = FALSE, $attributes = NULL) {
1217 $addTime = array('addTime' => TRUE);
1218 if (is_array($attributes)) {
1219 $attributes = array_merge($attributes, $addTime);
1220 }
1221 else {
1222 $attributes = $addTime;
1223 }
1224
1225 $this->addDate($name, $label, $required, $attributes);
1226 }
1227
1228 /**
1229 * add a currency and money element to the form
1230 */
1231 function addMoney($name,
1232 $label,
1233 $required = FALSE,
1234 $attributes = NULL,
1235 $addCurrency = TRUE,
1236 $currencyName = 'currency',
1237 $defaultCurrency = NULL,
1238 $freezeCurrency = FALSE
1239 ) {
1240 $element = $this->add('text', $name, $label, $attributes, $required);
1241 $this->addRule($name, ts('Please enter a valid amount.'), 'money');
1242
1243 if ($addCurrency) {
1244 $ele = $this->addCurrency($currencyName, NULL, TRUE, $defaultCurrency, $freezeCurrency);
1245 }
1246
1247 return $element;
1248 }
1249
1250 /**
1251 * add currency element to the form
1252 */
1253 function addCurrency($name = 'currency',
1254 $label = NULL,
1255 $required = TRUE,
1256 $defaultCurrency = NULL,
1257 $freezeCurrency = FALSE
1258 ) {
1259 $currencies = CRM_Core_OptionGroup::values('currencies_enabled');
1260 $options = array('class' => 'crm-select2 eight');
1261 if (!$required) {
1262 $currencies = array('' => '') + $currencies;
1263 $options['placeholder'] = ts('- none -');
1264 }
1265 $ele = $this->add('select', $name, $label, $currencies, $required, $options);
1266 if ($freezeCurrency) {
1267 $ele->freeze();
1268 }
1269 if (!$defaultCurrency) {
1270 $config = CRM_Core_Config::singleton();
1271 $defaultCurrency = $config->defaultCurrency;
1272 }
1273 $this->setDefaults(array($name => $defaultCurrency));
1274 }
1275
1276 /**
1277 * Create a single or multiple entity ref field
1278 * @param string $name
1279 * @param string $label
1280 * @param array $props mix of html and widget properties, including:
1281 * - select - params to give to select2 widget
1282 * - entity - defaults to contact
1283 * - create - can the user create a new entity on-the-fly?
1284 * Set to TRUE if entity is contact and you want the default profiles,
1285 * or pass in your own set of links. @see CRM_Core_BAO_UFGroup::getCreateLinks for format
1286 * note that permissions are checked automatically
1287 * - api - array of settings for the getlist api wrapper
1288 * note that it accepts a 'params' setting which will be passed to the underlying api
1289 * - placeholder - string
1290 * - multiple - bool
1291 * - class, etc. - other html properties
1292 * @param bool $required
1293 *
1294 * @access public
1295 * @return HTML_QuickForm_Element
1296 */
1297 function addEntityRef($name, $label = '', $props = array(), $required = FALSE) {
1298 require_once "api/api.php";
1299 $config = CRM_Core_Config::singleton();
1300 // Default properties
1301 $props['api'] = CRM_Utils_Array::value('api', $props, array());
1302 $props['entity'] = _civicrm_api_get_entity_name_from_camel(CRM_Utils_Array::value('entity', $props, 'contact'));
1303 $props['class'] = ltrim(CRM_Utils_Array::value('class', $props, '') . ' crm-form-entityref');
1304
1305 if ($props['entity'] == 'contact' && isset($props['create']) && !(CRM_Core_Permission::check('edit all contacts') || CRM_Core_Permission::check('add contacts'))) {
1306 unset($props['create']);
1307 }
1308
1309 $props['placeholder'] = CRM_Utils_Array::value('placeholder', $props, $required ? ts('- select %1 -', array(1 => ts(str_replace('_', ' ', $props['entity'])))) : ts('- none -'));
1310
1311 $defaults = array();
1312 if (!empty($props['multiple'])) {
1313 $defaults['multiple'] = TRUE;
1314 }
1315 $props['select'] = CRM_Utils_Array::value('select', $props, array()) + $defaults;
1316
1317 $this->formatReferenceFieldAttributes($props);
1318 return $this->add('text', $name, $label, $props, $required);
1319 }
1320
1321 /**
1322 * @param $props
1323 */
1324 private function formatReferenceFieldAttributes(&$props) {
1325 $props['data-select-params'] = json_encode($props['select']);
1326 $props['data-api-params'] = $props['api'] ? json_encode($props['api']) : NULL;
1327 $props['data-api-entity'] = $props['entity'];
1328 if (!empty($props['create'])) {
1329 $props['data-create-links'] = json_encode($props['create']);
1330 }
1331 CRM_Utils_Array::remove($props, 'multiple', 'select', 'api', 'entity', 'create');
1332 }
1333
1334 /**
1335 * Convert all date fields within the params to mysql date ready for the
1336 * BAO layer. In this case fields are checked against the $_datefields defined for the form
1337 * and if time is defined it is incorporated
1338 *
1339 * @param array $params input params from the form
1340 *
1341 * @todo it would probably be better to work on $this->_params than a passed array
1342 * @todo standardise the format which dates are passed to the BAO layer in & remove date
1343 * handling from BAO
1344 */
1345 function convertDateFieldsToMySQL(&$params){
1346 foreach ($this->_dateFields as $fieldName => $specs){
1347 if(!empty($params[$fieldName])){
1348 $params[$fieldName] = CRM_Utils_Date::isoToMysql(
1349 CRM_Utils_Date::processDate(
1350 $params[$fieldName],
1351 CRM_Utils_Array::value("{$fieldName}_time", $params), TRUE)
1352 );
1353 }
1354 else{
1355 if(isset($specs['default'])){
1356 $params[$fieldName] = date('YmdHis', strtotime($specs['default']));
1357 }
1358 }
1359 }
1360 }
1361
1362 function removeFileRequiredRules($elementName) {
1363 $this->_required = array_diff($this->_required, array($elementName));
1364 if (isset($this->_rules[$elementName])) {
1365 foreach ($this->_rules[$elementName] as $index => $ruleInfo) {
1366 if ($ruleInfo['type'] == 'uploadedfile') {
1367 unset($this->_rules[$elementName][$index]);
1368 }
1369 }
1370 if (empty($this->_rules[$elementName])) {
1371 unset($this->_rules[$elementName]);
1372 }
1373 }
1374 }
1375
1376 /**
1377 * Function that can be defined in Form to override or
1378 * perform specific action on cancel action
1379 *
1380 * @access public
1381 */
1382 function cancelAction() {}
1383
1384 /**
1385 * Helper function to verify that required fields have been filled
1386 * Typically called within the scope of a FormRule function
1387 */
1388 static function validateMandatoryFields($fields, $values, &$errors) {
1389 foreach ($fields as $name => $fld) {
1390 if (!empty($fld['is_required']) && CRM_Utils_System::isNull(CRM_Utils_Array::value($name, $values))) {
1391 $errors[$name] = ts('%1 is a required field.', array(1 => $fld['title']));
1392 }
1393 }
1394 }
1395
1396 /**
1397 * Get contact if for a form object. Prioritise
1398 * - cid in URL if 0 (on behalf on someoneelse)
1399 * (@todo consider setting a variable if onbehalf for clarity of downstream 'if's
1400 * - logged in user id if it matches the one in the cid in the URL
1401 * - contact id validated from a checksum from a checksum
1402 * - cid from the url if the caller has ACL permission to view
1403 * - fallback is logged in user (or ? NULL if no logged in user) (@todo wouldn't 0 be more intuitive?)
1404 *
1405 * @return Ambigous <mixed, NULL, value, unknown, array, number>|unknown
1406 */
1407 function getContactID() {
1408 $tempID = CRM_Utils_Request::retrieve('cid', 'Positive', $this);
1409 if(isset($this->_params) && isset($this->_params['select_contact_id'])) {
1410 $tempID = $this->_params['select_contact_id'];
1411 }
1412 if(isset($this->_params, $this->_params[0]) && !empty($this->_params[0]['select_contact_id'])) {
1413 // event form stores as an indexed array, contribution form not so much...
1414 $tempID = $this->_params[0]['select_contact_id'];
1415 }
1416
1417 // force to ignore the authenticated user
1418 if ($tempID === '0' || $tempID === 0) {
1419 // we set the cid on the form so that this will be retained for the Confirm page
1420 // in the multi-page form & prevent us returning the $userID when this is called
1421 // from that page
1422 // we don't really need to set it when $tempID is set because the params have that stored
1423 $this->set('cid', 0);
1424 return $tempID;
1425 }
1426
1427 $userID = $this->getLoggedInUserContactID();
1428
1429 if ($tempID == $userID) {
1430 return $userID;
1431 }
1432
1433 //check if this is a checksum authentication
1434 $userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this);
1435 if ($userChecksum) {
1436 //check for anonymous user.
1437 $validUser = CRM_Contact_BAO_Contact_Utils::validChecksum($tempID, $userChecksum);
1438 if ($validUser) {
1439 return $tempID;
1440 }
1441 }
1442 // check if user has permission, CRM-12062
1443 else if ($tempID && CRM_Contact_BAO_Contact_Permission::allow($tempID)) {
1444 return $tempID;
1445 }
1446
1447 return $userID;
1448 }
1449
1450 /**
1451 * Get the contact id of the logged in user
1452 */
1453 function getLoggedInUserContactID() {
1454 // check if the user is logged in and has a contact ID
1455 $session = CRM_Core_Session::singleton();
1456 return $session->get('userID');
1457 }
1458
1459 /**
1460 * add autoselector field -if user has permission to view contacts
1461 * If adding this to a form you also need to add to the tpl e.g
1462 *
1463 * {if !empty($selectable)}
1464 * <div class="crm-summary-row">
1465 * <div class="crm-label">{$form.select_contact.label}</div>
1466 * <div class="crm-content">
1467 * {$form.select_contact.html}
1468 * </div>
1469 * </div>
1470 * {/if}
1471 *
1472 * @param array $profiles ids of profiles that are on the form (to be autofilled)
1473 * @param array $autoCompleteField
1474 *
1475 * @internal param array $field metadata of field to use as selector including
1476 * - name_field
1477 * - id_field
1478 * - url (for ajax lookup)
1479 *
1480 * @todo add data attributes so we can deal with multiple instances on a form
1481 */
1482 function addAutoSelector($profiles = array(), $autoCompleteField = array()) {
1483 $autoCompleteField = array_merge(array(
1484 'id_field' => 'select_contact_id',
1485 'placeholder' => ts('Select someone else ...'),
1486 'show_hide' => TRUE,
1487 'api' => array('params' => array('contact_type' => 'Individual'))
1488 ), $autoCompleteField);
1489
1490 if($this->canUseAjaxContactLookups()) {
1491 $this->assign('selectable', $autoCompleteField['id_field']);
1492 $this->addEntityRef($autoCompleteField['id_field'], NULL, array('placeholder' => $autoCompleteField['placeholder'], 'api' => $autoCompleteField['api']));
1493
1494 CRM_Core_Resources::singleton()->addScriptFile('civicrm', 'js/AlternateContactSelector.js')
1495 ->addSetting(array(
1496 'form' => array('autocompletes' => $autoCompleteField),
1497 'ids' => array('profile' => $profiles),
1498 ));
1499 }
1500 }
1501
1502 /**
1503 *
1504 */
1505 function canUseAjaxContactLookups() {
1506 if (0 < (civicrm_api3('contact', 'getcount', array('check_permissions' => 1))) &&
1507 CRM_Core_Permission::check(array(array('access AJAX API', 'access CiviCRM')))) {
1508 return TRUE;
1509 }
1510 }
1511
1512 /**
1513 * Add the options appropriate to cid = zero - ie. autocomplete
1514 *
1515 * @todo there is considerable code duplication between the contribution forms & event forms. It is apparent
1516 * that small pieces of duplication are not being refactored into separate functions because their only shared parent
1517 * is this form. Inserting a class FrontEndForm.php between the contribution & event & this class would allow functions like this
1518 * and a dozen other small ones to be refactored into a shared parent with the reduction of much code duplication
1519 */
1520 function addCIDZeroOptions($onlinePaymentProcessorEnabled) {
1521 $this->assign('nocid', TRUE);
1522 $profiles = array();
1523 if($this->_values['custom_pre_id']) {
1524 $profiles[] = $this->_values['custom_pre_id'];
1525 }
1526 if($this->_values['custom_post_id']) {
1527 $profiles[] = $this->_values['custom_post_id'];
1528 }
1529 if($onlinePaymentProcessorEnabled) {
1530 $profiles[] = 'billing';
1531 }
1532 if(!empty($this->_values)) {
1533 $this->addAutoSelector($profiles);
1534 }
1535 }
1536
1537 /**
1538 * Set default values on form for given contact (or no contact defaults)
1539 *
1540 * @param mixed $profile_id (can be id, or profile name)
1541 * @param integer $contactID
1542 *
1543 * @return array
1544 */
1545 function getProfileDefaults($profile_id = 'Billing', $contactID = NULL) {
1546 try{
1547 $defaults = civicrm_api3('profile', 'getsingle', array(
1548 'profile_id' => (array) $profile_id,
1549 'contact_id' => $contactID,
1550 ));
1551 return $defaults;
1552 }
1553 catch (Exception $e) {
1554 // the try catch block gives us silent failure -not 100% sure this is a good idea
1555 // as silent failures are often worse than noisy ones
1556 return array();
1557 }
1558 }
1559
1560 /**
1561 * Sets form attribute
1562 * @see CRM.loadForm
1563 */
1564 function preventAjaxSubmit() {
1565 $this->setAttribute('data-no-ajax-submit', 'true');
1566 }
1567
1568 /**
1569 * Sets form attribute
1570 * @see CRM.loadForm
1571 */
1572 function allowAjaxSubmit() {
1573 $this->removeAttribute('data-no-ajax-submit');
1574 }
1575 }
1576