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