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