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