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